## Automatically generated incremental diff
## From:   linux-2.5.70-bk6
## To:     linux-2.5.70-bk7
## Robot:  $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $

diff -urN linux-2.5.70-bk6/Documentation/sound/alsa/ALSA-Configuration.txt linux-2.5.70-bk7/Documentation/sound/alsa/ALSA-Configuration.txt
--- linux-2.5.70-bk6/Documentation/sound/alsa/ALSA-Configuration.txt	2003-05-26 18:00:22.000000000 -0700
+++ linux-2.5.70-bk7/Documentation/sound/alsa/ALSA-Configuration.txt	2003-06-02 04:39:11.000000000 -0700
@@ -53,14 +53,9 @@
 		  for soundcards which are not installed in your system
     device_mode
 		- specifies permission mask for dynamic sound device filesystem
+		  (available only when DEVFS is enabled)
 		- default value = 0666
 		- for example 'device_mode=0660'
-    device_gid
-		- specifies GID number for dynamic sound device filesystem
-		- default value = 0 (root)
-    device_uid
-		- specifies UID number for dynamic sound device filesystem
-		- default value = 0 (root)
 
   
   Module snd-pcm-oss
@@ -144,6 +139,7 @@
     Module for ALi M5451 PCI chip.
 
     pcm_channels    - Number of hardware channels assigned for PCM
+    spdif           - Support SPDIF I/O (disabled by default)
 
     Module supports autoprobe and multiple chips (max 8).
 
@@ -846,6 +842,20 @@
 
     Module supports up to 8 cards.
 
+  Module snd-sscape
+  -----------------
+
+    Module for ENSONIQ SoundScape PnP cards.
+
+    port	- Port # (PnP setup)
+    irq		- IRQ # (PnP setup)
+    mpu_irq	- MPU-401 IRQ # (PnP setup)
+    dma		- DMA # (PnP setup)
+
+    Module supports up to 8 cards.  ISA PnP must be enabled.
+    You need sscape_ctl tool in alsa-tools package for loading
+    the microcode.
+
   Module snd-sun-amd7930 (on sparc only)
   --------------------------------------
 
@@ -945,19 +955,34 @@
   Module snd-via82xx
   ------------------
 
-    Module for AC'97 motherboards based on VIA 82C686A/686B, 8233
-    (south) bridge.
+    Module for AC'97 motherboards based on VIA 82C686A/686B, 8233,
+    8233A, 8233C, 8235 (south) bridge.
 
     mpu_port	- 0x300,0x310,0x320,0x330, otherwise obtain BIOS setup
+		  [VIA686A/686B only]
     ac97_clock	- AC'97 codec clock base (default 48000Hz)
+    dxs_support	- support DXS channels,
+		  0 = auto (default), 1 = enable, 2 = disable,
+		  3 = 48k only
+		  [VIA8233/C,8235 only]
 
     Module supports autoprobe and multiple bus-master chips (max 8).
+
     Note: on some SMP motherboards like MSI 694D the interrupts might
           not be generated properly.  In such a case, please try to
           set the SMP (or MPS) version on BIOS to 1.1 instead of
           default value 1.4.  Then the interrupt number will be
           assigned under 15. You might also upgrade your BIOS.
     
+    Note: VIA8233/5 (not VIA8233A) can support DXS (direct sound)
+	  channels as the first PCM.  With this device, up to 4
+	  streams can be played at the same time.  If the playback on
+	  this PCM is noisy, try to specify dxs_channels option to 2
+	  or 3.
+
+    Note: for the MPU401 on VIA823x, use snd-mpu401 driver
+	  additonally.
+
   Module snd-virmidi
   ------------------
 
@@ -969,6 +994,53 @@
     
     Module supports up to 8 cards.
 
+  Module snd-vx222
+  ----------------
+
+    Module for Digigram VX-Pocket VX222, V222 v2 and Mic cards.
+
+    mic		- Enable Microphone on V222 Mic (NYI)
+
+    Module supports up to 8 cards.
+
+    For loading the firmware, use vxloader utility in alsa-tools
+    package.  You can load the firmware automatically by adding
+    the following to /etc/modules.conf
+
+	post-install snd-vx222 "/usr/bin/vxload"
+
+  Module snd-vxpocket
+  -------------------
+
+    Module for Digigram VX-Pocket VX2 PCMCIA card.
+
+    irq_mask - IRQ bitmask, specifies the available IRQs as bits
+
+    Module supports up to 8 cards.  The module is compiled only when
+    PCMCIA is supported on kernel.
+
+    To activate the driver via the card manager, you'll need to set
+    up /etc/pcmcia/vxpocket.conf.  See the sound/pcmcia/vx/vxpocket.c.
+
+    For loading the firmware, use vxloader utility in alsa-tools
+    package.
+
+  Module snd-vxp440
+  -----------------
+
+    Module for Digigram VX-Pocket 440 PCMCIA card.
+
+    irq_mask - IRQ bitmask, specifies the available IRQs as bits
+
+    Module supports up to 8 cards.  The module is compiled only when
+    PCMCIA is supported on kernel.
+
+    To activate the driver via the card manager, you'll need to set
+    up /etc/pcmcia/vxp440.conf.  See the sound/pcmcia/vx/vxp440.c.
+
+    For loading the firmware, use vxloader utility in alsa-tools
+    package.
+
   Module snd-ymfpci
   -----------------
 
diff -urN linux-2.5.70-bk6/Documentation/sound/alsa/CMIPCI.txt linux-2.5.70-bk7/Documentation/sound/alsa/CMIPCI.txt
--- linux-2.5.70-bk6/Documentation/sound/alsa/CMIPCI.txt	2003-05-26 18:00:21.000000000 -0700
+++ linux-2.5.70-bk7/Documentation/sound/alsa/CMIPCI.txt	2003-06-02 04:39:11.000000000 -0700
@@ -115,8 +115,7 @@
 
 	% aplay -Dspdif foo.wav
 
-So far, only S16LE format is supported.  Still no 24bit.  Sorry, not
-enough info for this.
+24bit format is also supported experimentally.
 
 The playback and capture over SPDIF use normal DAC and ADC,
 respectively, so you cannot playback both analog and digital streams
diff -urN linux-2.5.70-bk6/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl linux-2.5.70-bk7/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
--- linux-2.5.70-bk6/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl	2003-05-26 18:00:27.000000000 -0700
+++ linux-2.5.70-bk7/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl	2003-06-02 04:39:11.000000000 -0700
@@ -1538,11 +1538,12 @@
         <informalexample>
           <programlisting>
 <![CDATA[
-  static void snd_mychip_interrupt(int irq, void *dev_id,
-                                   struct pt_regs *regs)
+  static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id,
+                                          struct pt_regs *regs)
   {
           mychip_t *chip = snd_magic_cast(mychip_t, dev_id, return);
           ....
+          return IRQ_HANDLED;
   }
 ]]>
           </programlisting>
@@ -3149,8 +3150,8 @@
 	    <title>Interrupt Handler Case #1</title>
             <programlisting>
 <![CDATA[
-  static void snd_mychip_interrupt(int irq, void *dev_id,
-                                   struct pt_regs *regs)
+  static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id,
+                                          struct pt_regs *regs)
   {
           mychip_t *chip = snd_magic_cast(mychip_t, dev_id, return);
           spin_lock(&chip->lock);
@@ -3160,9 +3161,11 @@
                   spin_unlock(&chip->lock);
                   snd_pcm_period_elapsed(chip->substream);
                   spin_lock(&chip->lock);
+                  // acknowledge the interrupt if necessary
           }
           ....
           spin_unlock(&chip->lock);
+          return IRQ_HANDLED;
   }
 ]]>
             </programlisting>
@@ -3191,8 +3194,8 @@
 	    <title>Interrupt Handler Case #2</title>
             <programlisting>
 <![CDATA[
-  static void snd_mychip_interrupt(int irq, void *dev_id,
-                                   struct pt_regs *regs)
+  static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id,
+                                          struct pt_regs *regs)
   {
           mychip_t *chip = snd_magic_cast(mychip_t, dev_id, return);
           spin_lock(&chip->lock);
@@ -3221,9 +3224,11 @@
                           snd_pcm_period_elapsed(substream);
                           spin_lock(&chip->lock);
                   }
+                  // acknowledge the interrupt if necessary
           }
           ....
           spin_unlock(&chip->lock);
+          return IRQ_HANDLED;
   }
 ]]>
             </programlisting>
diff -urN linux-2.5.70-bk6/Documentation/sound/alsa/OSS-Emulation.txt linux-2.5.70-bk7/Documentation/sound/alsa/OSS-Emulation.txt
--- linux-2.5.70-bk6/Documentation/sound/alsa/OSS-Emulation.txt	2003-05-26 18:00:41.000000000 -0700
+++ linux-2.5.70-bk7/Documentation/sound/alsa/OSS-Emulation.txt	2003-06-02 04:39:11.000000000 -0700
@@ -249,7 +249,7 @@
 [Volume|Switch]" will be checked in addition.
 
 The current assignment of these mixer elements is listed in the proc
-file, /proc/asound/cardX/mixer_oss, which will be like the following
+file, /proc/asound/cardX/oss_mixer, which will be like the following
 
 	VOLUME "Master" 0
 	BASS "" 0
@@ -267,7 +267,7 @@
 proc file.  For example, to map "Wave Playback" to the PCM volume,
 send the command like the following:
 
-	% echo 'VOLUME "Wave Playback" 0' > /proc/asound/card0/mixer_oss
+	% echo 'VOLUME "Wave Playback" 0' > /proc/asound/card0/oss_mixer
 
 The command is exactly as same as listed in the proc file.  You can
 change one or more elements, one volume per line.  In the last
diff -urN linux-2.5.70-bk6/Makefile linux-2.5.70-bk7/Makefile
--- linux-2.5.70-bk6/Makefile	2003-06-02 04:39:07.000000000 -0700
+++ linux-2.5.70-bk7/Makefile	2003-06-02 04:39:11.000000000 -0700
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 5
 SUBLEVEL = 70
-EXTRAVERSION = -bk6
+EXTRAVERSION = -bk7
 
 # *DOCUMENTATION*
 # To see a list of typical targets execute "make help"
diff -urN linux-2.5.70-bk6/drivers/ide/ide.c linux-2.5.70-bk7/drivers/ide/ide.c
--- linux-2.5.70-bk6/drivers/ide/ide.c	2003-06-02 04:39:07.000000000 -0700
+++ linux-2.5.70-bk7/drivers/ide/ide.c	2003-06-02 04:39:11.000000000 -0700
@@ -2335,7 +2335,7 @@
 	setup_driver_defaults(drive);
 	spin_unlock_irqrestore(&ide_lock, flags);
 	spin_lock(&drives_lock);
-	list_add(&drive->list, &driver->drives);
+	list_add_tail(&drive->list, &driver->drives);
 	spin_unlock(&drives_lock);
 //	printk(KERN_INFO "%s: attached %s driver.\n", drive->name, driver->name);
 	if ((drive->autotune == IDE_TUNE_DEFAULT) ||
diff -urN linux-2.5.70-bk6/drivers/net/ppp_generic.c linux-2.5.70-bk7/drivers/net/ppp_generic.c
--- linux-2.5.70-bk6/drivers/net/ppp_generic.c	2003-05-26 18:00:58.000000000 -0700
+++ linux-2.5.70-bk7/drivers/net/ppp_generic.c	2003-06-02 04:39:11.000000000 -0700
@@ -682,8 +682,10 @@
 			if (code == 0)
 				break;
 			err = -EFAULT;
-			if (copy_from_user(code, uprog.filter, len))
+			if (copy_from_user(code, uprog.filter, len)) {
+				kfree(code);
 				break;
+			}
 			err = sk_chk_filter(code, uprog.len);
 			if (err) {
 				kfree(code);
diff -urN linux-2.5.70-bk6/include/linux/xfrm.h linux-2.5.70-bk7/include/linux/xfrm.h
--- linux-2.5.70-bk6/include/linux/xfrm.h	2003-05-26 18:00:25.000000000 -0700
+++ linux-2.5.70-bk7/include/linux/xfrm.h	2003-06-02 04:39:12.000000000 -0700
@@ -44,7 +44,7 @@
 	uid_t	user;
 };
 
-#define XFRM_INF (~(u64)0)
+#define XFRM_INF (~(__u64)0)
 
 struct xfrm_lifetime_cfg
 {
@@ -164,7 +164,7 @@
 };
 
 struct xfrm_usersa_id {
-	xfrm_address_t			saddr;
+	xfrm_address_t			daddr;
 	__u32				spi;
 	__u16				family;
 	__u8				proto;
diff -urN linux-2.5.70-bk6/include/net/sock.h linux-2.5.70-bk7/include/net/sock.h
--- linux-2.5.70-bk6/include/net/sock.h	2003-06-02 04:39:08.000000000 -0700
+++ linux-2.5.70-bk7/include/net/sock.h	2003-06-02 04:39:12.000000000 -0700
@@ -157,18 +157,18 @@
  */
 struct sock {
 	/* Begin of struct sock/struct tcp_tw_bucket shared layout */
-	volatile unsigned char	state,
-				zapped;
+	unsigned short		family;
+	volatile unsigned char	state;
 	unsigned char		reuse;
-	unsigned char		shutdown;
 	int			bound_dev_if;
 	struct sock		*next;
 	struct sock		**pprev;
 	struct sock		*bind_next;
 	struct sock		**bind_pprev;
 	atomic_t		refcnt;
-	unsigned short		family;
 	/* End of struct sock/struct tcp_tw_bucket shared layout */
+	volatile unsigned char	zapped;
+	unsigned char		shutdown;
 	unsigned char		use_write_queue;
 	unsigned char		userlocks;
 	socket_lock_t		lock;
diff -urN linux-2.5.70-bk6/include/net/tcp.h linux-2.5.70-bk7/include/net/tcp.h
--- linux-2.5.70-bk6/include/net/tcp.h	2003-06-02 04:39:08.000000000 -0700
+++ linux-2.5.70-bk7/include/net/tcp.h	2003-06-02 04:39:12.000000000 -0700
@@ -169,10 +169,9 @@
 	 * XXX Yes I know this is gross, but I'd have to edit every single
 	 * XXX networking file if I created a "struct sock_header". -DaveM
 	 */
-	volatile unsigned char	state,		/* Connection state	      */
-				substate;	/* "zapped" -> "substate"     */
+	unsigned short		family;
+	volatile unsigned char	state;		/* Connection state	      */
 	unsigned char		reuse;		/* SO_REUSEADDR setting       */
-	unsigned char		rcv_wscale;	/* also TW bucket specific    */
 	int			bound_dev_if;
 	/* Main hash linkage for various protocol lookup tables. */
 	struct sock		*next;
@@ -180,8 +179,9 @@
 	struct sock		*bind_next;
 	struct sock		**bind_pprev;
 	atomic_t		refcnt;
-	unsigned short		family;
 	/* End of struct sock/struct tcp_tw_bucket shared layout */
+	volatile unsigned char	substate;
+	unsigned char		rcv_wscale;
 	__u16			sport;
 	/* Socket demultiplex comparisons on incoming packets. */
 	/* these five are in inet_opt */
diff -urN linux-2.5.70-bk6/include/sound/ac97_codec.h linux-2.5.70-bk7/include/sound/ac97_codec.h
--- linux-2.5.70-bk6/include/sound/ac97_codec.h	2003-05-26 18:00:40.000000000 -0700
+++ linux-2.5.70-bk7/include/sound/ac97_codec.h	2003-06-02 04:39:12.000000000 -0700
@@ -293,6 +293,7 @@
 			unsigned short codec_cfg[3];	// CODEC_CFG bits
 			struct semaphore mutex;
 		} ad18xx;
+		unsigned int dev_flags;		/* device specific */
 	} spec;
 };
 
@@ -329,7 +330,7 @@
 void snd_ac97_resume(ac97_t *ac97);
 #endif
 
-enum { AC97_TUNE_HP_ONLY, AC97_TUNE_SWAP_HP };
+enum { AC97_TUNE_HP_ONLY, AC97_TUNE_SWAP_HP, AC97_TUNE_SWAP_SURROUND };
 
 struct ac97_quirk {
 	unsigned short vendor;
diff -urN linux-2.5.70-bk6/include/sound/ak4xxx-adda.h linux-2.5.70-bk7/include/sound/ak4xxx-adda.h
--- linux-2.5.70-bk6/include/sound/ak4xxx-adda.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/include/sound/ak4xxx-adda.h	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,61 @@
+#ifndef __SOUND_AK4XXX_ADDA_H
+#define __SOUND_AK4XXX_ADDA_H
+
+/*
+ *   ALSA driver for AK4524 / AK4528 / AK4529 / AK4355 / AK4381
+ *   AD and DA converters
+ *
+ *	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
+ *
+ */      
+
+#ifndef AK4XXX_MAX_CHIPS
+#define AK4XXX_MAX_CHIPS	4
+#endif
+
+typedef struct snd_akm4xxx akm4xxx_t;
+
+struct snd_ak4xxx_ops {
+	void (*lock)(akm4xxx_t *ak, int chip);
+	void (*unlock)(akm4xxx_t *ak, int chip);
+	void (*write)(akm4xxx_t *ak, int chip, unsigned char reg, unsigned char val);
+	// unsigned char (*read)(akm4xxx_t *ak, int chip, unsigned char reg);
+	void (*set_rate_val)(akm4xxx_t *ak, unsigned int rate);
+};
+
+struct snd_akm4xxx {
+	snd_card_t *card;
+	unsigned int num_adcs;				/* AK4524 or AK4528 ADCs */
+	unsigned int num_dacs;				/* AK4524 or AK4528 DACs */
+	unsigned char images[AK4XXX_MAX_CHIPS][16];	/* saved register image */
+	unsigned char ipga_gain[AK4XXX_MAX_CHIPS][2];	/* saved register image for IPGA (AK4528) */
+	unsigned long private_value[AK4XXX_MAX_CHIPS];	/* helper for driver */
+	void *private_data[AK4XXX_MAX_CHIPS];		/* helper for driver */
+	/* template should fill the following fields */
+	unsigned int idx_offset;			/* control index offset */
+	enum {
+		SND_AK4524, SND_AK4528, SND_AK4529, SND_AK4355, SND_AK4381
+	} type;
+	struct snd_ak4xxx_ops ops;
+};
+
+void snd_akm4xxx_write(akm4xxx_t *ak, int chip, unsigned char reg, unsigned char val);
+void snd_akm4xxx_reset(akm4xxx_t *ak, int state);
+void snd_akm4xxx_init(akm4xxx_t *ak);
+int snd_akm4xxx_build_controls(akm4xxx_t *ak);
+
+#endif /* __SOUND_AK4XXX_ADDA_H */
diff -urN linux-2.5.70-bk6/include/sound/asequencer.h linux-2.5.70-bk7/include/sound/asequencer.h
--- linux-2.5.70-bk6/include/sound/asequencer.h	2003-05-26 18:00:41.000000000 -0700
+++ linux-2.5.70-bk7/include/sound/asequencer.h	2003-06-02 04:39:12.000000000 -0700
@@ -596,7 +596,7 @@
 #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_SYNTH	(1<<10)	/* Synth device (no MIDI compatible - direct wavetable) */
 #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) */
 /*...*/
diff -urN linux-2.5.70-bk6/include/sound/core.h linux-2.5.70-bk7/include/sound/core.h
--- linux-2.5.70-bk6/include/sound/core.h	2003-05-26 18:01:01.000000000 -0700
+++ linux-2.5.70-bk7/include/sound/core.h	2003-06-02 04:39:12.000000000 -0700
@@ -184,7 +184,7 @@
 	up(&card->power_lock);
 }
 
-void snd_power_wait(snd_card_t *card);
+int snd_power_wait(snd_card_t *card, unsigned int power_state, struct file *file);
 
 static inline unsigned int snd_power_get_state(snd_card_t *card)
 {
@@ -199,7 +199,7 @@
 #else
 #define snd_power_lock(card)		do { (void)(card); } while (0)
 #define snd_power_unlock(card)		do { (void)(card); } while (0)
-#define snd_power_wait(card)		do { (void)(card); } while (0)
+static inline int snd_power_wait(snd_card_t *card, unsigned int state, struct file *file) { return 0; }
 #define snd_power_get_state(card)	SNDRV_CTL_POWER_D0
 #define snd_power_change_state(card, state)	do { (void)(card); } while (0)
 #endif
@@ -211,8 +211,8 @@
 	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 */
+	char name[0];			/* device name (keep at the end of structure) */
 };
 
 typedef struct _snd_minor snd_minor_t;
@@ -240,12 +240,13 @@
 /* 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);
-
+#else
+#define snd_minor_info_oss_init() /*NOP*/
+#define snd_minor_info_oss_done() /*NOP*/
+#define snd_oss_init_module() /*NOP*/
 #endif
 
 /* memory.c */
@@ -268,6 +269,10 @@
 #define kfree_nocheck(obj) snd_wrapper_kfree(obj)
 #define vfree_nocheck(obj) snd_wrapper_vfree(obj)
 #else
+#define snd_memory_init() /*NOP*/
+#define snd_memory_done() /*NOP*/
+#define snd_memory_info_init() /*NOP*/
+#define snd_memory_info_done() /*NOP*/
 #define kmalloc_nocheck(size, flags) kmalloc(size, flags)
 #define vmalloc_nocheck(size) vmalloc(size)
 #define kfree_nocheck(obj) kfree(obj)
diff -urN linux-2.5.70-bk6/include/sound/cs8427.h linux-2.5.70-bk7/include/sound/cs8427.h
--- linux-2.5.70-bk6/include/sound/cs8427.h	2003-05-26 18:00:41.000000000 -0700
+++ linux-2.5.70-bk7/include/sound/cs8427.h	2003-06-02 04:39:12.000000000 -0700
@@ -48,7 +48,7 @@
 #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_CORU_DATABUF	0x20	/* 24 byte buffer area */
 #define CS8427_REG_ID_AND_VER	0x7f
 
 /* CS8427_REG_CONTROL1 bits */
@@ -188,6 +188,7 @@
 
 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);
+void snd_cs8427_reset(snd_i2c_device_t *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);
diff -urN linux-2.5.70-bk6/include/sound/info.h linux-2.5.70-bk7/include/sound/info.h
--- linux-2.5.70-bk6/include/sound/info.h	2003-05-26 18:00:45.000000000 -0700
+++ linux-2.5.70-bk7/include/sound/info.h	2003-06-02 04:39:12.000000000 -0700
@@ -69,20 +69,15 @@
 		     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;
+	unsigned short disconnected: 1;
 	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;
diff -urN linux-2.5.70-bk6/include/sound/initval.h linux-2.5.70-bk7/include/sound/initval.h
--- linux-2.5.70-bk6/include/sound/initval.h	2003-05-26 18:00:20.000000000 -0700
+++ linux-2.5.70-bk7/include/sound/initval.h	2003-06-02 04:39:12.000000000 -0700
@@ -53,7 +53,7 @@
 #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__
+#ifdef CONFIG_PNP
 #define SNDRV_DEFAULT_ENABLE_ISAPNP SNDRV_DEFAULT_ENABLE_PNP
 #else
 #define SNDRV_DEFAULT_ENABLE_ISAPNP SNDRV_DEFAULT_ENABLE
diff -urN linux-2.5.70-bk6/include/sound/mpu401.h linux-2.5.70-bk7/include/sound/mpu401.h
--- linux-2.5.70-bk6/include/sound/mpu401.h	2003-05-26 18:00:56.000000000 -0700
+++ linux-2.5.70-bk7/include/sound/mpu401.h	2003-06-02 04:39:12.000000000 -0700
@@ -22,7 +22,6 @@
  *
  */
 
-#include <linux/interrupt.h>
 #include "rawmidi.h"
 #include <linux/interrupt.h>
 
@@ -50,8 +49,6 @@
 #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)
@@ -88,6 +85,9 @@
 	spinlock_t input_lock;
 	spinlock_t output_lock;
 	spinlock_t timer_lock;
+	
+	atomic_t rx_loop;
+	atomic_t tx_loop;
 
 	struct timer_list timer;
 
diff -urN linux-2.5.70-bk6/include/sound/opl4.h linux-2.5.70-bk7/include/sound/opl4.h
--- linux-2.5.70-bk6/include/sound/opl4.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/include/sound/opl4.h	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,32 @@
+#ifndef __SOUND_OPL4_H
+#define __SOUND_OPL4_H
+
+/*
+ * Global definitions for the OPL4 driver
+ * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.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/opl3.h>
+
+typedef struct opl4 opl4_t;
+
+extern int snd_opl4_create(snd_card_t *card,
+			   unsigned long fm_port, unsigned long pcm_port,
+			   int seq_device,
+			   opl3_t **opl3, opl4_t **opl4);
+
+#endif /* __SOUND_OPL4_H */
diff -urN linux-2.5.70-bk6/include/sound/pcm.h linux-2.5.70-bk7/include/sound/pcm.h
--- linux-2.5.70-bk6/include/sound/pcm.h	2003-05-26 18:00:39.000000000 -0700
+++ linux-2.5.70-bk7/include/sound/pcm.h	2003-06-02 04:39:12.000000000 -0700
@@ -317,8 +317,8 @@
 	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_uframes_t silence_start; /* starting pointer to silence area */
+	snd_pcm_uframes_t silence_filled; /* size filled with silence */
 
 	snd_pcm_sync_id_t sync;		/* hardware synchronization ID */
 
@@ -328,7 +328,6 @@
 	atomic_t mmap_count;
 
 	/* -- locking / scheduling -- */
-	spinlock_t lock;
 	wait_queue_head_t sleep;
 	struct timer_list tick_timer;
 	struct fasync_struct *fasync;
@@ -360,6 +359,11 @@
 #endif
 };
 
+typedef struct _snd_pcm_group {		/* keep linked substreams */
+	spinlock_t lock;
+	struct list_head substreams;
+} snd_pcm_group_t;
+
 struct _snd_pcm_substream {
 	snd_pcm_t *pcm;
 	snd_pcm_str_t *pstr;
@@ -383,8 +387,10 @@
 	/* -- next substream -- */
 	snd_pcm_substream_t *next;
 	/* -- linked substreams -- */
-	snd_pcm_substream_t *link_next;
-	snd_pcm_substream_t *link_prev;
+	struct list_head link_list;	/* linked list member */
+	snd_pcm_group_t self_group;	/* fake group for non linked substream (with substream lock inside) */
+	snd_pcm_group_t *group;		/* pointer to current group */
+	/* -- assigned files -- */
 	snd_pcm_file_t *file;
 	struct file *ffile;
 #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
@@ -468,6 +474,8 @@
  *  Native I/O
  */
 
+extern rwlock_t snd_pcm_link_rwlock;
+
 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);
@@ -568,6 +576,53 @@
  *  PCM library
  */
 
+static inline int snd_pcm_stream_linked(snd_pcm_substream_t *substream)
+{
+	return substream->group != &substream->self_group;
+}
+
+static inline void snd_pcm_stream_lock(snd_pcm_substream_t *substream)
+{
+	read_lock(&snd_pcm_link_rwlock);
+	spin_lock(&substream->self_group.lock);
+}
+
+static inline void snd_pcm_stream_unlock(snd_pcm_substream_t *substream)
+{
+	spin_unlock(&substream->self_group.lock);
+	read_unlock(&snd_pcm_link_rwlock);
+}
+
+static inline void snd_pcm_stream_lock_irq(snd_pcm_substream_t *substream)
+{
+	read_lock_irq(&snd_pcm_link_rwlock);
+	spin_lock(&substream->self_group.lock);
+}
+
+static inline void snd_pcm_stream_unlock_irq(snd_pcm_substream_t *substream)
+{
+	spin_unlock(&substream->self_group.lock);
+	read_unlock_irq(&snd_pcm_link_rwlock);
+}
+
+#define snd_pcm_stream_lock_irqsave(substream, flags) \
+do { \
+	read_lock_irqsave(&snd_pcm_link_rwlock, (flags)); \
+	spin_lock(&substream->self_group.lock); \
+} while (0)
+
+#define snd_pcm_stream_unlock_irqrestore(substream, flags) \
+do { \
+	spin_unlock(&substream->self_group.lock); \
+	read_unlock_irqrestore(&snd_pcm_link_rwlock, (flags)); \
+} while (0)
+
+#define snd_pcm_group_for_each(pos, substream) \
+	list_for_each(pos, &substream->group->substreams)
+
+#define snd_pcm_group_substream_entry(pos) \
+	list_entry(pos, snd_pcm_substream_t, link_list)
+
 static inline int snd_pcm_running(snd_pcm_substream_t *substream)
 {
 	return (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
diff -urN linux-2.5.70-bk6/include/sound/sb.h linux-2.5.70-bk7/include/sound/sb.h
--- linux-2.5.70-bk6/include/sound/sb.h	2003-05-26 18:00:39.000000000 -0700
+++ linux-2.5.70-bk7/include/sound/sb.h	2003-06-02 04:39:12.000000000 -0700
@@ -40,10 +40,11 @@
 	SB_HW_DT019X,		/* Diamond Tech. DT-019X / Avance Logic ALS-007 */
 };
 
-#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_OPEN_PCM			0x01
+#define SB_OPEN_MIDI_INPUT		0x02
+#define SB_OPEN_MIDI_OUTPUT		0x04
+#define SB_OPEN_MIDI_INPUT_TRIGGER	0x08
+#define SB_OPEN_MIDI_OUTPUT_TRIGGER	0x10
 
 #define SB_MODE_HALT		0x00
 #define SB_MODE_PLAYBACK_8	0x01
@@ -157,6 +158,7 @@
 #define SB_DSP_STEREO_16BIT	0xac
 
 #define SB_DSP_MIDI_INPUT_IRQ	0x31
+#define SB_DSP_MIDI_UART_IRQ	0x35
 #define SB_DSP_MIDI_OUTPUT	0x38
 
 #define SB_DSP4_OUT8_AI		0xc6
@@ -295,13 +297,13 @@
 /* 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);
+irqreturn_t 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);
+irqreturn_t snd_sb8dsp_midi_interrupt(sb_t *chip);
 int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi);
 
 /* sb16_init.c */
diff -urN linux-2.5.70-bk6/include/sound/sndmagic.h linux-2.5.70-bk7/include/sound/sndmagic.h
--- linux-2.5.70-bk6/include/sound/sndmagic.h	2003-05-26 18:00:27.000000000 -0700
+++ linux-2.5.70-bk7/include/sound/sndmagic.h	2003-06-02 04:39:12.000000000 -0700
@@ -193,6 +193,9 @@
 #define snd_usb_midi_in_endpoint_t_magic	0xa15a3f03
 #define ak4117_t_magic				0xa15a4000
 #define psic_t_magic				0xa15a4100
+#define opl4_t_magic				0xa15a2602
+#define vx_core_t_magic				0xa15a4110
+#define vx_pipe_t_magic				0xa15a4112
 
 
 #else
diff -urN linux-2.5.70-bk6/include/sound/uda1341.h linux-2.5.70-bk7/include/sound/uda1341.h
--- linux-2.5.70-bk6/include/sound/uda1341.h	2003-05-26 18:00:43.000000000 -0700
+++ linux-2.5.70-bk7/include/sound/uda1341.h	2003-06-02 04:39:12.000000000 -0700
@@ -15,7 +15,7 @@
  *                           features support
  */
 
-/* $Id: uda1341.h,v 1.4 2003/02/25 12:48:16 perex Exp $ */
+/* $Id: uda1341.h,v 1.5 2003/04/19 13:34:32 perex Exp $ */
 
 #define UDA1341_ALSA_NAME "snd-uda1341"
 
@@ -210,6 +210,10 @@
 	CMD_IG,
 	CMD_AGC_TIME,
 	CMD_AGC_LEVEL,
+#ifdef CONFIG_PM
+	CMD_SUSPEND,
+	CMD_RESUME,
+#endif
 	CMD_LAST,
 };
 
@@ -221,19 +225,6 @@
 };
 
 int __init snd_chip_uda1341_mixer_new(snd_card_t *card, struct l3_client **clnt);
-void __init snd_chip_uda1341_mixer_del(snd_card_t *card);
-
-#ifdef DEBUG_MODE
-#define DEBUG(format, args...)      do{printk(format, ##args);}while(0)
-#else
-#define DEBUG(format, args...)      /* nothing */
-#endif
-
-#ifdef DEBUG_FUNCTION_NAMES
-#define DEBUG_NAME(format, args...)     do{printk(format, ##args);}while(0)
-#else
-#define DEBUG_NAME(format, args...)     /* nothing */
-#endif
 
 /*
  * Local variables:
diff -urN linux-2.5.70-bk6/include/sound/version.h linux-2.5.70-bk7/include/sound/version.h
--- linux-2.5.70-bk6/include/sound/version.h	2003-05-26 18:00:38.000000000 -0700
+++ linux-2.5.70-bk7/include/sound/version.h	2003-06-02 04:39:12.000000000 -0700
@@ -1,3 +1,3 @@
 /* include/version.h.  Generated by configure.  */
-#define CONFIG_SND_VERSION "0.9.2"
-#define CONFIG_SND_DATE " (Thu Mar 20 13:31:57 2003 UTC)"
+#define CONFIG_SND_VERSION "0.9.4"
+#define CONFIG_SND_DATE " (Sat May 31 13:37:06 2003 UTC)"
diff -urN linux-2.5.70-bk6/include/sound/vx_core.h linux-2.5.70-bk7/include/sound/vx_core.h
--- linux-2.5.70-bk6/include/sound/vx_core.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/include/sound/vx_core.h	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,550 @@
+/*
+ * Driver for Digigram VX soundcards
+ *
+ * Hardware core part
+ *
+ * Copyright (c) 2002 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 __SOUND_VX_COMMON_H
+#define __SOUND_VX_COMMON_H
+
+#include <sound/pcm.h>
+#include <sound/hwdep.h>
+#include <linux/interrupt.h>
+
+typedef struct snd_vx_core vx_core_t;
+typedef struct vx_pipe vx_pipe_t;
+
+#define VX_DRIVER_VERSION	0x010000	/* 1.0.0 */
+
+/*
+ */
+#define SIZE_MAX_CMD    0x10
+#define SIZE_MAX_STATUS 0x10
+
+struct vx_rmh {
+	u16	LgCmd;		/* length of the command to send (WORDs) */
+	u16	LgStat;		/* length of the status received (WORDs) */
+	u32	Cmd[SIZE_MAX_CMD];
+	u32	Stat[SIZE_MAX_STATUS];
+	u16	DspStat;	/* status type, RMP_SSIZE_XXX */
+};
+	
+typedef u64 pcx_time_t;
+
+#define VX_MAX_PIPES	16
+#define VX_MAX_PERIODS	32
+#define VX_MAX_CODECS	2
+
+struct vx_ibl_info {
+	int size;	/* the current IBL size (0 = query) in bytes */
+	int max_size;	/* max. IBL size in bytes */
+	int min_size;	/* min. IBL size in bytes */
+	int granularity;	/* granularity */
+};
+
+struct vx_pipe {
+	int number;
+	unsigned int is_capture: 1;
+	unsigned int data_mode: 1;
+	unsigned int running: 1;
+	unsigned int prepared: 1;
+	int channels;
+	unsigned int differed_type;
+	pcx_time_t pcx_time;
+	snd_pcm_substream_t *substream;
+
+	int hbuf_size;		/* H-buffer size in bytes */
+	int buffer_bytes;	/* the ALSA pcm buffer size in bytes */
+	int period_bytes;	/* the ALSA pcm period size in bytes */
+	int hw_ptr;		/* the current hardware pointer in bytes */
+	int position;		/* the current position in frames (playback only) */
+	int transferred;	/* the transferred size (per period) in frames */
+	int align;		/* size of alignment */
+	u64 cur_count;		/* current sample position (for playback) */
+
+	unsigned int references;     /* an output pipe may be used for monitoring and/or playback */
+	vx_pipe_t *monitoring_pipe;  /* pointer to the monitoring pipe (capture pipe only)*/
+
+	struct tasklet_struct start_tq;
+};
+
+struct snd_vx_ops {
+	/* low-level i/o */
+	unsigned char (*in8)(vx_core_t *chip, int reg);
+	unsigned int (*in32)(vx_core_t *chip, int reg);
+	void (*out8)(vx_core_t *chip, int reg, unsigned char val);
+	void (*out32)(vx_core_t *chip, int reg, unsigned int val);
+	/* irq */
+	int (*test_and_ack)(vx_core_t *chip);
+	void (*validate_irq)(vx_core_t *chip, int enable);
+	/* codec */
+	void (*write_codec)(vx_core_t *chip, int codec, unsigned int data);
+	void (*akm_write)(vx_core_t *chip, int reg, unsigned int data);
+	void (*reset_codec)(vx_core_t *chip);
+	void (*change_audio_source)(vx_core_t *chip, int src);
+	void (*set_clock_source)(vx_core_t *chp, int src);
+	/* chip init */
+	int (*load_dsp)(vx_core_t *chip, const snd_hwdep_dsp_image_t *dsp);
+	void (*reset_dsp)(vx_core_t *chip);
+	void (*reset_board)(vx_core_t *chip, int cold_reset);
+	int (*add_controls)(vx_core_t *chip);
+	/* pcm */
+	void (*dma_write)(vx_core_t *chip, snd_pcm_runtime_t *runtime,
+			  vx_pipe_t *pipe, int count);
+	void (*dma_read)(vx_core_t *chip, snd_pcm_runtime_t *runtime,
+			  vx_pipe_t *pipe, int count);
+};
+
+struct snd_vx_hardware {
+	const char *name;
+	int type;	/* VX_TYPE_XXX */
+
+	/* hardware specs */
+	unsigned int num_codecs;
+	unsigned int num_ins;
+	unsigned int num_outs;
+	unsigned int output_level_max;
+};
+
+/* hwdep id string */
+#define SND_VX_HWDEP_ID		"VX Loader"
+
+/* hardware type */
+enum {
+	/* VX222 PCI */
+	VX_TYPE_BOARD,		/* old VX222 PCI */
+	VX_TYPE_V2,		/* VX222 V2 PCI */
+	VX_TYPE_MIC,		/* VX222 Mic PCI */
+	/* VX-pocket */
+	VX_TYPE_VXPOCKET,	/* VXpocket V2 */
+	VX_TYPE_VXP440,		/* VXpocket 440 */
+	VX_TYPE_NUMS
+};
+
+/* chip status */
+enum {
+	VX_STAT_XILINX_LOADED	= (1 << 0),	/* devices are registered */
+	VX_STAT_DEVICE_INIT	= (1 << 1),	/* devices are registered */
+	VX_STAT_CHIP_INIT	= (1 << 2),	/* all operational */
+	VX_STAT_IN_SUSPEND	= (1 << 10),	/* in suspend phase */
+	VX_STAT_IS_STALE	= (1 << 15)	/* device is stale */
+};
+
+/* min/max values for analog output for old codecs */
+#define VX_ANALOG_OUT_LEVEL_MAX		0xe3
+
+struct snd_vx_core {
+	/* ALSA stuff */
+	snd_card_t *card;
+	snd_pcm_t *pcm[VX_MAX_CODECS];
+	int type;	/* VX_TYPE_XXX */
+
+	int irq;
+	/* ports are defined externally */
+
+	/* low-level functions */
+	struct snd_vx_hardware *hw;
+	struct snd_vx_ops *ops;
+
+	spinlock_t lock;
+	spinlock_t irq_lock;
+	struct tasklet_struct tq;
+
+	unsigned int chip_status;
+	unsigned int pcm_running;
+
+	snd_hwdep_t *hwdep;
+
+	struct vx_rmh irq_rmh;	/* RMH used in interrupts */
+
+	unsigned int audio_info; /* see VX_AUDIO_INFO */
+	unsigned int audio_ins;
+	unsigned int audio_outs;
+	struct vx_pipe **playback_pipes;
+	struct vx_pipe **capture_pipes;
+
+	/* clock and audio sources */
+	unsigned int audio_source;	/* current audio input source */
+	unsigned int audio_source_target;
+	unsigned int clock_source;	/* current clock source (INTERNAL_QUARTZ or UER_SYNC) */
+	unsigned int freq;		/* current frequency */
+	unsigned int freq_detected;	/* detected frequency from digital in */
+	unsigned int uer_detected;	/* VX_UER_MODE_XXX */
+	unsigned int uer_bits;	/* IEC958 status bits */
+	struct vx_ibl_info ibl;	/* IBL information */
+
+	/* mixer setting */
+	int output_level[VX_MAX_CODECS][2];	/* analog output level */
+	int audio_gain[2][4];			/* digital audio level (playback/capture) */
+	unsigned char audio_active[4];		/* mute/unmute on digital playback */
+	int audio_monitor[4];			/* playback hw-monitor level */
+	unsigned char audio_monitor_active[4];	/* playback hw-monitor mute/unmute */
+
+	struct semaphore mixer_mutex;
+};
+
+
+/*
+ * constructor
+ */
+vx_core_t *snd_vx_create(snd_card_t *card, struct snd_vx_hardware *hw,
+			 struct snd_vx_ops *ops, int extra_size);
+int snd_vx_hwdep_new(vx_core_t *chip);
+int snd_vx_load_boot_image(vx_core_t *chip, const snd_hwdep_dsp_image_t *boot);
+int snd_vx_dsp_boot(vx_core_t *chip, const snd_hwdep_dsp_image_t *boot);
+int snd_vx_dsp_load(vx_core_t *chip, const snd_hwdep_dsp_image_t *dsp);
+
+/*
+ * interrupt handler; exported for pcmcia
+ */
+irqreturn_t snd_vx_irq_handler(int irq, void *dev, struct pt_regs *regs);
+
+/*
+ * power-management routines
+ */
+#ifdef CONFIG_PM
+void snd_vx_suspend(vx_core_t *chip);
+void snd_vx_resume(vx_core_t *chip);
+#endif
+
+
+/*
+ * lowlevel functions
+ */
+inline static int vx_test_and_ack(vx_core_t *chip)
+{
+	snd_assert(chip->ops->test_and_ack, return -ENXIO);
+	return chip->ops->test_and_ack(chip);
+}
+
+inline static void vx_validate_irq(vx_core_t *chip, int enable)
+{
+	snd_assert(chip->ops->validate_irq, return);
+	chip->ops->validate_irq(chip, enable);
+}
+
+inline static unsigned char snd_vx_inb(vx_core_t *chip, int reg)
+{
+	snd_assert(chip->ops->in8, return 0);
+	return chip->ops->in8(chip, reg);
+}
+
+inline static unsigned int snd_vx_inl(vx_core_t *chip, int reg)
+{
+	snd_assert(chip->ops->in32, return 0);
+	return chip->ops->in32(chip, reg);
+}
+
+inline static void snd_vx_outb(vx_core_t *chip, int reg, unsigned char val)
+{
+	snd_assert(chip->ops->out8, return);
+	return chip->ops->out8(chip, reg, val);
+}
+
+inline static void snd_vx_outl(vx_core_t *chip, int reg, unsigned int val)
+{
+	snd_assert(chip->ops->out32, return);
+	return chip->ops->out32(chip, reg, val);
+}
+
+#define vx_inb(chip,reg)	snd_vx_inb(chip, VX_##reg)
+#define vx_outb(chip,reg,val)	snd_vx_outb(chip, VX_##reg,val)
+#define vx_inl(chip,reg)	snd_vx_inl(chip, VX_##reg)
+#define vx_outl(chip,reg,val)	snd_vx_outl(chip, VX_##reg,val)
+
+void snd_vx_delay(vx_core_t *chip, int msec);
+
+static inline void vx_reset_dsp(vx_core_t *chip)
+{
+	snd_assert(chip->ops->reset_dsp, return);
+	chip->ops->reset_dsp(chip);
+}
+
+int vx_send_msg(vx_core_t *chip, struct vx_rmh *rmh);
+int vx_send_msg_nolock(vx_core_t *chip, struct vx_rmh *rmh);
+int vx_send_rih(vx_core_t *chip, int cmd);
+int vx_send_rih_nolock(vx_core_t *chip, int cmd);
+
+void vx_reset_codec(vx_core_t *chip, int cold_reset);
+
+/*
+ * check the bit on the specified register
+ * returns zero if a bit matches, or a negative error code.
+ * exported for vxpocket driver
+ */
+int snd_vx_check_reg_bit(vx_core_t *chip, int reg, int mask, int bit, int time);
+#define vx_check_isr(chip,mask,bit,time) snd_vx_check_reg_bit(chip, VX_ISR, mask, bit, time)
+#define vx_wait_isr_bit(chip,bit) vx_check_isr(chip, bit, bit, 200)
+#define vx_wait_for_rx_full(chip) vx_wait_isr_bit(chip, ISR_RX_FULL)
+
+
+/*
+ * pseudo-DMA transfer
+ */
+inline static void vx_pseudo_dma_write(vx_core_t *chip, snd_pcm_runtime_t *runtime,
+				       vx_pipe_t *pipe, int count)
+{
+	snd_assert(chip->ops->dma_write, return);
+	chip->ops->dma_write(chip, runtime, pipe, count);
+}
+
+inline static void vx_pseudo_dma_read(vx_core_t *chip, snd_pcm_runtime_t *runtime,
+				      vx_pipe_t *pipe, int count)
+{
+	snd_assert(chip->ops->dma_read, return);
+	chip->ops->dma_read(chip, runtime, pipe, count);
+}
+
+
+
+/* error with hardware code,
+ * the return value is -(VX_ERR_MASK | actual-hw-error-code)
+ */
+#define VX_ERR_MASK	0x1000000
+#define vx_get_error(err)	(-(err) & ~VX_ERR_MASK)
+
+
+/*
+ * pcm stuff
+ */
+int snd_vx_pcm_new(vx_core_t *chip);
+void vx_pcm_update_intr(vx_core_t *chip, unsigned int events);
+
+/*
+ * mixer stuff
+ */
+int snd_vx_mixer_new(vx_core_t *chip);
+void vx_toggle_dac_mute(vx_core_t *chip, int mute);
+int vx_sync_audio_source(vx_core_t *chip);
+int vx_set_monitor_level(vx_core_t *chip, int audio, int level, int active);
+
+/*
+ * IEC958 & clock stuff
+ */
+void vx_set_iec958_status(vx_core_t *chip, unsigned int bits);
+int vx_set_clock(vx_core_t *chip, unsigned int freq);
+void vx_change_clock_source(vx_core_t *chip, int source);
+void vx_set_internal_clock(vx_core_t *chip, unsigned int freq);
+int vx_change_frequency(vx_core_t *chip);
+
+
+/*
+ * hardware constants
+ */
+
+#define vx_has_new_dsp(chip)	((chip)->type != VX_TYPE_BOARD)
+#define vx_is_pcmcia(chip)	((chip)->type >= VX_TYPE_VXPOCKET)
+
+/* audio input source */
+enum {
+	VX_AUDIO_SRC_DIGITAL,
+	VX_AUDIO_SRC_LINE,
+	VX_AUDIO_SRC_MIC
+};
+
+/* clock source */
+enum {
+	INTERNAL_QUARTZ,
+	UER_SYNC
+};
+
+/* SPDIF/UER type */
+enum {
+	VX_UER_MODE_CONSUMER,
+	VX_UER_MODE_PROFESSIONAL,
+	VX_UER_MODE_NOT_PRESENT,
+};
+
+/* register indices */
+enum {
+	VX_ICR,
+	VX_CVR,
+	VX_ISR,
+	VX_IVR,
+	VX_RXH,
+	VX_TXH = VX_RXH,
+	VX_RXM,
+	VX_TXM = VX_RXM,
+	VX_RXL,
+	VX_TXL = VX_RXL,
+	VX_DMA,
+	VX_CDSP,
+	VX_RFREQ,
+	VX_RUER_V2,
+	VX_GAIN,
+	VX_DATA = VX_GAIN,
+	VX_MEMIRQ,
+	VX_ACQ,
+	VX_BIT0,
+	VX_BIT1,
+	VX_MIC0,
+	VX_MIC1,
+	VX_MIC2,
+	VX_MIC3,
+	VX_PLX0,
+	VX_PLX1,
+	VX_PLX2,
+
+	VX_LOFREQ,  // V2: ACQ, VP: RFREQ
+	VX_HIFREQ,  // V2: BIT0, VP: RUER_V2
+	VX_CSUER,   // V2: BIT1, VP: BIT0
+	VX_RUER,    // V2: RUER_V2, VP: BIT1
+
+	VX_REG_MAX,
+
+	/* aliases for VX board */
+	VX_RESET_DMA = VX_ISR,
+	VX_CFG = VX_RFREQ,
+	VX_STATUS = VX_MEMIRQ,
+	VX_SELMIC = VX_MIC0,
+	VX_COMPOT = VX_MIC1,
+	VX_SCOMPR = VX_MIC2,
+	VX_GLIMIT = VX_MIC3,
+	VX_INTCSR = VX_PLX0,
+	VX_CNTRL = VX_PLX1,
+	VX_GPIOC = VX_PLX2,
+
+	/* aliases for VXPOCKET board */
+	VX_MICRO = VX_MEMIRQ,
+	VX_CODEC2 = VX_MEMIRQ,
+	VX_DIALOG = VX_ACQ,
+
+};
+
+/* RMH status type */
+enum {
+	RMH_SSIZE_FIXED = 0,	/* status size given by the driver (in LgStat) */
+	RMH_SSIZE_ARG = 1,	/* status size given in the LSB byte */
+	RMH_SSIZE_MASK = 2,	/* status size given in bitmask */
+};
+
+
+/* bits for ICR register */
+#define ICR_HF1		0x10
+#define ICR_HF0		0x08
+#define ICR_TREQ	0x02	/* Interrupt mode + HREQ set on for transfer (->DSP) request */
+#define ICR_RREQ	0x01	/* Interrupt mode + RREQ set on for transfer (->PC) request */
+
+/* bits for CVR register */
+#define CVR_HC		0x80
+
+/* bits for ISR register */
+#define ISR_HF3		0x10
+#define ISR_HF2		0x08
+#define ISR_CHK		0x10
+#define ISR_ERR		0x08
+#define ISR_TX_READY	0x04
+#define ISR_TX_EMPTY	0x02
+#define ISR_RX_FULL	0x01
+
+/* Constants used to access the DATA register */
+#define VX_DATA_CODEC_MASK	0x80
+#define VX_DATA_XICOR_MASK	0x80
+
+/* Constants used to access the CSUER register (both for VX2 and VXP) */
+#define VX_SUER_FREQ_MASK		0x0c
+#define VX_SUER_FREQ_32KHz_MASK		0x0c
+#define VX_SUER_FREQ_44KHz_MASK		0x00
+#define VX_SUER_FREQ_48KHz_MASK		0x04
+#define VX_SUER_DATA_PRESENT_MASK	0x02
+#define VX_SUER_CLOCK_PRESENT_MASK	0x01
+
+#define VX_CUER_HH_BITC_SEL_MASK	0x08
+#define VX_CUER_MH_BITC_SEL_MASK	0x04
+#define VX_CUER_ML_BITC_SEL_MASK	0x02
+#define VX_CUER_LL_BITC_SEL_MASK	0x01
+
+#define XX_UER_CBITS_OFFSET_MASK	0x1f
+
+
+/* bits for audio_info */
+#define VX_AUDIO_INFO_REAL_TIME	(1<<0)	/* real-time processing available */
+#define VX_AUDIO_INFO_OFFLINE	(1<<1)	/* offline processing available */
+#define VX_AUDIO_INFO_MPEG1	(1<<5)
+#define VX_AUDIO_INFO_MPEG2	(1<<6)
+#define VX_AUDIO_INFO_LINEAR_8	(1<<7)
+#define VX_AUDIO_INFO_LINEAR_16	(1<<8)
+#define VX_AUDIO_INFO_LINEAR_24	(1<<9)
+
+/* DSP Interrupt Request values */
+#define VXP_IRQ_OFFSET		0x40 /* add 0x40 offset for vxpocket and vx222/v2 */
+/* call with vx_send_irq_dsp() */
+#define IRQ_MESS_WRITE_END          0x30
+#define IRQ_MESS_WRITE_NEXT         0x32
+#define IRQ_MESS_READ_NEXT          0x34
+#define IRQ_MESS_READ_END           0x36
+#define IRQ_MESSAGE                 0x38
+#define IRQ_RESET_CHK               0x3A
+#define IRQ_CONNECT_STREAM_NEXT     0x26
+#define IRQ_CONNECT_STREAM_END      0x28
+#define IRQ_PAUSE_START_CONNECT     0x2A
+#define IRQ_END_CONNECTION          0x2C
+
+/* Is there async. events pending ( IT Source Test ) */
+#define ASYNC_EVENTS_PENDING            0x008000
+#define HBUFFER_EVENTS_PENDING          0x004000   // Not always accurate
+#define NOTIF_EVENTS_PENDING            0x002000
+#define TIME_CODE_EVENT_PENDING         0x001000
+#define FREQUENCY_CHANGE_EVENT_PENDING  0x000800
+#define END_OF_BUFFER_EVENTS_PENDING    0x000400
+#define FATAL_DSP_ERROR                 0xff0000
+
+/* Stream Format Header Defines */ 
+#define HEADER_FMT_BASE			0xFED00000
+#define HEADER_FMT_MONO			0x000000C0
+#define HEADER_FMT_INTEL		0x00008000
+#define HEADER_FMT_16BITS		0x00002000
+#define HEADER_FMT_24BITS		0x00004000
+#define HEADER_FMT_UPTO11		0x00000200	/* frequency is less or equ. to 11k.*/
+#define HEADER_FMT_UPTO32		0x00000100	/* frequency is over 11k and less then 32k.*/
+
+/* Constants used to access the Codec */
+#define XX_CODEC_SELECTOR               0x20
+/* codec commands */
+#define XX_CODEC_ADC_CONTROL_REGISTER   0x01
+#define XX_CODEC_DAC_CONTROL_REGISTER   0x02
+#define XX_CODEC_LEVEL_LEFT_REGISTER    0x03
+#define XX_CODEC_LEVEL_RIGHT_REGISTER   0x04
+#define XX_CODEC_PORT_MODE_REGISTER     0x05
+#define XX_CODEC_STATUS_REPORT_REGISTER 0x06
+#define XX_CODEC_CLOCK_CONTROL_REGISTER 0x07
+
+/*
+ * Audio-level control values
+ */
+#define CVAL_M110DB		0x000	/* -110dB */
+#define CVAL_M99DB		0x02C
+#define CVAL_M21DB		0x163
+#define CVAL_M18DB		0x16F
+#define CVAL_M10DB		0x18F
+#define CVAL_0DB		0x1B7
+#define CVAL_18DB		0x1FF	/* +18dB */
+#define CVAL_MAX		0x1FF
+
+#define AUDIO_IO_HAS_MUTE_LEVEL			0x400000
+#define AUDIO_IO_HAS_MUTE_MONITORING_1		0x200000
+#define AUDIO_IO_HAS_MUTE_MONITORING_2		0x100000
+#define VALID_AUDIO_IO_DIGITAL_LEVEL		0x01
+#define VALID_AUDIO_IO_MONITORING_LEVEL		0x02
+#define VALID_AUDIO_IO_MUTE_LEVEL		0x04
+#define VALID_AUDIO_IO_MUTE_MONITORING_1	0x08
+#define VALID_AUDIO_IO_MUTE_MONITORING_2	0x10
+
+
+#endif /* __SOUND_VX_COMMON_H */
diff -urN linux-2.5.70-bk6/net/xfrm/xfrm_policy.c linux-2.5.70-bk7/net/xfrm/xfrm_policy.c
--- linux-2.5.70-bk6/net/xfrm/xfrm_policy.c	2003-06-02 04:39:08.000000000 -0700
+++ linux-2.5.70-bk7/net/xfrm/xfrm_policy.c	2003-06-02 04:39:12.000000000 -0700
@@ -280,7 +280,6 @@
 	write_unlock_bh(&xfrm_policy_lock);
 
 	if (delpol) {
-		atomic_dec(&delpol->refcnt);
 		xfrm_policy_kill(delpol);
 		xfrm_pol_put(delpol);
 	}
diff -urN linux-2.5.70-bk6/net/xfrm/xfrm_state.c linux-2.5.70-bk7/net/xfrm/xfrm_state.c
--- linux-2.5.70-bk6/net/xfrm/xfrm_state.c	2003-05-26 18:00:45.000000000 -0700
+++ linux-2.5.70-bk7/net/xfrm/xfrm_state.c	2003-06-02 04:39:12.000000000 -0700
@@ -112,9 +112,9 @@
 		if (tmo < next)
 			next = tmo;
 	}
-	if (x->lft.hard_use_expires_seconds && x->curlft.use_time) {
+	if (x->lft.hard_use_expires_seconds) {
 		long tmo = x->lft.hard_use_expires_seconds +
-			x->curlft.use_time - now;
+			(x->curlft.use_time ? : now) - now;
 		if (tmo <= 0)
 			goto expired;
 		if (tmo < next)
@@ -130,9 +130,9 @@
 		else if (tmo < next)
 			next = tmo;
 	}
-	if (x->lft.soft_use_expires_seconds && x->curlft.use_time) {
+	if (x->lft.soft_use_expires_seconds) {
 		long tmo = x->lft.soft_use_expires_seconds +
-			x->curlft.use_time - now;
+			(x->curlft.use_time ? : now) - now;
 		if (tmo <= 0)
 			warn = 1;
 		else if (tmo < next)
diff -urN linux-2.5.70-bk6/net/xfrm/xfrm_user.c linux-2.5.70-bk7/net/xfrm/xfrm_user.c
--- linux-2.5.70-bk6/net/xfrm/xfrm_user.c	2003-05-26 18:00:41.000000000 -0700
+++ linux-2.5.70-bk7/net/xfrm/xfrm_user.c	2003-06-02 04:39:12.000000000 -0700
@@ -260,7 +260,7 @@
 	if (!x)
 		return err;
 
-	x1 = xfrm_state_lookup(&x->props.saddr, x->id.spi, x->id.proto, x->props.family);
+	x1 = xfrm_state_lookup(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
 	if (x1) {
 		xfrm_state_put(x);
 		xfrm_state_put(x1);
@@ -277,7 +277,7 @@
 	struct xfrm_state *x;
 	struct xfrm_usersa_id *p = NLMSG_DATA(nlh);
 
-	x = xfrm_state_lookup(&p->saddr, p->spi, p->proto, p->family);
+	x = xfrm_state_lookup(&p->daddr, p->spi, p->proto, p->family);
 	if (x == NULL)
 		return -ESRCH;
 
@@ -403,7 +403,7 @@
 	struct sk_buff *resp_skb;
 	int err;
 
-	x = xfrm_state_lookup(&p->saddr, p->spi, p->proto, p->family);
+	x = xfrm_state_lookup(&p->daddr, p->spi, p->proto, p->family);
 	err = -ESRCH;
 	if (x == NULL)
 		goto out_noput;
diff -urN linux-2.5.70-bk6/sound/Kconfig linux-2.5.70-bk7/sound/Kconfig
--- linux-2.5.70-bk6/sound/Kconfig	2003-05-26 18:00:57.000000000 -0700
+++ linux-2.5.70-bk7/sound/Kconfig	2003-06-02 04:39:12.000000000 -0700
@@ -24,8 +24,14 @@
 # here assuming USB is defined before ALSA
 source "sound/usb/Kconfig"
 
+# the following will depenend on the order of config.
+# here assuming PCMCIA is defined before ALSA
+source "sound/pcmcia/Kconfig"
+
 source "sound/sparc/Kconfig"
 
+source "sound/parisc/Kconfig"
+
 endmenu
 
 menu "Open Sound System"
diff -urN linux-2.5.70-bk6/sound/Makefile linux-2.5.70-bk7/sound/Makefile
--- linux-2.5.70-bk6/sound/Makefile	2003-05-26 18:00:27.000000000 -0700
+++ linux-2.5.70-bk7/sound/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -3,8 +3,7 @@
 
 obj-$(CONFIG_SOUND) += soundcore.o
 obj-$(CONFIG_SOUND_PRIME) += oss/
-obj-$(CONFIG_DMASOUND) += oss/
-obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/
+obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/
 
 ifeq ($(CONFIG_SND),y)
   obj-y += last.o
diff -urN linux-2.5.70-bk6/sound/arm/sa11xx-uda1341.c linux-2.5.70-bk7/sound/arm/sa11xx-uda1341.c
--- linux-2.5.70-bk6/sound/arm/sa11xx-uda1341.c	2003-05-26 18:00:21.000000000 -0700
+++ linux-2.5.70-bk7/sound/arm/sa11xx-uda1341.c	2003-06-02 04:39:12.000000000 -0700
@@ -15,9 +15,13 @@
  * 2002-04-04   Tomas Kasparek  better rates handling (allow non-standard rates)
  * 2003-02-14   Brian Avery     fixed full duplex mode, other updates
  * 2003-02-20   Tomas Kasparek  merged updates by Brian (except HAL)
+ * 2003-04-19   Jaroslav Kysela recoded DMA stuff to follow 2.4.18rmk3-hh24 kernel
+ *                              working suspend and resume
+ * 2003-04-28   Tomas Kasparek  updated work by Jaroslav to compile it under 2.5.x again
+ *                              merged HAL layer (patches from Brian)
  */
 
-/* $Id: sa11xx-uda1341.c,v 1.8 2003/02/25 12:48:15 perex Exp $ */
+/* $Id: sa11xx-uda1341.c,v 1.11 2003/04/30 14:53:11 perex Exp $ */
 
 /***************************************************************************************************
 *
@@ -55,7 +59,7 @@
 * 
 ***************************************************************************************************/
 
-
+#include <linux/config.h>
 #include <sound/driver.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -64,9 +68,21 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+
 #include <asm/hardware.h>
+#include <asm/arch/h3600.h>
+#include <asm/mach-types.h>
 #include <asm/dma.h>
 
+#ifdef CONFIG_H3600_HAL
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/arch/h3600_hal.h>
+#endif
+
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/initval.h>
@@ -77,6 +93,15 @@
 #undef DEBUG_FUNCTION_NAMES
 #include <sound/uda1341.h>
 
+/*
+ * FIXME: Is this enough as autodetection of 2.4.X-rmkY-hhZ kernels?
+ * We use DMA stuff from 2.4.18-rmk3-hh24 here to be able to compile this
+ * module for Familiar 0.6.1
+ */
+#ifdef CONFIG_H3600_HAL
+#define HH_VERSION 1
+#endif
+
 /* {{{ Type definitions */
 
 MODULE_AUTHOR("Tomas Kasparek <tomas.kasparek@seznam.cz>");
@@ -92,26 +117,21 @@
 
 #define chip_t sa11xx_uda1341_t
 
-typedef enum stream_id_t{
-	PLAYBACK=0,
-	CAPTURE,
-	MAX_STREAMS,
-}stream_id_t;
-
 typedef struct audio_stream {
-	char *id;				/* identification string */
-	int  stream_id;			/* numeric identification */	
+	char *id;		/* identification string */
+	int stream_id;		/* numeric identification */	
 	dma_device_t dma_dev;	/* device identifier for DMA */
+#ifdef HH_VERSION
+	dmach_t dmach;		/* dma channel identification */
+#else
 	dma_regs_t *dma_regs;	/* points to our DMA registers */
-
-	int active:1;			/* we are using this stream for transfer now */
-
-	int sent_periods;       /* # of sent periods from actual DMA buffer */
-	int sent_total;         /* # of sent periods total (just for info & debug) */
-
-	int sync;               /* are we recoding - flag used to do DMA trans. for sync */
+#endif
+	int active:1;		/* we are using this stream for transfer now */
+	int period;		/* current transfer period */
+	int periods;		/* current count of periods registerd in the DMA engine */
+	int tx_spin;		/* are we recoding - flag used to do DMA trans. for sync */
+	unsigned int old_offset;
 	spinlock_t dma_lock;	/* for locking in DMA operations (see dma-sa1100.c in the kernel) */
-        
 	snd_pcm_substream_t *stream;
 }audio_stream_t;
 
@@ -119,10 +139,10 @@
 	struct pm_dev *pm_dev;        
 	snd_card_t *card;
 	struct l3_client *uda1341;
-
+	snd_pcm_t *pcm;
 	long samplerate;
-	audio_stream_t *s[MAX_STREAMS];
-}sa11xx_uda1341_t;
+	audio_stream_t s[2];	/* playback & capture */
+} sa11xx_uda1341_t;
 
 static struct snd_card_sa11xx_uda1341 *sa11xx_uda1341 = NULL;
 
@@ -185,8 +205,6 @@
 	int clk_div = 0;
 	int clk=0;
 
-	DEBUG(KERN_DEBUG "set_samplerate rate: %ld\n", rate);
-        
 	/* We don't want to mess with clocks when frames are in flight */
 	Ser4SSCR0 &= ~SSCR0_SSE;
 	/* wait for any frame to complete */
@@ -224,7 +242,11 @@
 		rate = 8000;
 
 	/* Set the external clock generator */
+#ifdef CONFIG_H3600_HAL
+	h3600_audio_clock(rate);
+#else	
 	sa11xx_uda1341_set_audio_clock(rate);
+#endif
 
 	/* Select the clock divisor */
 	switch (rate) {
@@ -256,7 +278,6 @@
 	
 	l3_command(sa11xx_uda1341->uda1341, CMD_FS, (void *)clk);        
 	Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE;
-	DEBUG(KERN_DEBUG "set_samplerate done (new rate: %ld)\n", rate);
 	sa11xx_uda1341->samplerate = rate;
 }
 
@@ -268,20 +289,14 @@
 {
 	unsigned long flags;
 
-	DEBUG_NAME(KERN_DEBUG "audio_init\n");
-
 	/* Setup DMA stuff */
-	if (sa11xx_uda1341->s[PLAYBACK]) {
-		sa11xx_uda1341->s[PLAYBACK]->id = "UDA1341 out";
-		sa11xx_uda1341->s[PLAYBACK]->stream_id = PLAYBACK;
-		sa11xx_uda1341->s[PLAYBACK]->dma_dev = DMA_Ser4SSPWr;
-	}
-
-	if (sa11xx_uda1341->s[CAPTURE]) {
-		sa11xx_uda1341->s[CAPTURE]->id = "UDA1341 in";
-		sa11xx_uda1341->s[CAPTURE]->stream_id = CAPTURE;
-		sa11xx_uda1341->s[CAPTURE]->dma_dev = DMA_Ser4SSPRd;
-	}
+	sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].id = "UDA1341 out";
+	sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = SNDRV_PCM_STREAM_PLAYBACK;
+	sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev = DMA_Ser4SSPWr;
+
+	sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].id = "UDA1341 in";
+	sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = SNDRV_PCM_STREAM_CAPTURE;
+	sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev = DMA_Ser4SSPRd;
 
 	/* Initialize the UDA1341 internal state */
        
@@ -296,41 +311,60 @@
 	local_irq_restore(flags);
 
 	/* Enable the audio power */
+#ifdef CONFIG_H3600_HAL
+	h3600_audio_power(AUDIO_RATE_DEFAULT);
+#else
 	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
 	set_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
 	set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
-        
+#endif
+ 
+	/* Wait for the UDA1341 to wake up */
+	mdelay(1); //FIXME - was removed by Perex - Why?
+
 	/* Initialize the UDA1341 internal state */
 	l3_open(sa11xx_uda1341->uda1341);
 	
-	/* external clock configuration (after l3_open - regs must be
-	 * initialized */
-	sa11xx_uda1341_set_samplerate(sa11xx_uda1341, AUDIO_RATE_DEFAULT);
+	/* external clock configuration (after l3_open - regs must be initialized */
+	sa11xx_uda1341_set_samplerate(sa11xx_uda1341, sa11xx_uda1341->samplerate);
 
 	/* Wait for the UDA1341 to wake up */
 	set_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
-	mdelay(1);
-	
+	mdelay(1);	
 
-	/* make the left and right channels unswapped (flip the WS latch ) */
+	/* make the left and right channels unswapped (flip the WS latch) */
 	Ser4SSDR = 0;
-       
+
+#ifdef CONFIG_H3600_HAL
+	h3600_audio_mute(0);
+#else	
 	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);        
+#endif     
 }
 
 static void sa11xx_uda1341_audio_shutdown(sa11xx_uda1341_t *sa11xx_uda1341)
 {
 	/* mute on */
+#ifdef CONFIG_H3600_HAL
+	h3600_audio_mute(1);
+#else	
 	set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
+#endif
 	
 	/* disable the audio power and all signals leading to the audio chip */
 	l3_close(sa11xx_uda1341->uda1341);
 	Ser4SSCR0 = 0;
 	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
-	/* power off */
+
+	/* power off and mute off */
+	/* FIXME - is muting off necesary??? */
+#ifdef CONFIG_H3600_HAL
+	h3600_audio_power(0);
+	h3600_audio_mute(0);
+#else	
 	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
-	/* mute off */
 	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
+#endif	
 }
 
 /* }}} */
@@ -344,16 +378,40 @@
 #define FORCE_CLOCK_ADDR		(dma_addr_t)FLUSH_BASE_PHYS
 #define FORCE_CLOCK_SIZE		4096 // was 2048
 
-static void audio_dma_request(audio_stream_t *s, void (*callback)(void *))
+// FIXME Why this value exactly - wrote comment
+#define DMA_BUF_SIZE	8176	/* <= MAX_DMA_SIZE from asm/arch-sa1100/dma.h */
+
+#ifdef HH_VERSION
+
+static int audio_dma_request(audio_stream_t *s, void (*callback)(void *, int))
 {
 	int ret;
 
-	DEBUG_NAME(KERN_DEBUG "audio_dma_request");
+	ret = sa1100_request_dma(&s->dmach, s->id, s->dma_dev);
+	if (ret < 0) {
+		printk(KERN_ERR "unable to grab audio dma 0x%x\n", s->dma_dev);
+		return ret;
+	}
+	sa1100_dma_set_callback(s->dmach, callback);
+	return 0;
+}
 
-	DEBUG("\t request id <%s>\n", s->id);
-	DEBUG("\t  request dma_dev = 0x%x \n", s->dma_dev);
-	ret = sa1100_request_dma((s)->dma_dev, (s)->id, callback, s, &((s)->dma_regs));
-	DEBUG("\t  request ret = %d\n", ret);
+static inline void audio_dma_free(audio_stream_t *s)
+{
+	sa1100_free_dma(s->dmach);
+	s->dmach = -1;
+}
+
+#else
+
+static int audio_dma_request(audio_stream_t *s, void (*callback)(void *))
+{
+	int ret;
+
+	ret = sa1100_request_dma(s->dma_dev, s->id, callback, s, &s->dma_regs);
+	if (ret < 0)
+		printk(KERN_ERR "unable to grab audio dma 0x%x\n", s->dma_dev);
+	return ret;
 }
 
 static void audio_dma_free(audio_stream_t *s)
@@ -362,33 +420,30 @@
 	(s)->dma_regs = 0;
 }
 
+#endif
+
 static u_int audio_get_dma_pos(audio_stream_t *s)
 {
 	snd_pcm_substream_t * substream = s->stream;
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	unsigned int offset;
 	unsigned long flags;
+	dma_addr_t addr;
 	
-	DEBUG_NAME(KERN_DEBUG "get_dma_pos");
-        
 	// this must be called w/ interrupts locked out see dma-sa1100.c in the kernel
 	spin_lock_irqsave(&s->dma_lock, flags);
-	offset = sa1100_get_dma_pos((s)->dma_regs) - runtime->dma_addr;
+#ifdef HH_VERSION	
+	sa1100_dma_get_current(s->dmach, NULL, &addr);
+#else
+	addr = sa1100_get_dma_pos((s)->dma_regs);
+#endif
+	offset = addr - runtime->dma_addr;
 	spin_unlock_irqrestore(&s->dma_lock, flags);
 	
-	DEBUG(" %d ->", offset);
 	offset = bytes_to_frames(runtime,offset);
-	DEBUG(" %d [fr]\n", offset);
-        
-	if (offset >= runtime->buffer_size){
-		offset = runtime->buffer_size;
-	}
+	if (offset >= runtime->buffer_size)
+		offset = 0;
 
-	DEBUG(KERN_DEBUG "  hw_ptr_interrupt: %lX\n",
-	      (unsigned long)runtime->hw_ptr_interrupt);
-	DEBUG(KERN_DEBUG "  updated pos [fr]: %ld\n",
-	      offset - (offset % runtime->min_align));
-        
 	return offset;
 }
 
@@ -397,143 +452,100 @@
  */
 static void audio_stop_dma(audio_stream_t *s)
 {
-	long flags;
-
-	DEBUG_NAME(KERN_DEBUG "stop_dma\n");
-        
-	/*
-	 * zero filling streams (sync=1) don;t have alsa streams attached 
-	 * but the 0 fill dma xfer still needs to be stopped
-	 */
-	if (!(s->stream || s->sync))
-		return;
+	unsigned long flags;
 
-	spin_lock_irqsave(&(s->dma_lock), flags);	
+	spin_lock_irqsave(&s->dma_lock, flags);	
 	s->active = 0;
-	s->sent_periods = 0;
-	s->sent_total = 0;
-	s->sync = 0;
-
+	s->period = 0;
 	/* this stops the dma channel and clears the buffer ptrs */
-	sa1100_clear_dma((s)->dma_regs);	
-	spin_unlock_irqrestore(&(s->dma_lock), flags);
-}
-
-static void audio_reset(audio_stream_t *s)
-{
-	DEBUG_NAME(KERN_DEBUG "dma_reset\n");
-        
-	if (s->stream) {
-		audio_stop_dma(s);
-	}
-	s->active = 0;
+#ifdef HH_VERSION
+	sa1100_dma_flush_all(s->dmach);
+#else
+	sa1100_clear_dma(s->dma_regs);	
+#endif
+	spin_unlock_irqrestore(&s->dma_lock, flags);
 }
 
-
 static void audio_process_dma(audio_stream_t *s)
 {
-	snd_pcm_substream_t * substream = s->stream;
+	snd_pcm_substream_t *substream = s->stream;
 	snd_pcm_runtime_t *runtime;
+	unsigned int dma_size;		
+	unsigned int offset;
 	int ret;
                 
-	DEBUG_NAME(KERN_DEBUG "process_dma\n");
-
 	/* we are requested to process synchronization DMA transfer */
-	if(!s->active && s->sync){
-		snd_assert(s->stream_id == PLAYBACK,return);		
+	if (s->tx_spin) {
+		snd_assert(s->stream_id == SNDRV_PCM_STREAM_PLAYBACK, return);
 		/* fill the xmit dma buffers and return */
+#ifdef HH_VERSION
+		sa1100_dma_set_spin(s->dmach, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE);
+#else
 		while (1) {
-			DEBUG(KERN_DEBUG "sent zero dma period (dma_size[B]: %d)\n", FORCE_CLOCK_SIZE);
-			ret = sa1100_start_dma((s)->dma_regs, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE);			
+			ret = sa1100_start_dma(s->dma_regs, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE);
 			if (ret)
 				return;   
 		}
+#endif
+		return;
 	}
 
 	/* must be set here - only valid for running streams, not for forced_clock dma fills  */
 	runtime = substream->runtime;
-                
-	DEBUG("audio_process_dma hw_ptr_base = 0x%x w_ptr_interrupt = 0x%x "
-		  "period_size = %d  periods  = %d buffer_size = %d sync=0x%x  dma_area = 0x%x\n",
-	       runtime->hw_ptr_base,
-	       runtime->hw_ptr_interrupt,
-	       runtime->period_size,
-	       runtime->periods,
-	       runtime->buffer_size,
-	       runtime->sync,
-	       runtime->dma_area);
-
-	DEBUG("audio_process_dma sent_total = %d  sent_period = %d\n",
-	       s->sent_total,
-	       s->sent_periods);
-	
-	while(s->active) {       
-		unsigned int  dma_size;		
-		unsigned int offset ;
-   
-		dma_size = frames_to_bytes(runtime,runtime->period_size) ;
-		offset = dma_size * s->sent_periods;
-		if (dma_size > MAX_DMA_SIZE){
-			/* this should not happen! */
-			printk(KERN_ERR "---> cut dma_size: %d -> ", dma_size);
-			dma_size = CUT_DMA_SIZE;
-			printk("%d <---\n", dma_size);
+	while (s->active && s->periods < runtime->periods) {
+		dma_size = frames_to_bytes(runtime, runtime->period_size);
+		if (s->old_offset) {
+			/* a little trick, we need resume from old position */
+			offset = frames_to_bytes(runtime, s->old_offset - 1);
+			s->old_offset = 0;
+			s->periods = 0;
+			s->period = offset / dma_size;
+			offset %= dma_size;
+			dma_size = dma_size - offset;
+			if (!dma_size)
+				continue;		/* special case */
+		} else {
+			offset = dma_size * s->period;
+			snd_assert(dma_size <= DMA_BUF_SIZE, );
 		}
-
-		/*
-		 * the first time this while loop will run 3 times, i.e. it'll fill the 2 dma
-		 * buffers then get a -EBUSY, every other time it'll refill the completed buffer
-		 * and then get the -EBUSY so it'll just run twice
-		 */
-		ret = sa1100_start_dma((s)->dma_regs, runtime->dma_addr + offset, dma_size);
+#ifdef HH_VERSION
+		ret = sa1100_dma_queue_buffer(s->dmach, s, runtime->dma_addr + offset, dma_size);
 		if (ret)
+			return; //FIXME
+#else
+		ret = sa1100_start_dma((s)->dma_regs, runtime->dma_addr + offset, dma_size);
+		if (ret) {
+			printk(KERN_ERR "audio_process_dma: cannot queue DMA buffer (%i)\n", ret);
 			return;
-
-		DEBUG(KERN_DEBUG "sent period %d (%d total)(dma_size[B]: %d"
-		      "offset[B]: %06d)\n",
-		      s->sent_periods, s->sent_total, dma_size, offset);
-
-#ifdef DEBUG_MODE
-		printk(KERN_DEBUG "  dma_area:");
-		for (i=0; i < 32; i++) {
-			printk(" %02x", *(char *)(runtime->dma_addr+offset+i));
 		}
-		printk("\n");
-#endif                
-		s->sent_total++;
-		s->sent_periods++;
-		s->sent_periods %= runtime->periods;
+#endif
+
+		s->period++;
+		s->period %= runtime->periods;
+		s->periods++;
 	}
 }
 
+#ifdef HH_VERSION
+static void audio_dma_callback(void *data, int size)
+#else
 static void audio_dma_callback(void *data)
+#endif
 {
 	audio_stream_t *s = data;
-	char *buf;
-	int i;
-        
-	DEBUG_NAME(KERN_DEBUG "dma_callback\n");
-
-	DEBUG(KERN_DEBUG "----> period done <----\n");
-
-#ifdef DEBUG_MODE
-	printk(KERN_DEBUG "  dma_area:");
-	buf = (char *)s->stream->runtime->dma_addr + ((s->sent_periods - 1 ) *
- 		  frames_to_bytes( s->stream->runtime, s->stream->runtime->period_size));	
-	for (i=0; i < 32; i++) {
-		printk(" %02x", *(char *)(buf + i));
-	}
-	printk("\n");
-#endif                      
         
-   /* 
-	* If we are getting a callback for an active stream then we inform
-	* the PCM middle layer we've finished a period
-	*/
+	/* 
+	 * If we are getting a callback for an active stream then we inform
+	 * the PCM middle layer we've finished a period
+	 */
  	if (s->active)
 		snd_pcm_period_elapsed(s->stream);
 
+	spin_lock(&s->dma_lock);
+	if (!s->tx_spin && s->periods > 0)
+		s->periods--;
 	audio_process_dma(s);
+	spin_unlock(&s->dma_lock);
 }
 
 /* }}} */
@@ -542,96 +554,172 @@
 
 /* {{{ trigger & timer */
 
-static int snd_card_sa11xx_uda1341_pcm_trigger(stream_id_t stream_id,
-				      snd_pcm_substream_t * substream, int cmd)
+static int snd_sa11xx_uda1341_trigger(snd_pcm_substream_t * substream, int cmd)
 {
 	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	int i;
-
-	DEBUG_NAME(KERN_DEBUG "pcm_trigger id: %d cmd: %d\n", stream_id, cmd);
+	int stream_id = substream->pstr->stream;
+	audio_stream_t *s = &chip->s[stream_id];
+	audio_stream_t *s1 = &chip->s[stream_id ^ 1];
+	int err = 0;
 
-	DEBUG(KERN_DEBUG "  sound: %d x %d [Hz]\n", runtime->channels, runtime->rate);
-	DEBUG(KERN_DEBUG "  periods: %ld x %ld [fr]\n", (unsigned long)runtime->periods,
-	      (unsigned long) runtime->period_size);
-	DEBUG(KERN_DEBUG "  buffer_size: %ld [fr]\n", (unsigned long)runtime->buffer_size);
-	DEBUG(KERN_DEBUG "  dma_addr %p\n", (char *)runtime->dma_addr);
-
-#ifdef DEBUG_MODE
-	printk(KERN_DEBUG "  dma_area:");
-	for (i=0; i < 32; i++) {
-		printk(" %02x", *(char *)(runtime->dma_addr+i));
-	}
-	printk("\n");
-#endif
-        
+	/* note local interrupts are already disabled in the midlevel code */
+	spin_lock(&s->dma_lock);
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_RESUME:
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		/* now we need to make sure a record only stream has a clock */
-		if (stream_id == CAPTURE && !chip->s[PLAYBACK]->active) {
-			/* we need to force fill the xmit DMA  with zeros */
-			DEBUG(KERN_DEBUG "starting zero fill  DMA transfer\n");
-			chip->s[PLAYBACK]->sync = 1;
-			audio_process_dma(chip->s[PLAYBACK]);
+		if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
+			/* we need to force fill the xmit DMA with zeros */
+			s1->tx_spin = 1;
+			audio_process_dma(s1);
 		}
 		/* this case is when you were recording then you turn on a
-		 * playback stream so we
-		 * stop (also clears it) the dma first, clear the sync flag
-		 * and then we let it get turned on
+		 * playback stream so we stop (also clears it) the dma first,
+		 * clear the sync flag and then we let it turned on
 		 */		
-		else if (stream_id == PLAYBACK && chip->s[PLAYBACK]->sync) {
- 			chip->s[PLAYBACK]->sync = 0;
-			audio_stop_dma(chip->s[PLAYBACK]);
-		}
+		else {
+ 			s->tx_spin = 0;
+ 		}
 
 		/* requested stream startup */
-		chip->s[stream_id]->active = 1;
-		audio_process_dma(chip->s[stream_id]);
+		s->active = 1;
+		audio_process_dma(s);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		/* requested stream shutdown */
-		chip->s[stream_id]->active = 0;
-		audio_stop_dma(chip->s[stream_id]);
+		audio_stop_dma(s);
 		
 		/*
 		 * now we need to make sure a record only stream has a clock
 		 * so if we're stopping a playback with an active capture
 		 * we need to turn the 0 fill dma on for the xmit side
 		 */
-		if (stream_id == PLAYBACK && chip->s[CAPTURE]->active) {
-			/* we need to force fill the xmit DMA  with zeros */
-			DEBUG(KERN_DEBUG "starting zero fill  DMA transfer\n");
-			chip->s[PLAYBACK]->sync = 1;
-			chip->s[PLAYBACK]->active = 0;
-			audio_process_dma(chip->s[PLAYBACK]);
+		if (stream_id == SNDRV_PCM_STREAM_PLAYBACK && s1->active) {
+			/* we need to force fill the xmit DMA with zeros */
+			s->tx_spin = 1;
+			audio_process_dma(s);
 		}
 		/*
 		 * we killed a capture only stream, so we should also kill
 		 * the zero fill transmit
 		 */
-		else if (stream_id == CAPTURE  && chip->s[PLAYBACK]->sync) {
-			audio_stop_dma(chip->s[PLAYBACK]);
+		else {
+			if (s1->tx_spin) {
+				s1->tx_spin = 0;
+				audio_stop_dma(s1);
+			}
 		}
 		
 		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		s->active = 0;
+#ifdef HH_VERSION		
+		sa1100_dma_stop(s->dmach);
+#else
+		//FIXME - DMA API
+#endif		
+		s->old_offset = audio_get_dma_pos(s) + 1;
+#ifdef HH_VERSION		
+		sa1100_dma_flush_all(s->dmach);
+#else
+		//FIXME - DMA API
+#endif		
+		s->periods = 0;
+		break;
+	case SNDRV_PCM_TRIGGER_RESUME:
+		s->active = 1;
+		s->tx_spin = 0;
+		audio_process_dma(s);
+		if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
+			s1->tx_spin = 1;
+			audio_process_dma(s1);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+#ifdef HH_VERSION		
+		sa1100_dma_stop(s->dmach);
+#else
+		//FIXME - DMA API
+#endif
+		s->active = 0;
+		if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+			if (s1->active) {
+				s->tx_spin = 1;
+				s->old_offset = audio_get_dma_pos(s) + 1;
+#ifdef HH_VERSION				
+				sa1100_dma_flush_all(s->dmach);
+#else
+				//FIXME - DMA API
+#endif				
+				audio_process_dma(s);
+			}
+		} else {
+			if (s1->tx_spin) {
+				s1->tx_spin = 0;
+#ifdef HH_VERSION				
+				sa1100_dma_flush_all(s1->dmach);
+#else
+				//FIXME - DMA API
+#endif				
+			}
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		s->active = 1;
+		if (s->old_offset) {
+			s->tx_spin = 0;
+			audio_process_dma(s);
+			break;
+		}
+		if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
+			s1->tx_spin = 1;
+			audio_process_dma(s1);
+		}
+#ifdef HH_VERSION		
+		sa1100_dma_resume(s->dmach);
+#else
+		//FIXME - DMA API
+#endif
+		break;
 	default:
-		return -EINVAL;
+		err = -EINVAL;
 		break;
 	}
+	spin_unlock(&s->dma_lock);	
+	return err;
+}
+
+static int snd_sa11xx_uda1341_prepare(snd_pcm_substream_t * substream)
+{
+	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	audio_stream_t *s = &chip->s[substream->pstr->stream];
+        
+	/* set requested samplerate */
+	sa11xx_uda1341_set_samplerate(chip, runtime->rate);
+
+	/* set requestd format when available */
+	/* set FMT here !!! FIXME */
+
+	s->period = 0;
+	s->periods = 0;
+        
 	return 0;
 }
 
+static snd_pcm_uframes_t snd_sa11xx_uda1341_pointer(snd_pcm_substream_t * substream)
+{
+	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+	return audio_get_dma_pos(&chip->s[substream->pstr->stream]);
+}
+
 /* }}} */
 
 static snd_pcm_hardware_t snd_sa11xx_uda1341_capture =
 {
 	.info			= (SNDRV_PCM_INFO_INTERLEAVED |
 				   SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				   SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+				   SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+				   SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
 	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
 	.rates			= (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
 				   SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
@@ -641,9 +729,9 @@
 	.rate_max		= 48000,
 	.channels_min		= 2,
 	.channels_max		= 2,
-	.buffer_bytes_max	= 16380,
+	.buffer_bytes_max	= 64*1024,
 	.period_bytes_min	= 64,
-	.period_bytes_max	= 8190, /* <= MAX_DMA_SIZE from ams/arch-sa1100/dma.h */
+	.period_bytes_max	= DMA_BUF_SIZE,
 	.periods_min		= 2,
 	.periods_max		= 255,
 	.fifo_size		= 0,
@@ -653,7 +741,8 @@
 {
 	.info			= (SNDRV_PCM_INFO_INTERLEAVED |
 				   SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				   SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+				   SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+				   SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
 	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
 	.rates			= (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
                                    SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
@@ -663,218 +752,87 @@
 	.rate_max		= 48000,
 	.channels_min		= 2,
 	.channels_max		= 2,
-	.buffer_bytes_max	= 16380,
+	.buffer_bytes_max	= 64*1024,
 	.period_bytes_min	= 64,
-	.period_bytes_max	= 8190, /* <= MAX_DMA_SIZE from ams/arch-sa1100/dma.h */
+	.period_bytes_max	= DMA_BUF_SIZE,
 	.periods_min		= 2,
 	.periods_max		= 255,
 	.fifo_size		= 0,
 };
 
-/* {{{ snd_card_sa11xx_uda1341_playback functions */
-
-static int snd_card_sa11xx_uda1341_playback_open(snd_pcm_substream_t * substream)
+static int snd_card_sa11xx_uda1341_open(snd_pcm_substream_t * substream)
 {
 	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
 	snd_pcm_runtime_t *runtime = substream->runtime;
+	int stream_id = substream->pstr->stream;
 	int err;
-        
-	DEBUG_NAME(KERN_DEBUG "playback_open\n");
-        
-	chip->s[PLAYBACK]->stream = substream;
-	chip->s[PLAYBACK]->sent_periods = 0;
-	chip->s[PLAYBACK]->sent_total = 0;
-        
-	/* no reset here since we may be zero filling the DMA
-	 * if we are, the dma stream will get reset in the pcm_trigger
-	 * i.e. when it actually starts to play
-	 */
-	/* audio_reset(chip->s[PLAYBACK]); */
- 
-	runtime->hw = snd_sa11xx_uda1341_playback;
-	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
-		return err;
-	if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-					      &hw_constraints_rates)) < 0)
-		return err;
-        
-	return 0;
-}
 
-static int snd_card_sa11xx_uda1341_playback_close(snd_pcm_substream_t * substream)
-{
-	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+	chip->s[stream_id].stream = substream;
 
-	DEBUG_NAME(KERN_DEBUG "playback_close\n");
-
-	chip->s[PLAYBACK]->stream = NULL;
-      
-	return 0;
-}
-
-static int snd_card_sa11xx_uda1341_playback_ioctl(snd_pcm_substream_t * substream,
-				         unsigned int cmd, void *arg)
-{
-	DEBUG_NAME(KERN_DEBUG "playback_ioctl cmd: %d\n", cmd);
-	return snd_pcm_lib_ioctl(substream, cmd, arg);
-}
-
-static int snd_card_sa11xx_uda1341_playback_prepare(snd_pcm_substream_t * substream)
-{
-	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-        
-	DEBUG_NAME(KERN_DEBUG "playback_prepare\n");
-                
-	/* set requested samplerate */
-	sa11xx_uda1341_set_samplerate(chip, runtime->rate);
-        
-	return 0;
-}
-
-static int snd_card_sa11xx_uda1341_playback_trigger(snd_pcm_substream_t * substream, int cmd)
-{
-	DEBUG_NAME(KERN_DEBUG "playback_trigger\n");
-	return snd_card_sa11xx_uda1341_pcm_trigger(PLAYBACK, substream, cmd);
-}
-
-static snd_pcm_uframes_t snd_card_sa11xx_uda1341_playback_pointer(snd_pcm_substream_t * substream)
-{
-	snd_pcm_uframes_t pos;
-	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
-
-	DEBUG_NAME(KERN_DEBUG "playback_pointer\n");        
-	
-	pos = audio_get_dma_pos(chip->s[PLAYBACK]);
-	return pos;
-}
-
-/* }}} */
-
-/* {{{ snd_card_sa11xx_uda1341_record functions */
-
-static int snd_card_sa11xx_uda1341_capture_open(snd_pcm_substream_t * substream)
-{
-	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	int err;
-
-	DEBUG_NAME(KERN_DEBUG "record_open\n");
-
-	chip->s[CAPTURE]->stream = substream;
-	chip->s[CAPTURE]->sent_periods = 0;
-	chip->s[CAPTURE]->sent_total = 0;
-        
-	audio_reset(chip->s[CAPTURE]);        
-
-	runtime->hw = snd_sa11xx_uda1341_capture;
+	if (stream_id == SNDRV_PCM_STREAM_PLAYBACK)
+		runtime->hw = snd_sa11xx_uda1341_playback;
+	else
+		runtime->hw = snd_sa11xx_uda1341_capture;
 	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
 		return err;
-	if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-					      &hw_constraints_rates)) < 0)
+	if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0)
 		return err;
         
 	return 0;
 }
 
-static int snd_card_sa11xx_uda1341_capture_close(snd_pcm_substream_t * substream)
-{
-	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
-
-	DEBUG_NAME(KERN_DEBUG "record_close\n");
-        
-	chip->s[CAPTURE]->stream = NULL;
-
-	return 0;
-}
-
-static int snd_card_sa11xx_uda1341_capture_ioctl(snd_pcm_substream_t * substream,
-					unsigned int cmd, void *arg)
-{
-	DEBUG_NAME(KERN_DEBUG "record_ioctl cmd: %d\n", cmd);
-	return snd_pcm_lib_ioctl(substream, cmd, arg);
-}
-
-static int snd_card_sa11xx_uda1341_capture_prepare(snd_pcm_substream_t * substream)
+static int snd_card_sa11xx_uda1341_close(snd_pcm_substream_t * substream)
 {
 	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-        
-	DEBUG_NAME(KERN_DEBUG "record_prepare\n");
 
-	/* set requested samplerate */
-	sa11xx_uda1341_set_samplerate(chip, runtime->rate);
-        
+	chip->s[substream->pstr->stream].stream = NULL;
 	return 0;
 }
 
-static int snd_card_sa11xx_uda1341_capture_trigger(snd_pcm_substream_t * substream, int cmd)
-{
-	DEBUG_NAME(KERN_DEBUG "record_trigger\n");
-	return snd_card_sa11xx_uda1341_pcm_trigger(CAPTURE, substream, cmd);
-}
-
-static snd_pcm_uframes_t snd_card_sa11xx_uda1341_capture_pointer(snd_pcm_substream_t * substream)
-{
-	snd_pcm_uframes_t pos;
-	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
-
-	DEBUG_NAME(KERN_DEBUG "record_pointer\n");        
-	pos = audio_get_dma_pos(chip->s[CAPTURE]);
-	return pos;
-}
-
-/* }}} */
-
 /* {{{ HW params & free */
 
 static int snd_sa11xx_uda1341_hw_params(snd_pcm_substream_t * substream,
-			       snd_pcm_hw_params_t * hw_params)
+					snd_pcm_hw_params_t * hw_params)
 {
         
-	DEBUG_NAME(KERN_DEBUG "hw_params\n");
 	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
 }
 
 static int snd_sa11xx_uda1341_hw_free(snd_pcm_substream_t * substream)
 {
-	DEBUG_NAME(KERN_DEBUG "hw_free\n");        
 	return snd_pcm_lib_free_pages(substream);
 }
 
 /* }}} */
 
 static snd_pcm_ops_t snd_card_sa11xx_uda1341_playback_ops = {
-	.open			= snd_card_sa11xx_uda1341_playback_open,
-	.close			= snd_card_sa11xx_uda1341_playback_close,
-	.ioctl			= snd_card_sa11xx_uda1341_playback_ioctl,
+	.open			= snd_card_sa11xx_uda1341_open,
+	.close			= snd_card_sa11xx_uda1341_close,
+	.ioctl			= snd_pcm_lib_ioctl,
 	.hw_params	        = snd_sa11xx_uda1341_hw_params,
 	.hw_free	        = snd_sa11xx_uda1341_hw_free,
-	.prepare		= snd_card_sa11xx_uda1341_playback_prepare,
-	.trigger		= snd_card_sa11xx_uda1341_playback_trigger,
-	.pointer		= snd_card_sa11xx_uda1341_playback_pointer,
+	.prepare		= snd_sa11xx_uda1341_prepare,
+	.trigger		= snd_sa11xx_uda1341_trigger,
+	.pointer		= snd_sa11xx_uda1341_pointer,
 };
 
 static snd_pcm_ops_t snd_card_sa11xx_uda1341_capture_ops = {
-	.open			= snd_card_sa11xx_uda1341_capture_open,
-	.close			= snd_card_sa11xx_uda1341_capture_close,
-	.ioctl			= snd_card_sa11xx_uda1341_capture_ioctl,
+	.open			= snd_card_sa11xx_uda1341_open,
+	.close			= snd_card_sa11xx_uda1341_close,
+	.ioctl			= snd_pcm_lib_ioctl,
 	.hw_params	        = snd_sa11xx_uda1341_hw_params,
 	.hw_free	        = snd_sa11xx_uda1341_hw_free,
-	.prepare		= snd_card_sa11xx_uda1341_capture_prepare,
-	.trigger		= snd_card_sa11xx_uda1341_capture_trigger,
-	.pointer		= snd_card_sa11xx_uda1341_capture_pointer,
+	.prepare		= snd_sa11xx_uda1341_prepare,
+	.trigger		= snd_sa11xx_uda1341_trigger,
+	.pointer		= snd_sa11xx_uda1341_pointer,
 };
 
-static int __init snd_card_sa11xx_uda1341_pcm(sa11xx_uda1341_t *sa11xx_uda1341, int device, int substreams)
+static int __init snd_card_sa11xx_uda1341_pcm(sa11xx_uda1341_t *sa11xx_uda1341, int device)
 {
 	snd_pcm_t *pcm;
 	int err;
 
-	DEBUG_NAME(KERN_DEBUG "sa11xx_uda1341_pcm\n");
-
-	if ((err = snd_pcm_new(sa11xx_uda1341->card, "UDA1341 PCM", device,
-			       substreams, substreams, &pcm)) < 0)
+	if ((err = snd_pcm_new(sa11xx_uda1341->card, "UDA1341 PCM", device, 1, 1, &pcm)) < 0)
 		return err;
 
 	/*
@@ -890,14 +848,13 @@
 	pcm->info_flags = 0;
 	strcpy(pcm->name, "UDA1341 PCM");
 
-	sa11xx_uda1341->s[PLAYBACK] = snd_kcalloc(sizeof(audio_stream_t), GFP_KERNEL);
-	sa11xx_uda1341->s[CAPTURE] = snd_kcalloc(sizeof(audio_stream_t), GFP_KERNEL);
-
 	sa11xx_uda1341_audio_init(sa11xx_uda1341);
 
 	/* setup DMA controller */
-	audio_dma_request(sa11xx_uda1341->s[PLAYBACK], audio_dma_callback);
-	audio_dma_request(sa11xx_uda1341->s[CAPTURE], audio_dma_callback);
+	audio_dma_request(&sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK], audio_dma_callback);
+	audio_dma_request(&sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE], audio_dma_callback);
+
+	sa11xx_uda1341->pcm = pcm;
 
 	return 0;
 }
@@ -908,73 +865,88 @@
 
 #ifdef CONFIG_PM
 
-static int sa11xx_uda1341_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data)
+static void snd_sa11xx_uda1341_suspend(sa11xx_uda1341_t *chip)
 {
-	sa11xx_uda1341_t *sa11xx_uda1341 = pm_dev->data;
-	audio_stream_t *is, *os;
-	int stopstate;
-
-	DEBUG_NAME(KERN_DEBUG "pm_callback\n");
-
-	/* pause resume is broken  see note */
-	printk("Pause/Resume support currently broken... \n");	
-        return -1;
+	snd_card_t *card = chip->card;
+
+	if (card->power_state == SNDRV_CTL_POWER_D3hot)
+		return;
+	snd_pcm_suspend_all(chip->pcm);
+#ifdef HH_VERSION	
+	sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach);
+	sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);
+#else
+	//FIXME
+#endif
+	l3_command(chip->uda1341, CMD_SUSPEND, NULL);
+	sa11xx_uda1341_audio_shutdown(chip);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+}
 
-	is = sa11xx_uda1341->s[PLAYBACK];
-	os = sa11xx_uda1341->s[CAPTURE];
+static void snd_sa11xx_uda1341_resume(sa11xx_uda1341_t *chip)
+{
+	snd_card_t *card = chip->card;
+
+	if (card->power_state == SNDRV_CTL_POWER_D0)
+		return;
+	sa11xx_uda1341_audio_init(chip);
+	l3_command(chip->uda1341, CMD_RESUME, NULL);
+#ifdef HH_VERSION	
+	sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach);
+	sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);
+#else
+	//FIXME
+#endif
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+}
+
+static int sa11xx_uda1341_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data)
+{
+	sa11xx_uda1341_t *chip = pm_dev->data;
         
 	switch (req) {
 	case PM_SUSPEND: /* enter D1-D3 */
-		if (is && is->dma_regs) {
-			stopstate = is->active;
-			audio_stop_dma(is);
-			DMA_CLEAR(is);
-			is->active = stopstate;
-		}
-		if (os && os->dma_regs) {
-			stopstate = os->active;
-			audio_stop_dma(os);
-			DMA_CLEAR(os);
-			os->active = stopstate;
-		}
-		if (is->stream || os->stream)
-			sa11xx_uda1341_audio_shutdown(sa11xx_uda1341);
+		snd_sa11xx_uda1341_suspend(chip);
 		break;
 	case PM_RESUME:  /* enter D0 */
-		if (is->stream || os->stream)
-			sa11xx_uda1341_audio_init(sa11xx_uda1341);
-		if (os && os->dma_regs) {
-			DMA_RESET(os);
-			audio_process_dma(os);
-		}
-		if (is && is->dma_regs) {
-			DMA_RESET(is);
-			audio_process_dma(is);
-		}
+		snd_sa11xx_uda1341_resume(chip);
 		break;
 	}
 	return 0;
 }
 
-#endif
-
-void snd_sa11xx_uda1341_free(snd_card_t *card)
+static int sa11xx_uda1341_set_power_state(snd_card_t *card, unsigned int power_state)
 {
-	sa11xx_uda1341_t *chip = snd_magic_cast(sa11xx_uda1341_t, card->private_data, return);
-
-	DEBUG_NAME(KERN_DEBUG "snd_sa11xx_uda1341_free\n");
+	sa11xx_uda1341_t *chip = snd_magic_cast(sa11xx_uda1341_t, card->power_state_private_data, return);
 
-	audio_dma_free(chip->s[PLAYBACK]);
-	audio_dma_free(chip->s[CAPTURE]);
+	switch (power_state) {
+	case SNDRV_CTL_POWER_D0:
+	case SNDRV_CTL_POWER_D1:
+	case SNDRV_CTL_POWER_D2:
+		snd_sa11xx_uda1341_resume(chip);
+		break;
+	case SNDRV_CTL_POWER_D3hot:
+	case SNDRV_CTL_POWER_D3cold:
+		snd_sa11xx_uda1341_suspend(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
 
-	kfree(chip->s[PLAYBACK]);
-	kfree(chip->s[CAPTURE]);
+#endif /* COMFIG_PM */
 
-	chip->s[PLAYBACK] = NULL;
-	chip->s[CAPTURE] = NULL;
+void snd_sa11xx_uda1341_free(snd_card_t *card)
+{
+	sa11xx_uda1341_t *chip = snd_magic_cast(sa11xx_uda1341_t, card->private_data, return);
 
-	snd_magic_kfree(chip);
+	pm_unregister(chip->pm_dev);
+	audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]);
+	audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
+	sa11xx_uda1341 = NULL;
 	card->private_data = NULL;
+	kfree(chip);
 }
 
 static int __init sa11xx_uda1341_init(void)
@@ -982,34 +954,36 @@
 	int err;
 	snd_card_t *card;
 
-	DEBUG_NAME(KERN_DEBUG "sa11xx_uda1341_uda1341_init\n");
-        
 	if (!machine_is_h3xxx())
 		return -ENODEV;
 
 	/* register the soundcard */
-	card = snd_card_new(-1, id, THIS_MODULE, 0);
+	card = snd_card_new(-1, id, THIS_MODULE, sizeof(sa11xx_uda1341_t));
 	if (card == NULL)
 		return -ENOMEM;
+
 	sa11xx_uda1341 = snd_magic_kcalloc(sa11xx_uda1341_t, 0, GFP_KERNEL);
 	if (sa11xx_uda1341 == NULL)
-		return -ENOMEM;
+		return -ENOMEM;	
          
 	card->private_data = (void *)sa11xx_uda1341;
 	card->private_free = snd_sa11xx_uda1341_free;
-        
+
 	sa11xx_uda1341->card = card;
+	sa11xx_uda1341->samplerate = AUDIO_RATE_DEFAULT;
 
 	// mixer
 	if ((err = snd_chip_uda1341_mixer_new(sa11xx_uda1341->card, &sa11xx_uda1341->uda1341)))
 		goto nodev;
 
 	// PCM
-	if ((err = snd_card_sa11xx_uda1341_pcm(sa11xx_uda1341, 0, 2)) < 0)
+	if ((err = snd_card_sa11xx_uda1341_pcm(sa11xx_uda1341, 0)) < 0)
 		goto nodev;
         
        
 #ifdef CONFIG_PM
+	card->power_state_private_data = sa11xx_uda1341;
+	card->set_power_state = sa11xx_uda1341_set_power_state;
 	sa11xx_uda1341->pm_dev = pm_register(PM_SYS_DEV, 0, sa11xx_uda1341_pm_callback);
 	if (sa11xx_uda1341->pm_dev)
 		sa11xx_uda1341->pm_dev->data = sa11xx_uda1341;
@@ -1031,9 +1005,7 @@
 
 static void __exit sa11xx_uda1341_exit(void)
 {
-	snd_chip_uda1341_mixer_del(sa11xx_uda1341->card);
 	snd_card_free(sa11xx_uda1341->card);
-	sa11xx_uda1341 = NULL;
 }
 
 module_init(sa11xx_uda1341_init);
diff -urN linux-2.5.70-bk6/sound/core/Makefile linux-2.5.70-bk7/sound/core/Makefile
--- linux-2.5.70-bk6/sound/core/Makefile	2003-05-26 18:00:43.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -69,6 +69,7 @@
 obj-$(CONFIG_SND_SBAWE) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
 obj-$(CONFIG_SND_ES968) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
 obj-$(CONFIG_SND_WAVEFRONT) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_SSCAPE) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
 obj-$(CONFIG_SND_ALS4000) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
 obj-$(CONFIG_SND_CMIPCI) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
 obj-$(CONFIG_SND_CS4281) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
@@ -78,6 +79,7 @@
 obj-$(CONFIG_SND_ES1968) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
 obj-$(CONFIG_SND_FM801) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
 obj-$(CONFIG_SND_ICE1712) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_ICE1724) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
 obj-$(CONFIG_SND_INTEL8X0) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
 obj-$(CONFIG_SND_MAESTRO3) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
 obj-$(CONFIG_SND_RME32) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
@@ -90,7 +92,7 @@
 obj-$(CONFIG_SND_KORG1212) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
 obj-$(CONFIG_SND_NM256) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
 obj-$(CONFIG_SND_RME9652) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
-obj-$(CONFIG_SND_HDSP) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_HDSP) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
 obj-$(CONFIG_SND_TRIDENT) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
 obj-$(CONFIG_SND_YMFPCI) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
 obj-$(CONFIG_SND_POWERMAC) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
@@ -101,5 +103,11 @@
   obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o
 endif
 obj-$(CONFIG_SND_USB_AUDIO) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_SUN_AMD7930) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
+obj-$(CONFIG_SND_SUN_CS4231) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
+obj-$(CONFIG_SND_HARMONY) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
+obj-$(CONFIG_SND_VXPOCKET) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o
+obj-$(CONFIG_SND_VXP440) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o
+obj-$(CONFIG_SND_VX222) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o
 
 obj-m := $(sort $(obj-m))
diff -urN linux-2.5.70-bk6/sound/core/control.c linux-2.5.70-bk7/sound/core/control.c
--- linux-2.5.70-bk6/sound/core/control.c	2003-05-26 18:00:24.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/control.c	2003-06-02 04:39:12.000000000 -0700
@@ -396,10 +396,6 @@
 			continue;
 		if (kctl->id.index + kctl->count <= id->index)
 			continue;
-		if (kctl->id.index != id->index) {
-			printk("BOOOR: %i, %i, %i\n", kctl->id.index, kctl->count, id->index);
-			continue;
-		}
 		return kctl;
 	}
 	return NULL;
@@ -772,9 +768,9 @@
 			return -EPERM;
 #ifdef CONFIG_PM
 		if (card->set_power_state) {
-		    snd_power_lock(card);
-		    err = card->set_power_state(card, err);
-		    snd_power_unlock(card);
+			snd_power_lock(card);
+			err = card->set_power_state(card, err);
+			snd_power_unlock(card);
 		} else
 #endif
 			err = -ENOPROTOOPT;
@@ -931,9 +927,7 @@
 
 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,
diff -urN linux-2.5.70-bk6/sound/core/hwdep.c linux-2.5.70-bk7/sound/core/hwdep.c
--- linux-2.5.70-bk6/sound/core/hwdep.c	2003-05-26 18:00:21.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/hwdep.c	2003-06-02 04:39:12.000000000 -0700
@@ -292,9 +292,7 @@
 
 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,
diff -urN linux-2.5.70-bk6/sound/core/info.c linux-2.5.70-bk7/sound/core/info.c
--- linux-2.5.70-bk6/sound/core/info.c	2003-05-26 18:00:59.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/info.c	2003-06-02 04:39:12.000000000 -0700
@@ -41,7 +41,6 @@
 {
 	static char *reserved[] =
 	{
-		"dev",
 		"version",
 		"meminfo",
 		"memdebug",
@@ -69,9 +68,6 @@
 
 #ifdef CONFIG_PROC_FS
 
-extern int major;
-extern struct file_operations snd_fops;
-
 static DECLARE_MUTEX(info_mutex);
 
 typedef struct _snd_info_private_data {
@@ -120,33 +116,11 @@
  */
 
 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;
@@ -158,7 +132,6 @@
 	if (de)
 		remove_proc_entry(de->name, parent);
 }
-#endif
 
 static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
 {
@@ -168,9 +141,7 @@
 
 	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) {
@@ -199,9 +170,7 @@
 	}
 	ret = -ENXIO;
 out:
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3)
 	unlock_kernel();
-#endif
 	return ret;
 }
 
@@ -296,7 +265,7 @@
 	down(&info_mutex);
 	p = PDE(inode);
 	entry = p == NULL ? NULL : (snd_info_entry_t *)p->data;
-	if (entry == NULL) {
+	if (entry == NULL || entry->disconnected) {
 		up(&info_mutex);
 		return -ENODEV;
 	}
@@ -520,9 +489,7 @@
 
 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,
@@ -533,71 +500,6 @@
 	.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,
-};
-
 /**
  * snd_create_proc_entry - create a procfs entry
  * @name: the name of the proc file
@@ -627,10 +529,6 @@
 	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;
@@ -658,13 +556,9 @@
 	}
 #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;
 }
@@ -672,13 +566,9 @@
 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)
@@ -689,7 +579,6 @@
 		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;
@@ -729,7 +618,6 @@
  */
 int snd_info_card_register(snd_card_t * card)
 {
-	char *s;
 	struct proc_dir_entry *p;
 
 	snd_assert(card != NULL, return -ENXIO);
@@ -737,19 +625,9 @@
 	if (!strcmp(card->id, card->proc_root->name))
 		return 0;
 
-	s = snd_kmalloc_strdup(card->proc_root->name, 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);
+	p = proc_symlink(card->id, snd_proc_root, card->proc_root->name);
 	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;
 }
@@ -760,13 +638,8 @@
  */
 int snd_info_card_free(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;
 	}
@@ -941,6 +814,13 @@
 	return snd_info_register(entry);
 }
 
+static int snd_info_dev_disconnect_entry(snd_device_t *device)
+{
+	snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO);
+	entry->disconnected = 1;
+	return 0;
+}
+
 static int snd_info_dev_unregister_entry(snd_device_t *device)
 {
 	snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO);
@@ -973,7 +853,7 @@
 	static snd_device_ops_t ops = {
 		.dev_free = snd_info_dev_free_entry,
 		.dev_register =	snd_info_dev_register_entry,
-		// .dev_disconnect = snd_info_dev_disconnect_entry,
+		.dev_disconnect = snd_info_dev_disconnect_entry,
 		.dev_unregister = snd_info_dev_unregister_entry
 	};
 	snd_info_entry_t *entry;
@@ -1008,97 +888,6 @@
 	snd_magic_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 = device_gid;
-	inode->i_uid = 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;
-	de->ops = &snd_info_device_inode_operations;
-}
-#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 */
-
-/*
- * create a procfs device file
- */
-snd_info_entry_t *snd_info_create_device(const char *name, unsigned int number, unsigned int mode)
-{
-	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 = major;
-	if (!mode)
-		mode = S_IFCHR | S_IRUGO | S_IWUGO;
-	mode &= (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);
-	} else {
-		up(&info_mutex);
-		snd_info_free_entry(entry);
-		return NULL;
-	}
-	p->gid = device_gid;
-	p->uid = device_uid;
-	p->data = (void *) entry;
-	entry->p = p;
-	up(&info_mutex);
-
-	if (strncmp(name, "controlC", 8) != 0)	/* created in sound.c */
-		devfs_mk_cdev(MKDEV(_major, minor), mode, "snd/%s", name);
-	return entry;
-}
-
-/*
- * release a procfs device file
- */
-void snd_info_free_device(snd_info_entry_t * entry)
-{
-	snd_runtime_check(entry, return);
-	down(&info_mutex);
-	snd_remove_proc_entry(snd_proc_dev, entry->p);
-	up(&info_mutex);
-	if (entry->p && strncmp(entry->name, "controlC", 8))
-		devfs_remove("snd/%s", entry->name);
-	snd_info_free_entry(entry);
-}
-
 /**
  * snd_info_register - register the info entry
  * @entry: the info entry
@@ -1119,16 +908,9 @@
 		up(&info_mutex);
 		return -ENOMEM;
 	}
-#ifndef LINUX_2_2
 	p->owner = entry->module;
-#endif
-	if (!S_ISDIR(entry->mode)) {
-#ifndef LINUX_2_2
+	if (!S_ISDIR(entry->mode))
 		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;
diff -urN linux-2.5.70-bk6/sound/core/init.c linux-2.5.70-bk7/sound/core/init.c
--- linux-2.5.70-bk6/sound/core/init.c	2003-05-26 18:00:25.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/init.c	2003-06-02 04:39:12.000000000 -0700
@@ -193,9 +193,7 @@
 		f_ops = &s_f_ops->f_ops;
 
 		memset(f_ops, 0, sizeof(*f_ops));
-#ifndef LINUX_2_2
 		f_ops->owner = file->f_op->owner;
-#endif
 		f_ops->release = file->f_op->release;
 		f_ops->poll = snd_disconnect_poll;
 
@@ -252,6 +250,10 @@
 	snd_cards_count--;
 	write_unlock(&snd_card_rwlock);
 
+#ifdef CONFIG_PM
+	wake_up(&card->power_sleep);
+#endif
+
 	/* wait, until all devices are ready for the free operation */
 	wait_event(card->shutdown_sleep, card->files == NULL);
 
@@ -662,21 +664,35 @@
 /**
  *  snd_power_wait - wait until the power-state is changed.
  *  @card: soundcard structure
+ *  @power_state: expected power state
+ *  @file: file structure for the O_NONBLOCK check (optional)
  *
  *  Waits until the power-state is changed.
  *
  *  Note: the power lock must be active before call.
  */
-void snd_power_wait(snd_card_t *card)
+int snd_power_wait(snd_card_t *card, unsigned int power_state, struct file *file)
 {
 	wait_queue_t wait;
 
+	/* fastpath */
+	if (snd_power_get_state(card) == power_state)
+		return 0;
 	init_waitqueue_entry(&wait, current);
 	add_wait_queue(&card->power_sleep, &wait);
-	snd_power_unlock(card);
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout(30 * HZ);
-	remove_wait_queue(&card->power_sleep, &wait);
-	snd_power_lock(card);
+	while (1) {
+		if (card->shutdown)
+			return -ENODEV;
+		if (snd_power_get_state(card) == power_state) {
+			remove_wait_queue(&card->power_sleep, &wait);
+			return 0;
+		}
+		if (file && (file->f_flags & O_NONBLOCK))
+			return -EAGAIN;
+		snd_power_unlock(card);
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(30 * HZ);
+		snd_power_lock(card);
+	}
 }
 #endif /* CONFIG_PM */
diff -urN linux-2.5.70-bk6/sound/core/ioctl32/pcm32.c linux-2.5.70-bk7/sound/core/ioctl32/pcm32.c
--- linux-2.5.70-bk6/sound/core/ioctl32/pcm32.c	2003-05-26 18:00:41.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/ioctl32/pcm32.c	2003-06-02 04:39:12.000000000 -0700
@@ -21,7 +21,6 @@
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/slab.h>
-#include <linux/compat.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include "ioctl32.h"
@@ -137,10 +136,15 @@
 	COPY(step);\
 }
 
+struct timeval32 {
+	s32 tv_sec;
+	s32 tv_usec;
+} __attribute__((packed));
+
 struct sndrv_pcm_status32 {
 	s32 state;
-	struct compat_timespec trigger_tstamp;
-	struct compat_timespec tstamp;
+	struct timeval32 trigger_tstamp;
+	struct timeval32 tstamp;
 	u32 appl_ptr;
 	u32 hw_ptr;
 	s32 delay;
@@ -155,9 +159,9 @@
 {\
 	COPY(state);\
 	COPY(trigger_tstamp.tv_sec);\
-	COPY(trigger_tstamp.tv_nsec);\
+	COPY(trigger_tstamp.tv_usec);\
 	COPY(tstamp.tv_sec);\
-	COPY(tstamp.tv_nsec);\
+	COPY(tstamp.tv_usec);\
 	COPY(appl_ptr);\
 	COPY(hw_ptr);\
 	COPY(delay);\
diff -urN linux-2.5.70-bk6/sound/core/ioctl32/rawmidi32.c linux-2.5.70-bk7/sound/core/ioctl32/rawmidi32.c
--- linux-2.5.70-bk6/sound/core/ioctl32/rawmidi32.c	2003-05-26 18:00:40.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/ioctl32/rawmidi32.c	2003-06-02 04:39:12.000000000 -0700
@@ -21,7 +21,6 @@
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/fs.h>
-#include <linux/compat.h>
 #include <sound/core.h>
 #include <sound/rawmidi.h>
 #include <asm/uaccess.h>
@@ -43,9 +42,14 @@
 	COPY(no_active_sensing);\
 }
 
+struct timeval32 {
+	s32 tv_sec;
+	s32 tv_usec;
+} __attribute__((packed));
+
 struct sndrv_rawmidi_status32 {
 	s32 stream;
-	struct compat_timespec tstamp;
+	struct timeval32 tstamp;
 	u32 avail;
 	u32 xruns;
 	unsigned char reserved[16];
@@ -55,7 +59,7 @@
 {\
 	COPY(stream);\
 	COPY(tstamp.tv_sec);\
-	COPY(tstamp.tv_nsec);\
+	COPY(tstamp.tv_usec);\
 	COPY(avail);\
 	COPY(xruns);\
 }
diff -urN linux-2.5.70-bk6/sound/core/ioctl32/timer32.c linux-2.5.70-bk7/sound/core/ioctl32/timer32.c
--- linux-2.5.70-bk6/sound/core/ioctl32/timer32.c	2003-05-26 18:00:40.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/ioctl32/timer32.c	2003-06-02 04:39:12.000000000 -0700
@@ -21,7 +21,6 @@
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/fs.h>
-#include <linux/compat.h>
 #include <sound/core.h>
 #include <sound/timer.h>
 #include <asm/uaccess.h>
@@ -32,7 +31,7 @@
 	s32 card;
 	unsigned char id[64];
 	unsigned char name[80];
-	u32 reserved0;
+	u32 ticks;
 	u32 resolution;
 	unsigned char reserved[64];
 };
@@ -43,11 +42,17 @@
 	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 compat_timespec tstamp;
+	struct timeval32 tstamp;
 	u32 resolution;
 	u32 lost;
 	u32 overrun;
@@ -58,7 +63,7 @@
 #define CVT_sndrv_timer_status()\
 {\
 	COPY(tstamp.tv_sec);\
-	COPY(tstamp.tv_nsec);\
+	COPY(tstamp.tv_usec);\
 	COPY(resolution);\
 	COPY(lost);\
 	COPY(overrun);\
diff -urN linux-2.5.70-bk6/sound/core/memalloc.c linux-2.5.70-bk7/sound/core/memalloc.c
--- linux-2.5.70-bk6/sound/core/memalloc.c	2003-05-26 18:01:00.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/memalloc.c	2003-06-02 04:39:12.000000000 -0700
@@ -80,7 +80,6 @@
 #define snd_assert(expr, args...) /**/
 #endif
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
 #ifdef CONFIG_PCI
 #if defined(__i386__) || defined(__ppc__) || defined(__x86_64__)
 #define HACK_PCI_ALLOC_CONSISTENT
@@ -98,7 +97,7 @@
  * again.
  */
 
-void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size,
+static void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size,
 				    dma_addr_t *dma_handle)
 {
 	void *ret;
@@ -133,7 +132,6 @@
 
 #endif /* arch */
 #endif /* CONFIG_PCI */
-#endif /* LINUX >= 2.4.0 */
 
 
 /*
@@ -261,14 +259,16 @@
 /*
  * search for the device
  */
-static struct snd_mem_list *mem_list_find(const struct snd_dma_device *dev, int allow_unused)
+static struct snd_mem_list *mem_list_find(const struct snd_dma_device *dev, int search_empty)
 {
 	struct list_head *p;
 	struct snd_mem_list *mem;
 
 	list_for_each(p, &mem_list_head) {
 		mem = list_entry(p, struct snd_mem_list, list);
-		if (compare_device(&mem->dev, dev, allow_unused))
+		if (mem->used && search_empty)
+			continue;
+		if (compare_device(&mem->dev, dev, search_empty))
 			return mem;
 	}
 	return NULL;
@@ -623,7 +623,7 @@
 }
 
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) && defined(__i386__)
+#if defined(__i386__)
 /*
  * on ix86, we allocate a page with GFP_KERNEL to assure the
  * allocation.  the code is almost same with kernel/i386/pci-dma.c but
diff -urN linux-2.5.70-bk6/sound/core/oss/mixer_oss.c linux-2.5.70-bk7/sound/core/oss/mixer_oss.c
--- linux-2.5.70-bk6/sound/core/oss/mixer_oss.c	2003-05-26 18:00:42.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/oss/mixer_oss.c	2003-06-02 04:39:12.000000000 -0700
@@ -376,9 +376,7 @@
 
 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,
diff -urN linux-2.5.70-bk6/sound/core/oss/pcm_oss.c linux-2.5.70-bk7/sound/core/oss/pcm_oss.c
--- linux-2.5.70-bk6/sound/core/oss/pcm_oss.c	2003-05-26 18:00:56.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/oss/pcm_oss.c	2003-06-02 04:39:12.000000000 -0700
@@ -1477,10 +1477,10 @@
 			continue;
 		runtime = substream->runtime;
 		
-		spin_lock_irq(&runtime->lock);
+		snd_pcm_stream_lock_irq(substream);
 		if (snd_pcm_running(substream))
 			snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
-		spin_unlock_irq(&runtime->lock);
+		snd_pcm_stream_unlock_irq(substream);
 		if (substream->open_flag) {
 			if (substream->ops->hw_free != NULL)
 				substream->ops->hw_free(substream);
@@ -1918,21 +1918,21 @@
 	if (psubstream != NULL) {
 		snd_pcm_runtime_t *runtime = psubstream->runtime;
 		poll_wait(file, &runtime->sleep, wait);
-		spin_lock_irq(&runtime->lock);
+		snd_pcm_stream_lock_irq(psubstream);
 		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);
+		snd_pcm_stream_unlock_irq(psubstream);
 	}
 	if (csubstream != NULL) {
 		snd_pcm_runtime_t *runtime = csubstream->runtime;
 		poll_wait(file, &runtime->sleep, wait);
-		spin_lock_irq(&runtime->lock);
+		snd_pcm_stream_lock_irq(csubstream);
 		if (runtime->status->state != SNDRV_PCM_STATE_RUNNING ||
 		    snd_pcm_oss_capture_ready(csubstream))
 			mask |= POLLIN | POLLRDNORM;
-		spin_unlock_irq(&runtime->lock);
+		snd_pcm_stream_unlock_irq(csubstream);
 	}
 
 	return mask;
@@ -1981,11 +1981,7 @@
 	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);
@@ -2148,9 +2144,7 @@
 
 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,
diff -urN linux-2.5.70-bk6/sound/core/pcm.c linux-2.5.70-bk7/sound/core/pcm.c
--- linux-2.5.70-bk6/sound/core/pcm.c	2003-05-26 18:01:02.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/pcm.c	2003-06-02 04:39:12.000000000 -0700
@@ -317,10 +317,10 @@
 		snd_iprintf(buffer, "closed\n");
 		return;
 	}
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
 		snd_iprintf(buffer, "no setup\n");
-		spin_unlock_irq(&runtime->lock);
+		snd_pcm_stream_unlock_irq(substream);
 		return;
 	}
 	snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access));
@@ -340,7 +340,7 @@
 		snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods);
 	}
 #endif
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
 }
 
 static void snd_pcm_substream_proc_sw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
@@ -351,10 +351,10 @@
 		snd_iprintf(buffer, "closed\n");
 		return;
 	}
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
 		snd_iprintf(buffer, "no setup\n");
-		spin_unlock_irq(&runtime->lock);
+		snd_pcm_stream_unlock_irq(substream);
 		return;
 	}
 	snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode));
@@ -367,7 +367,7 @@
 	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);
+	snd_pcm_stream_unlock_irq(substream);
 }
 
 static void snd_pcm_substream_proc_status_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
@@ -588,13 +588,15 @@
 			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->group = &substream->self_group;
+		spin_lock_init(&substream->self_group.lock);
+		INIT_LIST_HEAD(&substream->self_group.substreams);
+		list_add_tail(&substream->link_list, &substream->self_group.substreams);
 		spin_lock_init(&substream->timer_lock);
 		prev = substream;
 	}
@@ -793,7 +795,6 @@
 	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;
@@ -1022,6 +1023,7 @@
 EXPORT_SYMBOL(snd_pcm_format_name);
 EXPORT_SYMBOL(snd_pcm_subformat_name);
   /* pcm_native.c */
+EXPORT_SYMBOL(snd_pcm_link_rwlock);
 EXPORT_SYMBOL(snd_pcm_start);
 #ifdef CONFIG_PM
 EXPORT_SYMBOL(snd_pcm_suspend);
diff -urN linux-2.5.70-bk6/sound/core/pcm_lib.c linux-2.5.70-bk7/sound/core/pcm_lib.c
--- linux-2.5.70-bk6/sound/core/pcm_lib.c	2003-05-26 18:00:38.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/pcm_lib.c	2003-06-02 04:39:12.000000000 -0700
@@ -32,8 +32,8 @@
 
 /*
  * fill ring buffer with silence
- * runtime->silenced_start: starting pointer to silence area
- * runtime->silenced_size: size filled with silence
+ * runtime->silence_start: starting pointer to silence area
+ * runtime->silence_filled: size filled with silence
  * runtime->silence_threshold: threshold from application
  * runtime->silence_size: maximal size from application
  *
@@ -46,20 +46,20 @@
 
 	if (runtime->silence_size < runtime->boundary) {
 		snd_pcm_sframes_t noise_dist, n;
-		if (runtime->silenced_start != runtime->control->appl_ptr) {
-			n = runtime->control->appl_ptr - runtime->silenced_start;
+		if (runtime->silence_start != runtime->control->appl_ptr) {
+			n = runtime->control->appl_ptr - runtime->silence_start;
 			if (n < 0)
 				n += runtime->boundary;
-			if ((snd_pcm_uframes_t)n < runtime->silenced_size)
-				runtime->silenced_size -= n;
+			if ((snd_pcm_uframes_t)n < runtime->silence_filled)
+				runtime->silence_filled -= n;
 			else
-				runtime->silenced_size = 0;
-			runtime->silenced_start = runtime->control->appl_ptr;
+				runtime->silence_filled = 0;
+			runtime->silence_start = runtime->control->appl_ptr;
 		}
-		if (runtime->silenced_size == runtime->buffer_size)
+		if (runtime->silence_filled == runtime->buffer_size)
 			return;
-		snd_assert(runtime->silenced_size <= runtime->buffer_size, return);
-		noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silenced_size;
+		snd_assert(runtime->silence_filled <= runtime->buffer_size, return);
+		noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
 		if (noise_dist > (snd_pcm_sframes_t) runtime->silence_threshold)
 			return;
 		frames = runtime->silence_threshold - noise_dist;
@@ -67,30 +67,30 @@
 			frames = runtime->silence_size;
 	} else {
 		if (new_hw_ptr == ULONG_MAX) {	/* initialization */
-			runtime->silenced_size = 0;
-			runtime->silenced_start = runtime->control->appl_ptr;
+			runtime->silence_filled = 0;
+			runtime->silence_start = runtime->control->appl_ptr;
 		} else {
 			ofs = runtime->status->hw_ptr;
 			frames = new_hw_ptr - ofs;
 			if ((snd_pcm_sframes_t)frames < 0)
 				frames += runtime->boundary;
-			runtime->silenced_size -= frames;
-			if ((snd_pcm_sframes_t)runtime->silenced_size < 0) {
-				runtime->silenced_size = 0;
-				runtime->silenced_start = (ofs + frames) - runtime->buffer_size;
+			runtime->silence_filled -= frames;
+			if ((snd_pcm_sframes_t)runtime->silence_filled < 0) {
+				runtime->silence_filled = 0;
+				runtime->silence_start = (ofs + frames) - runtime->buffer_size;
 			} else {
-				runtime->silenced_start = ofs - runtime->silenced_size;
+				runtime->silence_start = ofs - runtime->silence_filled;
 			}
-			if ((snd_pcm_sframes_t)runtime->silenced_start < 0)
-				runtime->silenced_start += runtime->boundary;
+			if ((snd_pcm_sframes_t)runtime->silence_start < 0)
+				runtime->silence_start += runtime->boundary;
 		}
 		frames = runtime->buffer_size;
 	}
-	snd_assert(frames >= runtime->silenced_size, return);
-	frames -= runtime->silenced_size;
+	snd_assert(frames >= runtime->silence_filled, return);
+	frames -= runtime->silence_filled;
 	if (frames == 0)
 		return;
-	ofs = (runtime->silenced_start + runtime->silenced_size) % runtime->buffer_size;
+	ofs = (runtime->silence_start + runtime->silence_filled) % runtime->buffer_size;
 	while (frames > 0) {
 		transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames;
 		if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
@@ -120,13 +120,13 @@
 				}
 			}
 		}
-		runtime->silenced_size += transfer;
+		runtime->silence_filled += transfer;
 		frames -= transfer;
 		ofs = 0;
 	}
 }
 
-int snd_pcm_update_hw_ptr_interrupt(snd_pcm_substream_t *substream)
+static inline int snd_pcm_update_hw_ptr_interrupt(snd_pcm_substream_t *substream)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	snd_pcm_uframes_t pos;
@@ -1932,9 +1932,9 @@
 		if (runtime->silence_size >= runtime->boundary) {
 			frames = 1;
 		} else if (runtime->silence_size > 0 &&
-		    runtime->silenced_size < runtime->buffer_size) {
+		    runtime->silence_filled < runtime->buffer_size) {
 			snd_pcm_sframes_t noise_dist;
-			noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silenced_size;
+			noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
 			snd_assert(noise_dist <= (snd_pcm_sframes_t)runtime->silence_threshold, );
 			frames = noise_dist - runtime->silence_threshold;
 		}
@@ -1976,18 +1976,20 @@
 void snd_pcm_tick_elapsed(snd_pcm_substream_t *substream)
 {
 	snd_pcm_runtime_t *runtime;
+	unsigned long flags;
+	
 	snd_assert(substream != NULL, return);
 	runtime = substream->runtime;
 	snd_assert(runtime != NULL, return);
 
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irqsave(substream, flags);
 	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);
+	snd_pcm_stream_unlock_irqrestore(substream, flags);
 }
 
 /**
@@ -2004,6 +2006,8 @@
 void snd_pcm_period_elapsed(snd_pcm_substream_t *substream)
 {
 	snd_pcm_runtime_t *runtime;
+	unsigned long flags;
+
 	snd_assert(substream != NULL, return);
 	runtime = substream->runtime;
 	snd_assert(runtime != NULL, return);
@@ -2011,7 +2015,7 @@
 	if (runtime->transfer_ack_begin)
 		runtime->transfer_ack_begin(substream);
 
-	spin_lock(&runtime->lock);
+	snd_pcm_stream_lock_irqsave(substream, flags);
 	if (!snd_pcm_running(substream) ||
 	    snd_pcm_update_hw_ptr_interrupt(substream) < 0)
 		goto _end;
@@ -2021,7 +2025,7 @@
 	if (runtime->sleep_min)
 		snd_pcm_tick_prepare(substream);
  _end:
-	spin_unlock(&runtime->lock);
+	snd_pcm_stream_unlock_irqrestore(substream, flags);
 	if (runtime->transfer_ack_end)
 		runtime->transfer_ack_end(substream);
 	kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
@@ -2065,7 +2069,7 @@
 	if (size > runtime->xfer_align)
 		size -= size % runtime->xfer_align;
 
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_PREPARED:
 	case SNDRV_PCM_STATE_RUNNING:
@@ -2093,6 +2097,7 @@
 		   (size >= runtime->xfer_align && avail < runtime->xfer_align))) {
 			wait_queue_t wait;
 			enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state;
+			long tout;
 
 			if (nonblock) {
 				err = -EAGAIN;
@@ -2107,16 +2112,16 @@
 					state = SIGNALED;
 					break;
 				}
-				spin_unlock_irq(&runtime->lock);
-				if (schedule_timeout(10 * HZ) == 0) {
-					spin_lock_irq(&runtime->lock);
+				snd_pcm_stream_unlock_irq(substream);
+				tout = schedule_timeout(10 * HZ);
+				snd_pcm_stream_lock_irq(substream);
+				if (tout == 0) {
 					if (runtime->status->state != SNDRV_PCM_STATE_PREPARED &&
 					    runtime->status->state != SNDRV_PCM_STATE_PAUSED) {
 						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:
 				case SNDRV_PCM_STATE_DRAINING:
@@ -2162,15 +2167,13 @@
 		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);
+		snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL);
 		appl_ptr = runtime->control->appl_ptr;
 		appl_ofs = appl_ptr % runtime->buffer_size;
-		spin_unlock_irq(&runtime->lock);
+		snd_pcm_stream_unlock_irq(substream);
 		if ((err = transfer(substream, appl_ofs, (void *)data, offset, frames)) < 0)
 			goto _end;
-		spin_lock_irq(&runtime->lock);
+		snd_pcm_stream_lock_irq(substream);
 		switch (runtime->status->state) {
 		case SNDRV_PCM_STATE_XRUN:
 			err = -EPIPE;
@@ -2204,7 +2207,7 @@
 			snd_pcm_tick_prepare(substream);
 	}
  _end_unlock:
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
  _end:
 	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
 }
@@ -2348,7 +2351,7 @@
 	if (size > runtime->xfer_align)
 		size -= size % runtime->xfer_align;
 
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_PREPARED:
 		if (size >= runtime->start_threshold) {
@@ -2389,6 +2392,7 @@
 			   (size >= runtime->xfer_align && avail < runtime->xfer_align)) {
 			wait_queue_t wait;
 			enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state;
+			long tout;
 
 			if (nonblock) {
 				err = -EAGAIN;
@@ -2403,16 +2407,16 @@
 					state = SIGNALED;
 					break;
 				}
-				spin_unlock_irq(&runtime->lock);
-				if (schedule_timeout(10 * HZ) == 0) {
-					spin_lock_irq(&runtime->lock);
+				snd_pcm_stream_unlock_irq(substream);
+				tout = schedule_timeout(10 * HZ);
+				snd_pcm_stream_lock_irq(substream);
+				if (tout == 0) {
 					if (runtime->status->state != SNDRV_PCM_STATE_PREPARED &&
 					    runtime->status->state != SNDRV_PCM_STATE_PAUSED) {
 						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;
@@ -2459,15 +2463,13 @@
 		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);
+		snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL);
 		appl_ptr = runtime->control->appl_ptr;
 		appl_ofs = appl_ptr % runtime->buffer_size;
-		spin_unlock_irq(&runtime->lock);
+		snd_pcm_stream_unlock_irq(substream);
 		if ((err = transfer(substream, appl_ofs, (void *)data, offset, frames)) < 0)
 			goto _end;
-		spin_lock_irq(&runtime->lock);
+		snd_pcm_stream_lock_irq(substream);
 		switch (runtime->status->state) {
 		case SNDRV_PCM_STATE_XRUN:
 			err = -EPIPE;
@@ -2495,7 +2497,7 @@
 			snd_pcm_tick_prepare(substream);
 	}
  _end_unlock:
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
  _end:
 	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
 }
diff -urN linux-2.5.70-bk6/sound/core/pcm_native.c linux-2.5.70-bk7/sound/core/pcm_native.c
--- linux-2.5.70-bk6/sound/core/pcm_native.c	2003-05-26 18:00:37.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/pcm_native.c	2003-06-02 04:39:12.000000000 -0700
@@ -64,7 +64,8 @@
  *
  */
 
-static rwlock_t pcm_link_lock = RW_LOCK_UNLOCKED;
+rwlock_t snd_pcm_link_rwlock = RW_LOCK_UNLOCKED;
+
 
 static inline mm_segment_t snd_enter_user(void)
 {
@@ -463,7 +464,7 @@
 	if ((params->silence_threshold != 0 || params->silence_size < runtime->boundary) &&
 	    (params->silence_threshold + params->silence_size > runtime->buffer_size))
 		return -EINVAL;
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 	runtime->tstamp_mode = params->tstamp_mode;
 	runtime->sleep_min = params->sleep_min;
 	runtime->period_step = params->period_step;
@@ -484,7 +485,7 @@
 			snd_pcm_playback_silence(substream, ULONG_MAX);
 		wake_up(&runtime->sleep);
 	}
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
 	return 0;
 }
 
@@ -505,7 +506,7 @@
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
 
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 	status->state = runtime->status->state;
 	status->suspended_state = runtime->status->suspended_state;
 	if (status->state == SNDRV_PCM_STATE_OPEN)
@@ -540,7 +541,7 @@
 	runtime->avail_max = 0;
 	runtime->overrange = 0;
  _end:
-	spin_unlock_irq(&runtime->lock);	
+ 	snd_pcm_stream_unlock_irq(substream);
 	return 0;
 }
 
@@ -601,56 +602,128 @@
 	runtime->trigger_master = NULL;
 }
 
-#define _SND_PCM_ACTION(aname, substream, state, res, check_master) { \
-	snd_pcm_substream_t *s; \
-	res = 0; \
-	read_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: \
-	read_unlock(&pcm_link_lock); \
-}
-
-#define SND_PCM_ACTION(aname, substream, state) { \
-	int res; \
-	_SND_PCM_ACTION(aname, substream, state, res, 1); \
-	return res; \
+struct action_ops {
+	int (*pre_action)(snd_pcm_substream_t *substream, int state);
+	int (*do_action)(snd_pcm_substream_t *substream, int state);
+	void (*post_action)(snd_pcm_substream_t *substream, int state);
+};
+
+/*
+ *  this functions is core for handling of linked stream
+ *  Note: the stream state might be changed also on failure
+ *  Note2: call with calling stream lock + link lock
+ */
+static int snd_pcm_action_group(struct action_ops *ops,
+				snd_pcm_substream_t *substream,
+				int state)
+{
+	struct list_head *pos;
+	snd_pcm_substream_t *s = NULL;
+	int err, res = 0;
+
+	snd_pcm_group_for_each(pos, substream) {
+		s = snd_pcm_group_substream_entry(pos);
+		if (s != substream)
+			spin_lock(&s->self_group.lock);
+		res = ops->pre_action(s, state);
+		if (res < 0)
+			break;
+	}
+	if (res >= 0) {
+		snd_pcm_group_for_each(pos, substream) {
+			s = snd_pcm_group_substream_entry(pos);
+			err = ops->do_action(s, state);
+			if (err < 0) {
+				if (res == 0)
+					res = err;
+			} else {
+				ops->post_action(s, state);
+			}
+			if (s != substream)
+				spin_unlock(&s->self_group.lock);
+		}
+	} else {
+		snd_pcm_substream_t *s1;
+		/* unlock all streams */
+		snd_pcm_group_for_each(pos, substream) {
+			s1 = snd_pcm_group_substream_entry(pos);
+			if (s1 != substream)
+				spin_unlock(&s1->self_group.lock);
+			if (s1 == s)	/* end */
+				break;
+		}
+	}
+	return res;
+}
+
+/*
+ *  Note: call with stream lock
+ */
+static int snd_pcm_action_single(struct action_ops *ops,
+				 snd_pcm_substream_t *substream,
+				 int state)
+{
+	int res;
+	
+	res = ops->pre_action(substream, state);
+	if (res < 0)
+		return res;
+	res = ops->do_action(substream, state);
+	if (res == 0) {
+		ops->post_action(substream, state);
+	}
+	return res;
+}
+
+/*
+ *  Note: call with stream lock
+ */
+static int snd_pcm_action(struct action_ops *ops,
+			  snd_pcm_substream_t *substream,
+			  int state)
+{
+	int res;
+
+	if (snd_pcm_stream_linked(substream)) {
+		if (!spin_trylock(&substream->group->lock)) {
+			spin_unlock(&substream->self_group.lock);
+			spin_lock(&substream->group->lock);
+			spin_lock(&substream->self_group.lock);
+		}
+		res = snd_pcm_action_group(ops, substream, state);
+		spin_unlock(&substream->group->lock);
+	} else {
+		res = snd_pcm_action_single(ops, substream, state);
+	}
+	return res;
+}
+
+/*
+ *  Note: don't use any locks before
+ */
+static int snd_pcm_action_lock_irq(struct action_ops *ops,
+				   snd_pcm_substream_t *substream,
+				   int state)
+{
+	int res;
+
+	read_lock_irq(&snd_pcm_link_rwlock);
+	if (snd_pcm_stream_linked(substream)) {
+		spin_lock(&substream->group->lock);
+		spin_lock(&substream->self_group.lock);
+		res = snd_pcm_action_group(ops, substream, state);
+		spin_unlock(&substream->self_group.lock);
+		spin_unlock(&substream->group->lock);
+	} else {
+		spin_lock(&substream->self_group.lock);
+		res = snd_pcm_action_single(ops, substream, state);
+		spin_unlock(&substream->self_group.lock);
+	}
+	read_unlock_irq(&snd_pcm_link_rwlock);
+	return res;
 }
 
-static inline int snd_pcm_pre_start(snd_pcm_substream_t *substream, int state)
+static 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)
@@ -662,12 +735,14 @@
 	return 0;
 }
 
-static inline int snd_pcm_do_start(snd_pcm_substream_t *substream, int state)
+static int snd_pcm_do_start(snd_pcm_substream_t *substream, int state)
 {
+	if (substream->runtime->trigger_master != substream)
+		return 0;
         return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
 }
 
-static inline void snd_pcm_post_start(snd_pcm_substream_t *substream, int state)
+static void snd_pcm_post_start(snd_pcm_substream_t *substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	snd_pcm_trigger_tstamp(substream);
@@ -681,15 +756,21 @@
 		snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART, &runtime->trigger_tstamp);
 }
 
+static struct action_ops snd_pcm_action_start = {
+	.pre_action = snd_pcm_pre_start,
+	.do_action = snd_pcm_do_start,
+	.post_action = snd_pcm_post_start
+};
+
 /**
- * snd_pcm_sart
+ * snd_pcm_start
  */
 int snd_pcm_start(snd_pcm_substream_t *substream)
 {
-	SND_PCM_ACTION(start, substream, 0);
+	return snd_pcm_action(&snd_pcm_action_start, substream, 0);
 }
 
-static inline int snd_pcm_pre_stop(snd_pcm_substream_t *substream, int state)
+static int snd_pcm_pre_stop(snd_pcm_substream_t *substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	if (substream->runtime->status->state != SNDRV_PCM_STATE_RUNNING &&
@@ -699,12 +780,14 @@
 	return 0;
 }
 
-static inline int snd_pcm_do_stop(snd_pcm_substream_t *substream, int state)
+static int snd_pcm_do_stop(snd_pcm_substream_t *substream, int state)
 {
+	if (substream->runtime->trigger_master != substream)
+		return 0;
 	return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
 }
 
-static inline void snd_pcm_post_stop(snd_pcm_substream_t *substream, int state)
+static void snd_pcm_post_stop(snd_pcm_substream_t *substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	snd_pcm_trigger_tstamp(substream);
@@ -715,15 +798,21 @@
 	wake_up(&runtime->sleep);
 }
 
+static struct action_ops snd_pcm_action_stop = {
+	.pre_action = snd_pcm_pre_stop,
+	.do_action = snd_pcm_do_stop,
+	.post_action = snd_pcm_post_stop
+};
+
 /**
  * snd_pcm_stop
  */
 int snd_pcm_stop(snd_pcm_substream_t *substream, int state)
 {
-	SND_PCM_ACTION(stop, substream, state);
+	return snd_pcm_action(&snd_pcm_action_stop, substream, state);
 }
 
-static inline int snd_pcm_pre_pause(snd_pcm_substream_t *substream, int push)
+static 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))
@@ -737,14 +826,16 @@
 	return 0;
 }
 
-static inline int snd_pcm_do_pause(snd_pcm_substream_t *substream, int push)
+static int snd_pcm_do_pause(snd_pcm_substream_t *substream, int push)
 {
+	if (substream->runtime->trigger_master != substream)
+		return 0;
 	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)
+static void snd_pcm_post_pause(snd_pcm_substream_t *substream, int push)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	snd_pcm_trigger_tstamp(substream);
@@ -763,15 +854,21 @@
 	}
 }
 
+static struct action_ops snd_pcm_action_pause = {
+	.pre_action = snd_pcm_pre_pause,
+	.do_action = snd_pcm_do_pause,
+	.post_action = snd_pcm_post_pause
+};
+
 static int snd_pcm_pause(snd_pcm_substream_t *substream, int push)
 {
-	SND_PCM_ACTION(pause, substream, push);
+	return snd_pcm_action(&snd_pcm_action_pause, substream, 0);
 }
 
 #ifdef CONFIG_PM
 /* suspend */
 
-static inline int snd_pcm_pre_suspend(snd_pcm_substream_t *substream, int state)
+static 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)
@@ -781,15 +878,17 @@
 	return 0;
 }
 
-static inline int snd_pcm_do_suspend(snd_pcm_substream_t *substream, int state)
+static int snd_pcm_do_suspend(snd_pcm_substream_t *substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (runtime->trigger_master != substream)
+		return 0;
 	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)
+static void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	snd_pcm_trigger_tstamp(substream);
@@ -800,12 +899,18 @@
 	wake_up(&runtime->sleep);
 }
 
+static struct action_ops snd_pcm_action_suspend = {
+	.pre_action = snd_pcm_pre_suspend,
+	.do_action = snd_pcm_do_suspend,
+	.post_action = snd_pcm_post_suspend
+};
+
 /**
  * snd_pcm_suspend
  */
 int snd_pcm_suspend(snd_pcm_substream_t *substream)
 {
-	SND_PCM_ACTION(suspend, substream, 0);
+	return snd_pcm_action(&snd_pcm_action_suspend, substream, 0);
 }
 
 /**
@@ -814,24 +919,23 @@
 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)
+			if (substream->runtime == NULL)
 				continue;
-			spin_lock(&runtime->lock);
-			if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
-				spin_unlock(&runtime->lock);
+			snd_pcm_stream_lock(substream);
+			if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+				snd_pcm_stream_unlock(substream);
 				continue;
 			}
 			if ((err = snd_pcm_suspend(substream)) < 0) {
-				spin_unlock(&runtime->lock);
+				snd_pcm_stream_unlock(substream);
 				return err;
 			}
-			spin_unlock(&runtime->lock);
+			snd_pcm_stream_unlock(substream);
 		}
 	}
 	return 0;
@@ -839,7 +943,7 @@
 
 /* resume */
 
-static inline int snd_pcm_pre_resume(snd_pcm_substream_t *substream, int state)
+static 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))
@@ -848,15 +952,17 @@
 	return 0;
 }
 
-static inline int snd_pcm_do_resume(snd_pcm_substream_t *substream, int state)
+static int snd_pcm_do_resume(snd_pcm_substream_t *substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (runtime->trigger_master != substream)
+		return 0;
 	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)
+static void snd_pcm_post_resume(snd_pcm_substream_t *substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	snd_pcm_trigger_tstamp(substream);
@@ -867,23 +973,20 @@
 		snd_pcm_tick_prepare(substream);
 }
 
+static struct action_ops snd_pcm_action_resume = {
+	.pre_action = snd_pcm_pre_resume,
+	.do_action = snd_pcm_do_resume,
+	.post_action = snd_pcm_post_resume
+};
+
 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:
+	if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0)
+		return snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0);
 	snd_power_unlock(card);
 	return res;
 }
@@ -904,7 +1007,7 @@
 	int result;
 
 	snd_power_lock(card);
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
        _xrun_recovery:
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_XRUN:
@@ -914,26 +1017,21 @@
 		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;
+		snd_pcm_stream_unlock_irq(substream);
+		result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
+		snd_pcm_stream_lock_irq(substream);
+		if (result >= 0)
+			goto _xrun_recovery;
+		break;
 	default:
 		result = -EBADFD;
 	}
-       _end:
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
 	snd_power_unlock(card);
 	return result;
 }
 
-static inline int snd_pcm_pre_reset(snd_pcm_substream_t * substream, int state)
+static int snd_pcm_pre_reset(snd_pcm_substream_t * substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	switch (runtime->status->state) {
@@ -947,7 +1045,7 @@
 	}
 }
 
-static inline int snd_pcm_do_reset(snd_pcm_substream_t * substream, int state)
+static 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);
@@ -956,12 +1054,12 @@
 	// 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;
-	runtime->silenced_start = runtime->status->hw_ptr;
-	runtime->silenced_size = 0;
+	runtime->silence_start = runtime->status->hw_ptr;
+	runtime->silence_filled = 0;
 	return 0;
 }
 
-static inline void snd_pcm_post_reset(snd_pcm_substream_t * substream, int state)
+static 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;
@@ -970,17 +1068,18 @@
 		snd_pcm_playback_silence(substream, ULONG_MAX);
 }
 
+static struct action_ops snd_pcm_action_reset = {
+	.pre_action = snd_pcm_pre_reset,
+	.do_action = snd_pcm_do_reset,
+	.post_action = snd_pcm_post_reset
+};
+
 static int snd_pcm_reset(snd_pcm_substream_t *substream)
 {
-	int res;
-
-	spin_lock_irq(&substream->runtime->lock);
-	_SND_PCM_ACTION(reset, substream, 0, res, 0);
-	spin_unlock_irq(&substream->runtime->lock);
-	return res;
+	return snd_pcm_action_lock_irq(&snd_pcm_action_reset, substream, 0);
 }
 
-static inline int snd_pcm_pre_prepare(snd_pcm_substream_t * substream, int state)
+static int snd_pcm_pre_prepare(snd_pcm_substream_t * substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	switch (runtime->status->state) {
@@ -993,7 +1092,7 @@
 	}
 }
 
-static inline int snd_pcm_do_prepare(snd_pcm_substream_t * substream, int state)
+static int snd_pcm_do_prepare(snd_pcm_substream_t * substream, int state)
 {
 	int err;
 	err = substream->ops->prepare(substream);
@@ -1002,13 +1101,19 @@
 	return snd_pcm_do_reset(substream, 0);
 }
 
-static inline void snd_pcm_post_prepare(snd_pcm_substream_t * substream, int state)
+static 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;
 }
 
+static struct action_ops snd_pcm_action_prepare = {
+	.pre_action = snd_pcm_pre_prepare,
+	.do_action = snd_pcm_do_prepare,
+	.post_action = snd_pcm_post_prepare
+};
+
 /**
  * snd_pcm_prepare
  */
@@ -1018,41 +1123,35 @@
 	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);
-	}
-
-	spin_lock_irq(&substream->runtime->lock);
-	_SND_PCM_ACTION(prepare, substream, 0, res, 0);
-	spin_unlock_irq(&substream->runtime->lock);
-
-       _power_unlock:
+	if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0)
+		res = snd_pcm_action_lock_irq(&snd_pcm_action_prepare, substream, 0);
 	snd_power_unlock(card);
 	return res;
 }
 
 static void snd_pcm_change_state(snd_pcm_substream_t *substream, int state)
 {
+	struct list_head *pos;
 	snd_pcm_substream_t *s;
-	read_lock(&pcm_link_lock);
-	s = substream->link_next;
-	while (s != substream) {
-		spin_lock(&s->runtime->lock);
-		s = s->link_next;
+
+	if (snd_pcm_stream_linked(substream)) {
+		if (!spin_trylock(&substream->group->lock)) {
+			spin_unlock(&substream->self_group.lock);
+			spin_lock(&substream->group->lock);
+			spin_lock(&substream->self_group.lock);
+		}
+		snd_pcm_group_for_each(pos, substream) {
+			s = snd_pcm_group_substream_entry(pos);
+			if (s != substream)
+				spin_lock(&s->self_group.lock);
+			s->runtime->status->state = state;
+			if (s != substream)
+				spin_unlock(&s->self_group.lock);
+		}
+		spin_unlock(&substream->group->lock);
+	} else {
+		substream->runtime->status->state = state;
 	}
-	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);
-	read_unlock(&pcm_link_lock);
 }
 
 static int snd_pcm_playback_drop(snd_pcm_substream_t *substream);
@@ -1072,7 +1171,7 @@
 	card = substream->pcm->card;
 
 	snd_power_lock(card);
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 
 	/* stop_threshold fixup to avoid endless loop when */
 	/* stop_threshold > buffer_size */
@@ -1088,16 +1187,12 @@
 	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;
+		snd_pcm_stream_unlock_irq(substream);
+		result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
+		snd_pcm_stream_lock_irq(substream);
+		if (result >= 0)
+			snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);
+		goto _end;
 	case SNDRV_PCM_STATE_OPEN:
 		result = -EBADFD;
 		goto _end;
@@ -1112,7 +1207,6 @@
 		}
 		/* 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:
@@ -1137,18 +1231,19 @@
 	init_waitqueue_entry(&wait, current);
 	add_wait_queue(&runtime->sleep, &wait);
 	while (1) {
+		long tout;
 		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);
+		snd_pcm_stream_unlock_irq(substream);
+		tout = schedule_timeout(10 * HZ);
+		snd_pcm_stream_lock_irq(substream);
+		if (tout == 0) {
 			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;
@@ -1174,7 +1269,7 @@
 
       _end:
 	runtime->stop_threshold = stop_threshold;
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
 	snd_power_unlock(card);
 	if (state == EXPIRED)
 		snd_pcm_playback_drop(substream);
@@ -1187,8 +1282,9 @@
 	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);
+	snd_pcm_stream_lock_irq(substream);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_OPEN:
 		res = -EBADFD;
@@ -1207,26 +1303,20 @@
 		/* 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;
+		snd_pcm_stream_unlock_irq(substream);
+		res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
+		snd_pcm_stream_lock_irq(substream);
+		if (res >= 0)
+			snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);
+		break;
 	default:
 		break; 
 	}
 	runtime->control->appl_ptr = runtime->status->hw_ptr;
-       _end:
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
 	snd_power_unlock(card);
 	return res;
 }
@@ -1236,8 +1326,9 @@
 	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);
+	snd_pcm_stream_lock_irq(substream);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_OPEN:
 		res = -EBADFD;
@@ -1266,21 +1357,16 @@
 				     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;
+		snd_pcm_stream_unlock_irq(substream);
+		res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
+		snd_pcm_stream_lock_irq(substream);
+		if (res >= 0)
+			goto _xrun_recovery;
+		break;
 	default: 
 		break; 
 	}
-       _end:
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
 	snd_power_unlock(card);
 	return res;
 }
@@ -1290,8 +1376,9 @@
 	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);
+	snd_pcm_stream_lock_irq(substream);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_OPEN:
 		res = -EBADFD;
@@ -1305,15 +1392,11 @@
 		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);
-		}
+		snd_pcm_stream_unlock_irq(substream);
+		res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
+		snd_pcm_stream_lock_irq(substream);
+		if (res < 0)
+			goto _end;
 		/* Fall through */
 	case SNDRV_PCM_STATE_PREPARED:
 	case SNDRV_PCM_STATE_DRAINING:
@@ -1324,13 +1407,14 @@
 		break; 
 	}
 	runtime->control->appl_ptr = runtime->status->hw_ptr;
-       _end: 
-	spin_unlock_irq(&runtime->lock);
+       _end:
+	snd_pcm_stream_unlock_irq(substream);
 	snd_power_unlock(card);
 	return res;
 }
 
 /* WARNING: Don't forget to fput back the file */
+extern int snd_major;
 static struct file *snd_pcm_file_fd(int fd)
 {
 	struct file *file;
@@ -1341,7 +1425,7 @@
 		return 0;
 	inode = file->f_dentry->d_inode;
 	if (!S_ISCHR(inode->i_mode) ||
-	    major(inode->i_rdev) != CONFIG_SND_MAJOR) {
+	    major(inode->i_rdev) != snd_major) {
 		fput(file);
 		return 0;
 	}
@@ -1359,7 +1443,8 @@
 	int res = 0;
 	struct file *file;
 	snd_pcm_file_t *pcm_file;
-	snd_pcm_substream_t *s, *substream1;
+	snd_pcm_substream_t *substream1;
+
 	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
 		return -EBADFD;
 	file = snd_pcm_file_fd(fd);
@@ -1367,40 +1452,67 @@
 		return -EBADFD;
 	pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO);
 	substream1 = pcm_file->substream;
-	write_lock_irq(&pcm_link_lock);
+	write_lock_irq(&snd_pcm_link_rwlock);
 	if (substream->runtime->status->state != substream1->runtime->status->state) {
 		res = -EBADFD;
 		goto _end;
 	}
-	s = substream;
-	do {
-		if (s == substream1) {
-			res = -EALREADY;
+	if (snd_pcm_stream_linked(substream1)) {
+		res = -EALREADY;
+		goto _end;
+	}
+	if (!snd_pcm_stream_linked(substream)) {
+		substream->group = kmalloc(sizeof(snd_pcm_group_t), GFP_ATOMIC);
+		if (substream->group == NULL) {
+			res = -ENOMEM;
 			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;
+		spin_lock_init(&substream->group->lock);
+		INIT_LIST_HEAD(&substream->group->substreams);
+		list_add_tail(&substream->link_list, &substream->group->substreams);
+	}
+	list_add_tail(&substream1->link_list, &substream->group->substreams);
+	substream1->group = substream->group;
  _end:
-	write_unlock_irq(&pcm_link_lock);
+	write_unlock_irq(&snd_pcm_link_rwlock);
 	fput(file);
 	return res;
 }
 
-static int snd_pcm_unlink(snd_pcm_substream_t *substream)
+static void relink_to_local(snd_pcm_substream_t *substream)
 {
-	write_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;
-	write_unlock_irq(&pcm_link_lock);
-	return 0;
+	substream->group = &substream->self_group;
+	INIT_LIST_HEAD(&substream->self_group.substreams);
+	list_add_tail(&substream->link_list, &substream->self_group.substreams);
 }
 
+static int snd_pcm_unlink(snd_pcm_substream_t *substream)
+{
+	struct list_head *pos;
+	int res = 0, count = 0;
+
+	write_lock_irq(&snd_pcm_link_rwlock);
+	if (!snd_pcm_stream_linked(substream)) {
+		res = -EINVAL;
+		goto _end;
+	}
+	list_del(&substream->link_list);
+	snd_pcm_group_for_each(pos, substream) {
+		if (++count > 1)
+			break;
+	}
+	if (count == 1) {	/* detach the last stream, too */
+		snd_pcm_group_for_each(pos, substream) {
+			relink_to_local(snd_pcm_group_substream_entry(pos));
+			break;
+		}
+		kfree(substream->group);
+	}
+	relink_to_local(substream);
+       _end:
+	write_unlock_irq(&snd_pcm_link_rwlock);
+	return res;
+}
 
 static int snd_pcm_hw_rule_mul(snd_pcm_hw_params_t *params,
 			       snd_pcm_hw_rule_t *rule)
@@ -1453,7 +1565,8 @@
 		if (! snd_mask_test(mask, k))
 			continue;
 		bits = snd_pcm_format_physical_width(k);
-		snd_assert(bits > 0, continue);
+		if (bits <= 0)
+			continue; /* ignore invalid formats */
 		if ((unsigned)bits < i->min || (unsigned)bits > i->max)
 			snd_mask_reset(&m, k);
 	}
@@ -1474,7 +1587,8 @@
 		if (! snd_mask_test(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k))
 			continue;
 		bits = snd_pcm_format_physical_width(k);
-		snd_assert(bits > 0, continue);
+		if (bits <= 0)
+			continue; /* ignore invalid formats */
 		if (t.min > (unsigned)bits)
 			t.min = bits;
 		if (t.max < (unsigned)bits)
@@ -1912,7 +2026,7 @@
 	if (frames == 0)
 		return 0;
 
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_PREPARED:
 		break;
@@ -1947,7 +2061,7 @@
 		snd_pcm_tick_prepare(substream);
 	ret = frames;
  __end:
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
 	return ret;
 }
 
@@ -1961,7 +2075,7 @@
 	if (frames == 0)
 		return 0;
 
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_PREPARED:
 	case SNDRV_PCM_STATE_DRAINING:
@@ -1996,7 +2110,7 @@
 		snd_pcm_tick_prepare(substream);
 	ret = frames;
  __end:
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
 	return ret;
 }
 
@@ -2010,7 +2124,7 @@
 	if (frames == 0)
 		return 0;
 
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_PREPARED:
 	case SNDRV_PCM_STATE_PAUSED:
@@ -2046,7 +2160,7 @@
 		snd_pcm_tick_prepare(substream);
 	ret = frames;
  __end:
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
 	return ret;
 }
 
@@ -2060,7 +2174,7 @@
 	if (frames == 0)
 		return 0;
 
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_PREPARED:
 	case SNDRV_PCM_STATE_DRAINING:
@@ -2096,7 +2210,7 @@
 		snd_pcm_tick_prepare(substream);
 	ret = frames;
  __end:
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
 	return ret;
 }
 
@@ -2105,7 +2219,7 @@
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	int err;
 
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_DRAINING:
 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
@@ -2126,7 +2240,7 @@
 		err = -EBADFD;
 		break;
 	}
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
 	return err;
 }
 		
@@ -2136,7 +2250,7 @@
 	int err;
 	snd_pcm_sframes_t n = 0;
 
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_DRAINING:
 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
@@ -2161,7 +2275,7 @@
 		err = -EBADFD;
 		break;
 	}
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
 	if (!err)
 		if (put_user(n, res))
 			err = -EFAULT;
@@ -2208,13 +2322,7 @@
 	case SNDRV_PCM_IOCTL_RESET:
 		return snd_pcm_reset(substream);
 	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;
-	}
+		return snd_pcm_action(&snd_pcm_action_start, substream, 0);
 	case SNDRV_PCM_IOCTL_LINK:
 		return snd_pcm_link(substream, (long) arg);
 	case SNDRV_PCM_IOCTL_UNLINK:
@@ -2310,9 +2418,9 @@
 	case SNDRV_PCM_IOCTL_PAUSE:
 	{
 		int res;
-		spin_lock_irq(&substream->runtime->lock);
+		snd_pcm_stream_lock_irq(substream);
 		res = snd_pcm_pause(substream, (long) arg);
-		spin_unlock_irq(&substream->runtime->lock);
+		snd_pcm_stream_unlock_irq(substream);
 		return res;
 	}
 	case SNDRV_PCM_IOCTL_DRAIN:
@@ -2494,9 +2602,6 @@
 	snd_pcm_runtime_t *runtime;
 	snd_pcm_sframes_t result;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0)
-	up(&file->f_dentry->d_inode->i_sem);
-#endif
 	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);
@@ -2514,13 +2619,9 @@
 	if (result > 0)
 		result = frames_to_bytes(runtime, result);
  end:
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0)
-	down(&file->f_dentry->d_inode->i_sem);
-#endif
 	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)
 
@@ -2567,9 +2668,6 @@
 	void **bufs;
 	snd_pcm_uframes_t frames;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0)
-	up(&file->f_dentry->d_inode->i_sem);
-#endif
 	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);
@@ -2594,12 +2692,8 @@
 		result = frames_to_bytes(runtime, result);
 	kfree(bufs);
  end:
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0)
-	down(&file->f_dentry->d_inode->i_sem);
-#endif
 	return result;
 }
-#endif
 
 unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait)
 {
@@ -2617,7 +2711,7 @@
 
 	poll_wait(file, &runtime->sleep, wait);
 
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 	avail = snd_pcm_playback_avail(runtime);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_RUNNING:
@@ -2635,7 +2729,7 @@
 		mask = POLLOUT | POLLWRNORM | POLLERR;
 		break;
 	}
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
 	return mask;
 }
 
@@ -2655,7 +2749,7 @@
 
 	poll_wait(file, &runtime->sleep, wait);
 
-	spin_lock_irq(&runtime->lock);
+	snd_pcm_stream_lock_irq(substream);
 	avail = snd_pcm_capture_avail(runtime);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_RUNNING:
@@ -2677,26 +2771,11 @@
 		mask = POLLIN | POLLRDNORM | POLLERR;
 		break;
 	}
-	spin_unlock_irq(&runtime->lock);
+	snd_pcm_stream_unlock_irq(substream);
 	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;
@@ -2707,19 +2786,12 @@
 	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,
@@ -2735,22 +2807,12 @@
 	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;
@@ -2761,19 +2823,12 @@
 	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,
@@ -2789,14 +2844,8 @@
 	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;
 }
 
@@ -2812,11 +2861,7 @@
 	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;
@@ -2828,11 +2873,7 @@
 	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);
@@ -2847,11 +2888,7 @@
 		page = virt_to_page(vaddr);
 	}
 	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 =
@@ -2859,9 +2896,6 @@
 	.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,
@@ -2889,11 +2923,7 @@
 	    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_t)size > dma_bytes)
 		return -EINVAL;
@@ -2901,14 +2931,8 @@
 		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;
 }
@@ -2923,11 +2947,7 @@
 	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);
@@ -3035,13 +3055,9 @@
  */
 
 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,
@@ -3051,13 +3067,9 @@
 };
 
 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,
diff -urN linux-2.5.70-bk6/sound/core/rawmidi.c linux-2.5.70-bk7/sound/core/rawmidi.c
--- linux-2.5.70-bk6/sound/core/rawmidi.c	2003-05-26 18:00:24.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/rawmidi.c	2003-06-02 04:39:12.000000000 -0700
@@ -1316,9 +1316,7 @@
 
 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,
diff -urN linux-2.5.70-bk6/sound/core/seq/Makefile linux-2.5.70-bk7/sound/core/seq/Makefile
--- linux-2.5.70-bk6/sound/core/seq/Makefile	2003-05-26 18:00:57.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/seq/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -65,6 +65,7 @@
 obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) snd-seq-virmidi.o
 obj-$(call sequencer,$(CONFIG_SND_ES968)) += $(RAWMIDI_OBJS)
 obj-$(call sequencer,$(CONFIG_SND_WAVEFRONT)) += $(RAWMIDI_OBJS) $(OPL3_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_SSCAPE)) += $(RAWMIDI_OBJS)
 obj-$(call sequencer,$(CONFIG_SND_ALS4000)) += $(RAWMIDI_OBJS) $(OPL3_OBJS)
 obj-$(call sequencer,$(CONFIG_SND_CMIPCI)) += $(RAWMIDI_OBJS) $(OPL3_OBJS)
 obj-$(call sequencer,$(CONFIG_SND_CS4281)) += $(RAWMIDI_OBJS) $(OPL3_OBJS)
@@ -74,6 +75,7 @@
 obj-$(call sequencer,$(CONFIG_SND_ES1968)) += $(RAWMIDI_OBJS)
 obj-$(call sequencer,$(CONFIG_SND_FM801)) += $(RAWMIDI_OBJS) $(OPL3_OBJS)
 obj-$(call sequencer,$(CONFIG_SND_ICE1712)) += $(RAWMIDI_OBJS)
+obj-$(call sequencer,$(CONFIG_SND_ICE1724)) += $(RAWMIDI_OBJS)
 obj-$(call sequencer,$(CONFIG_SND_INTEL8X0)) += $(RAWMIDI_OBJS)
 obj-$(call sequencer,$(CONFIG_SND_SONICVIBES)) += $(RAWMIDI_OBJS) $(OPL3_OBJS)
 obj-$(call sequencer,$(CONFIG_SND_VIA82XX)) += $(RAWMIDI_OBJS)
diff -urN linux-2.5.70-bk6/sound/core/seq/oss/seq_oss.c linux-2.5.70-bk7/sound/core/seq/oss/seq_oss.c
--- linux-2.5.70-bk6/sound/core/seq/oss/seq_oss.c	2003-05-26 18:00:46.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/seq/oss/seq_oss.c	2003-06-02 04:39:12.000000000 -0700
@@ -194,9 +194,7 @@
 
 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,
diff -urN linux-2.5.70-bk6/sound/core/seq/seq_clientmgr.c linux-2.5.70-bk7/sound/core/seq/seq_clientmgr.c
--- linux-2.5.70-bk6/sound/core/seq/seq_clientmgr.c	2003-05-26 18:00:24.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/seq/seq_clientmgr.c	2003-06-02 04:39:12.000000000 -0700
@@ -2454,9 +2454,7 @@
 
 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,
diff -urN linux-2.5.70-bk6/sound/core/seq/seq_memory.c linux-2.5.70-bk7/sound/core/seq/seq_memory.c
--- linux-2.5.70-bk6/sound/core/seq/seq_memory.c	2003-05-26 18:00:23.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/seq/seq_memory.c	2003-06-02 04:39:12.000000000 -0700
@@ -235,18 +235,7 @@
 	while (pool->free == NULL && ! nonblock && ! pool->closing) {
 
 		spin_unlock(&pool->lock);
-#ifdef LINUX_2_2
-		/* change semaphore to allow other clients
-		   to access device file */
-		if (file)
-			up(&semaphore_of(file));
-#endif
 		interruptible_sleep_on(&pool->output_sleep);
-#ifdef LINUX_2_2
-		/* restore semaphore again */
-		if (file)
-			down(&semaphore_of(file));
-#endif
 		spin_lock(&pool->lock);
 		/* interrupted? */
 		if (signal_pending(current)) {
diff -urN linux-2.5.70-bk6/sound/core/sgbuf.c linux-2.5.70-bk7/sound/core/sgbuf.c
--- linux-2.5.70-bk6/sound/core/sgbuf.c	2003-05-26 18:00:43.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/sgbuf.c	2003-06-02 04:39:12.000000000 -0700
@@ -20,7 +20,6 @@
  */
 
 #include <linux/config.h>
-#include <linux/version.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/mm.h>
diff -urN linux-2.5.70-bk6/sound/core/sound.c linux-2.5.70-bk7/sound/core/sound.c
--- linux-2.5.70-bk6/sound/core/sound.c	2003-05-26 18:00:43.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/sound.c	2003-06-02 04:39:12.000000000 -0700
@@ -35,11 +35,12 @@
 
 #define SNDRV_OS_MINORS 256
 
-int major = CONFIG_SND_MAJOR;
+static int major = CONFIG_SND_MAJOR;
+int snd_major;
 static int cards_limit = SNDRV_CARDS;
-int device_mode = S_IFCHR | S_IRUGO | S_IWUGO;
-int device_gid = 0;
-int device_uid = 0;
+#ifdef CONFIG_DEVFS_FS
+static int device_mode = S_IFCHR | S_IRUGO | S_IWUGO;
+#endif
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
 MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards.");
@@ -52,15 +53,11 @@
 MODULE_PARM(cards_limit, "i");
 MODULE_PARM_DESC(cards_limit, "Count of soundcards installed in the system.");
 MODULE_PARM_SYNTAX(cards_limit, "default:8,skill:advanced");
+#ifdef CONFIG_DEVFS_FS
 MODULE_PARM(device_mode, "i");
-MODULE_PARM_DESC(device_mode, "Device file permission mask for sound dynamic device filesystem.");
+MODULE_PARM_DESC(device_mode, "Device file permission mask for devfs.");
 MODULE_PARM_SYNTAX(device_mode, "default:0666,base:8");
-MODULE_PARM(device_gid, "i");
-MODULE_PARM_DESC(device_gid, "Device file GID for sound dynamic device filesystem.");
-MODULE_PARM_SYNTAX(device_gid, "default:0");
-MODULE_PARM(device_uid, "i");
-MODULE_PARM_DESC(device_uid, "Device file UID for sound dynamic device filesystem.");
-MODULE_PARM_SYNTAX(device_uid, "default:0");
+#endif
 
 int snd_ecards_limit;
 
@@ -157,9 +154,7 @@
 
 struct file_operations snd_fops =
 {
-#ifndef LINUX_2_2
 	.owner =	THIS_MODULE,
-#endif
 	.open =		snd_open
 };
 
@@ -210,22 +205,25 @@
 
 	if (minor < 0)
 		return minor;
-	preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t), GFP_KERNEL);
+	snd_assert(name, return -EINVAL);
+	preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t) + strlen(name) + 1, GFP_KERNEL);
 	if (preg == NULL)
 		return -ENOMEM;
 	*preg = *reg;
 	preg->number = minor;
 	preg->device = dev;
-	preg->dev = NULL;
+	strcpy(preg->name, name);
 	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)]);
+#ifdef CONFIG_DEVFS_FS
+	if (strncmp(name, "controlC", 8))     /* created in sound.c */
+		devfs_mk_cdev(MKDEV(major, minor), S_IFCHR | device_mode, "snd/%s", name);
+#endif
 	up(&sound_mutex);
 	return 0;
 }
@@ -253,8 +251,10 @@
 		up(&sound_mutex);
 		return -EINVAL;
 	}
-	if (mptr->dev)
-		snd_info_free_device(mptr->dev);
+#ifdef CONFIG_DEVFS_FS
+	if (strncmp(mptr->name, "controlC", 8))	/* created in sound.c */
+		devfs_remove("snd/%s", mptr->name);
+#endif
 	list_del(&mptr->list);
 	up(&sound_mutex);
 	kfree(mptr);
@@ -321,10 +321,15 @@
 
 static int __init alsa_sound_init(void)
 {
+#ifdef CONFIG_DEVFS_FS
 	short controlnum;
-	int err = 0;
+#endif
+#ifdef CONFIG_SND_OSSEMUL
+	int err;
+#endif
 	int card;
 
+	snd_major = major;
 	snd_ecards_limit = cards_limit;
 	for (card = 0; card < SNDRV_CARDS; card++)
 		INIT_LIST_HEAD(&snd_minors_hash[card]);
@@ -335,6 +340,7 @@
 	devfs_mk_dir("snd");
 	if (register_chrdev(major, "alsa", &snd_fops)) {
 		snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
+		devfs_remove("snd");
 		return -EIO;
 	}
 #ifdef CONFIG_SND_DEBUG_MEMORY
@@ -344,24 +350,21 @@
 #ifdef CONFIG_SND_DEBUG_MEMORY
 		snd_memory_done();
 #endif
+		unregister_chrdev(major, "alsa");
+		devfs_remove("snd");
 		return -ENOMEM;
 	}
 #ifdef CONFIG_SND_OSSEMUL
 	snd_info_minor_register();
 #endif
-
-	for (controlnum = 0; controlnum < cards_limit; controlnum++) {
-		devfs_mk_cdev(MKDEV(major, controlnum<<5),
-			device_mode | S_IFCHR, "snd/controlC%d", controlnum);
-	}
-
+#ifdef CONFIG_DEVFS_FS
+	for (controlnum = 0; controlnum < cards_limit; controlnum++) 
+		devfs_mk_cdev(MKDEV(major, controlnum<<5), S_IFCHR | device_mode, "snd/controlC%d", controlnum);
+#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 err;
+	return 0;
 }
 
 static void __exit alsa_sound_exit(void)
@@ -375,9 +378,6 @@
 	snd_info_minor_unregister();
 #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
@@ -390,6 +390,7 @@
 module_exit(alsa_sound_exit)
 
   /* sound.c */
+EXPORT_SYMBOL(snd_major);
 EXPORT_SYMBOL(snd_ecards_limit);
 #if defined(CONFIG_KMOD)
 EXPORT_SYMBOL(snd_request_card);
@@ -453,8 +454,6 @@
 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);
 EXPORT_SYMBOL(snd_card_proc_new);
diff -urN linux-2.5.70-bk6/sound/core/sound_oss.c linux-2.5.70-bk7/sound/core/sound_oss.c
--- linux-2.5.70-bk6/sound/core/sound_oss.c	2003-05-26 18:00:59.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/sound_oss.c	2003-06-02 04:39:12.000000000 -0700
@@ -107,7 +107,6 @@
 	*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);
diff -urN linux-2.5.70-bk6/sound/core/timer.c linux-2.5.70-bk7/sound/core/timer.c
--- linux-2.5.70-bk6/sound/core/timer.c	2003-05-26 18:00:41.000000000 -0700
+++ linux-2.5.70-bk7/sound/core/timer.c	2003-06-02 04:39:12.000000000 -0700
@@ -1733,9 +1733,7 @@
 
 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,
diff -urN linux-2.5.70-bk6/sound/drivers/Makefile linux-2.5.70-bk7/sound/drivers/Makefile
--- linux-2.5.70-bk6/sound/drivers/Makefile	2003-05-26 18:00:44.000000000 -0700
+++ linux-2.5.70-bk7/sound/drivers/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -14,4 +14,4 @@
 obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
 obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
 
-obj-$(CONFIG_SND) += opl3/ mpu401/
+obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/
diff -urN linux-2.5.70-bk6/sound/drivers/dummy.c linux-2.5.70-bk7/sound/drivers/dummy.c
--- linux-2.5.70-bk6/sound/drivers/dummy.c	2003-05-26 18:00:57.000000000 -0700
+++ linux-2.5.70-bk7/sound/drivers/dummy.c	2003-06-02 04:39:12.000000000 -0700
@@ -21,11 +21,7 @@
 #include <sound/driver.h>
 #include <linux/version.h>
 #include <linux/init.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
 #include <linux/jiffies.h>
-#else
-#include <linux/sched.h>
-#endif
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/wait.h>
@@ -58,8 +54,8 @@
 #if 0 /* ICE1712 emulation */
 #define MAX_BUFFER_SIZE		(256 * 1024)
 #define USE_FORMATS		SNDRV_PCM_FMTBIT_S32_LE
-#define USE_CHANNELS_MIN	12
-#define USE_CHANNELS_MAX	12
+#define USE_CHANNELS_MIN	10
+#define USE_CHANNELS_MAX	10
 #define USE_PERIODS_MIN		1
 #define USE_PERIODS_MAX		1024
 #endif
@@ -247,8 +243,8 @@
 	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;
+	if (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);	
@@ -430,7 +426,7 @@
 {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
+	uinfo->value.integer.min = -50;
 	uinfo->value.integer.max = 100;
 	return 0;
 }
@@ -455,8 +451,16 @@
 	int change, addr = kcontrol->private_value;
 	int left, right;
 
-	left = ucontrol->value.integer.value[0] % 101;
-	right = ucontrol->value.integer.value[1] % 101;
+	left = ucontrol->value.integer.value[0];
+	if (left < -50)
+		left = -50;
+	if (left > 100)
+		left = 100;
+	right = ucontrol->value.integer.value[1];
+	if (right < -50)
+		right = -50;
+	if (right > 100)
+		right = 100;
 	spin_lock_irqsave(&dummy->mixer_lock, flags);
 	change = dummy->mixer_volume[addr][0] != left ||
 	         dummy->mixer_volume[addr][1] != right;
diff -urN linux-2.5.70-bk6/sound/drivers/mpu401/Makefile linux-2.5.70-bk7/sound/drivers/mpu401/Makefile
--- linux-2.5.70-bk6/sound/drivers/mpu401/Makefile	2003-05-26 18:01:02.000000000 -0700
+++ linux-2.5.70-bk7/sound/drivers/mpu401/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -31,6 +31,7 @@
 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_ICE1724) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_INTEL8X0) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_SONICVIBES) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_VIA82XX) += snd-mpu401-uart.o
@@ -38,5 +39,6 @@
 obj-$(CONFIG_SND_TRIDENT) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_YMFPCI) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_PC98_CS4232) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_SSCAPE) += snd-mpu401-uart.o
 
 obj-m := $(sort $(obj-m))
diff -urN linux-2.5.70-bk6/sound/drivers/mpu401/mpu401_uart.c linux-2.5.70-bk7/sound/drivers/mpu401/mpu401_uart.c
--- linux-2.5.70-bk6/sound/drivers/mpu401/mpu401_uart.c	2003-05-26 18:00:38.000000000 -0700
+++ linux-2.5.70-bk7/sound/drivers/mpu401/mpu401_uart.c	2003-06-02 04:39:12.000000000 -0700
@@ -90,27 +90,27 @@
 #endif
 }
 
-static irqreturn_t _snd_mpu401_uart_interrupt(mpu401_t *mpu)
+static void _snd_mpu401_uart_interrupt(mpu401_t *mpu)
 {
+	spin_lock(&mpu->input_lock);
 	if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) {
-		if (! test_and_set_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode)) {
-			spin_lock(&mpu->input_lock);
-			snd_mpu401_uart_input_read(mpu);
-			spin_unlock(&mpu->input_lock);
-			clear_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode);
-		}
-	} else
+		atomic_dec(&mpu->rx_loop);
+		snd_mpu401_uart_input_read(mpu);
+		atomic_inc(&mpu->rx_loop);
+	} else {
 		snd_mpu401_uart_clear_rx(mpu);
+	}
+	spin_unlock(&mpu->input_lock);
  	/* ok. for better Tx performance try do some output when input is done */
 	if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) &&
 	    test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) {
-		spin_lock(&mpu->output_lock);
-		snd_mpu401_uart_output_write(mpu);
-		spin_unlock(&mpu->output_lock);
+		if (spin_trylock(&mpu->output_lock)) {
+			atomic_dec(&mpu->tx_loop);
+			snd_mpu401_uart_output_write(mpu);
+			atomic_inc(&mpu->tx_loop);
+			spin_unlock(&mpu->output_lock);
+		}
 	}
-
-	/* FIXME! This should really check whether the irq was for us */
-	return IRQ_HANDLED;
 }
 
 /**
@@ -127,7 +127,8 @@
 	
 	if (mpu == NULL)
 		return IRQ_NONE;
-	return _snd_mpu401_uart_interrupt(mpu);
+	_snd_mpu401_uart_interrupt(mpu);
+	return IRQ_HANDLED;
 }
 
 /*
@@ -242,6 +243,7 @@
 		snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1);
 	}
 	mpu->substream_input = substream;
+	atomic_set(&mpu->rx_loop, 1);
 	set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode);
 	return 0;
 }
@@ -259,6 +261,7 @@
 		snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1);
 	}
 	mpu->substream_output = substream;
+	atomic_set(&mpu->tx_loop, 1);
 	set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode);
 	return 0;
 }
@@ -312,12 +315,15 @@
 		
 		/* read data in advance */
 		/* prevent double enter via rawmidi->event callback */
-		if (! test_and_set_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode)) {
-			spin_lock_irqsave(&mpu->input_lock, flags);
-			snd_mpu401_uart_input_read(mpu);
-			spin_unlock_irqrestore(&mpu->input_lock, flags);
-			clear_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode);
+		if (atomic_dec_and_test(&mpu->rx_loop)) {
+			local_irq_save(flags);
+			if (spin_trylock(&mpu->input_lock)) {
+				snd_mpu401_uart_input_read(mpu);
+				spin_unlock(&mpu->input_lock);
+			}
+			local_irq_restore(flags);
 		}
+		atomic_inc(&mpu->rx_loop);
 	} else {
 		if (mpu->irq < 0)
 			snd_mpu401_uart_remove_timer(mpu, 1);
@@ -371,9 +377,11 @@
 					break;
 				}
 			}
+			if (timeout == 0)
+				break;	/* Tx FIFO full - try again later */
 		} else {
 			snd_mpu401_uart_remove_timer (mpu, 0);
-			max = 1; /* no other data - leave the tx loop */
+			break;	/* no other data - leave the tx loop */
 		}
 	} while (--max > 0);
 }
@@ -389,6 +397,7 @@
 	mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return);
 	if (up) {
 		set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);
+
 		/* try to add the timer at each output trigger,
 		 * since the output timer might have been removed in
 		 * snd_mpu401_uart_output_write().
@@ -397,12 +406,15 @@
 
 		/* output pending data */
 		/* prevent double enter via rawmidi->event callback */
-		if (! test_and_set_bit(MPU401_MODE_BIT_TX_LOOP, &mpu->mode)) {
-			spin_lock_irqsave(&mpu->output_lock, flags);
-			snd_mpu401_uart_output_write(mpu);
-			spin_unlock_irqrestore(&mpu->output_lock, flags);
-			clear_bit(MPU401_MODE_BIT_TX_LOOP, &mpu->mode);
+		if (atomic_dec_and_test(&mpu->tx_loop)) {
+			local_irq_save(flags);
+			if (spin_trylock(&mpu->output_lock)) {
+				snd_mpu401_uart_output_write(mpu);
+				spin_unlock(&mpu->output_lock);
+			}
+			local_irq_restore(flags);
 		}
+		atomic_inc(&mpu->tx_loop);
 	} else {
 		snd_mpu401_uart_remove_timer(mpu, 0);
 		clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);
diff -urN linux-2.5.70-bk6/sound/drivers/opl3/Makefile linux-2.5.70-bk7/sound/drivers/opl3/Makefile
--- linux-2.5.70-bk6/sound/drivers/opl3/Makefile	2003-05-26 18:00:20.000000000 -0700
+++ linux-2.5.70-bk7/sound/drivers/opl3/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -4,12 +4,10 @@
 #
 
 snd-opl3-lib-objs := opl3_lib.o opl3_synth.o
-ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
 snd-opl3-synth-objs := opl3_seq.o opl3_midi.o opl3_drums.o
 ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
 snd-opl3-synth-objs += opl3_oss.o
 endif
-endif
 
 OPL3_OBJS = snd-opl3-lib.o
 ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
diff -urN linux-2.5.70-bk6/sound/drivers/opl3/opl3_lib.c linux-2.5.70-bk7/sound/drivers/opl3/opl3_lib.c
--- linux-2.5.70-bk6/sound/drivers/opl3/opl3_lib.c	2003-05-26 18:00:25.000000000 -0700
+++ linux-2.5.70-bk7/sound/drivers/opl3/opl3_lib.c	2003-06-02 04:39:12.000000000 -0700
@@ -160,17 +160,11 @@
 		opl3->hardware = OPL3_HW_OPL2;
 	} else {
 		/*
-		 * Detect availability of OPL4. Unfortunately, the OPL4
-		 * port of the chip may not be connected to the PC bus.
+		 * If we had an OPL4 chip, opl3->hardware would have been set
+		 * by the OPL4 driver; so we can assume OPL3 here.
 		 */
 		snd_assert(opl3->r_port != 0, return -ENODEV);
-		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE | OPL3_OPL4_ENABLE);
-		/* All OPL4 registers are readable. */
-		if (inb(opl3->r_port + 1) == (OPL3_OPL3_ENABLE | OPL3_OPL4_ENABLE)) {
-			opl3->hardware = OPL3_HW_OPL4;
-		} else {
-			opl3->hardware = OPL3_HW_OPL3;
-		}
+		opl3->hardware = OPL3_HW_OPL3;
 	}
 	return 0;
 }
diff -urN linux-2.5.70-bk6/sound/drivers/opl4/Makefile linux-2.5.70-bk7/sound/drivers/opl4/Makefile
--- linux-2.5.70-bk6/sound/drivers/opl4/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/opl4/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,15 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o opl4_proc.o
+snd-opl4-synth-objs := opl4_seq.o opl4_synth.o yrw801.o
+
+OPL4_OBJS := snd-opl4-lib.o
+ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
+  OPL4_OBJS += snd-opl4-synth.o
+endif
+
+obj-$(CONFIG_SND_OPTI92X_AD1848) += $(OPL4_OBJS)
+obj-$(CONFIG_SND_OPTI92X_CS4231) += $(OPL4_OBJS)
diff -urN linux-2.5.70-bk6/sound/drivers/opl4/opl4_lib.c linux-2.5.70-bk7/sound/drivers/opl4/opl4_lib.c
--- linux-2.5.70-bk6/sound/drivers/opl4/opl4_lib.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/opl4/opl4_lib.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,275 @@
+/*
+ * Functions for accessing OPL4 devices
+ * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.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 "opl4_local.h"
+#include <sound/initval.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_DESCRIPTION("OPL4 driver");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+
+static void inline snd_opl4_wait(opl4_t *opl4)
+{
+	int timeout = 10;
+	while ((inb(opl4->fm_port) & OPL4_STATUS_BUSY) && --timeout > 0)
+		;
+}
+
+void snd_opl4_write(opl4_t *opl4, u8 reg, u8 value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&opl4->reg_lock, flags);
+
+	snd_opl4_wait(opl4);
+	outb(reg, opl4->pcm_port);
+
+	snd_opl4_wait(opl4);
+	outb(value, opl4->pcm_port + 1);
+
+	spin_unlock_irqrestore(&opl4->reg_lock, flags);
+}
+
+u8 snd_opl4_read(opl4_t *opl4, u8 reg)
+{
+	unsigned long flags;
+	u8 value;
+
+	spin_lock_irqsave(&opl4->reg_lock, flags);
+
+	snd_opl4_wait(opl4);
+	outb(reg, opl4->pcm_port);
+
+	snd_opl4_wait(opl4);
+	value = inb(opl4->pcm_port + 1);
+
+	spin_unlock_irqrestore(&opl4->reg_lock, flags);
+	return value;
+}
+
+void snd_opl4_read_memory(opl4_t *opl4, char *buf, int offset, int size)
+{
+	u8 memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION);
+	snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg | OPL4_MODE_BIT);
+
+	snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_HIGH, offset >> 16);
+	snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_MID, offset >> 8);
+	snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_LOW, offset);
+	for (; size > 0; size--)
+		*buf++ = snd_opl4_read(opl4, OPL4_REG_MEMORY_DATA);
+
+	snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg);
+}
+
+void snd_opl4_write_memory(opl4_t *opl4, const char *buf, int offset, int size)
+{
+	u8 memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION);
+	snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg | OPL4_MODE_BIT);
+
+	snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_HIGH, offset >> 16);
+	snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_MID, offset >> 8);
+	snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_LOW, offset);
+	for (; size > 0; size--)
+		snd_opl4_write(opl4, OPL4_REG_MEMORY_DATA, *buf++);
+
+	snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg);
+}
+
+static void snd_opl4_enable_opl4(opl4_t *opl4)
+{
+	outb(OPL3_REG_MODE, opl4->fm_port + 2);
+	inb(opl4->fm_port);
+	inb(opl4->fm_port);
+	outb(OPL3_OPL3_ENABLE | OPL3_OPL4_ENABLE, opl4->fm_port + 3);
+	inb(opl4->fm_port);
+	inb(opl4->fm_port);
+}
+
+static int snd_opl4_detect(opl4_t *opl4)
+{
+	u8 id1, id2;
+
+	snd_opl4_enable_opl4(opl4);
+
+	id1 = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION);
+	snd_printdd("OPL4[02]=%02x\n", id1);
+	switch (id1 & OPL4_DEVICE_ID_MASK) {
+	case 0x20:
+		opl4->hardware = OPL3_HW_OPL4;
+		break;
+	case 0x40:
+		opl4->hardware = OPL3_HW_OPL4_ML;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_FM, 0x00);
+	snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_PCM, 0xff);
+	id1 = snd_opl4_read(opl4, OPL4_REG_MIX_CONTROL_FM);
+	id2 = snd_opl4_read(opl4, OPL4_REG_MIX_CONTROL_PCM);
+	snd_printdd("OPL4 id1=%02x id2=%02x\n", id1, id2);
+       	if (id1 != 0x00 || id2 != 0xff)
+		return -ENODEV;
+
+	snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_FM, 0x3f);
+	snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_PCM, 0x3f);
+	snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, 0x00);
+	return 0;
+}
+
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+static void snd_opl4_seq_dev_free(snd_seq_device_t *seq_dev)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, seq_dev->private_data, return);
+	opl4->seq_dev = NULL;
+}
+
+static int snd_opl4_create_seq_dev(opl4_t *opl4, int seq_device)
+{
+	opl4->seq_dev_num = seq_device;
+	if (snd_seq_device_new(opl4->card, seq_device, SNDRV_SEQ_DEV_ID_OPL4,
+			       sizeof(opl4_t *), &opl4->seq_dev) >= 0) {
+		strcpy(opl4->seq_dev->name, "OPL4 Wavetable");
+		*(opl4_t **)SNDRV_SEQ_DEVICE_ARGPTR(opl4->seq_dev) = opl4;
+		opl4->seq_dev->private_data = opl4;
+		opl4->seq_dev->private_free = snd_opl4_seq_dev_free;
+	}
+	return 0;
+}
+#endif
+
+static void snd_opl4_free(opl4_t *opl4)
+{
+#ifdef CONFIG_PROC_FS
+	snd_opl4_free_proc(opl4);
+#endif
+	if (opl4->res_fm_port) {
+		release_resource(opl4->res_fm_port);
+		kfree_nocheck(opl4->res_fm_port);
+	}
+	if (opl4->res_pcm_port) {
+		release_resource(opl4->res_pcm_port);
+		kfree_nocheck(opl4->res_pcm_port);
+	}
+	snd_magic_kfree(opl4);
+}
+
+static int snd_opl4_dev_free(snd_device_t *device)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, device->device_data, return -ENXIO);
+	snd_opl4_free(opl4);
+	return 0;
+}
+
+int snd_opl4_create(snd_card_t *card,
+		    unsigned long fm_port, unsigned long pcm_port,
+		    int seq_device,
+		    opl3_t **ropl3, opl4_t **ropl4)
+{
+	opl4_t *opl4;
+	opl3_t *opl3;
+	int err;
+	static snd_device_ops_t ops = {
+		.dev_free = snd_opl4_dev_free
+	};
+
+	if (ropl3)
+		*ropl3 = NULL;
+	if (ropl4)
+		*ropl4 = NULL;
+
+	opl4 = snd_magic_kcalloc(opl4_t, 0, GFP_KERNEL);
+	if (!opl4)
+		return -ENOMEM;
+
+	opl4->res_fm_port = request_region(fm_port, 8, "OPL4 FM");
+	opl4->res_pcm_port = request_region(pcm_port, 8, "OPL4 PCM/MIX");
+	if (!opl4->res_fm_port || !opl4->res_pcm_port) {
+		snd_opl4_free(opl4);
+		return -EBUSY;
+	}
+
+	opl4->card = card;
+	opl4->fm_port = fm_port;
+	opl4->pcm_port = pcm_port;
+	spin_lock_init(&opl4->reg_lock);
+	init_MUTEX(&opl4->access_mutex);
+
+	err = snd_opl4_detect(opl4);
+	if (err < 0) {
+		snd_opl4_free(opl4);
+		snd_printd("OPL4 chip not detected at %#lx/%#lx\n", fm_port, pcm_port);
+		return err;
+	}
+
+	err = snd_opl3_create(card, fm_port, fm_port + 2, opl4->hardware, 1, &opl3);
+	if (err < 0) {
+		snd_opl4_free(opl4);
+		return err;
+	}
+
+	/* opl3 initialization disabled opl4, so reenable */
+	snd_opl4_enable_opl4(opl4); 
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, opl4, &ops);
+	if (err < 0) {
+		snd_device_free(card, opl3);
+		snd_opl4_free(opl4);
+		return err;
+	}
+
+	snd_opl4_create_mixer(opl4);
+#ifdef CONFIG_PROC_FS
+	snd_opl4_create_proc(opl4);
+#endif
+
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	opl4->seq_client = -1;
+	if (opl4->hardware < OPL3_HW_OPL4_ML)
+		snd_opl4_create_seq_dev(opl4, seq_device);
+#endif
+
+	if (ropl3)
+		*ropl3 = opl3;
+	if (ropl4)
+		*ropl4 = opl4;
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_opl4_write);
+EXPORT_SYMBOL(snd_opl4_read);
+EXPORT_SYMBOL(snd_opl4_write_memory);
+EXPORT_SYMBOL(snd_opl4_read_memory);
+EXPORT_SYMBOL(snd_opl4_create);
+
+static int __init alsa_opl4_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_opl4_exit(void)
+{
+}
+
+module_init(alsa_opl4_init)
+module_exit(alsa_opl4_exit)
diff -urN linux-2.5.70-bk6/sound/drivers/opl4/opl4_local.h linux-2.5.70-bk7/sound/drivers/opl4/opl4_local.h
--- linux-2.5.70-bk6/sound/drivers/opl4/opl4_local.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/opl4/opl4_local.h	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,231 @@
+/*
+ * Local definitions for the OPL4 driver
+ *
+ * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed and/or modified under the
+ * terms of the GNU 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 BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#ifndef __OPL4_LOCAL_H
+#define __OPL4_LOCAL_H
+
+#include <sound/opl4.h>
+
+/*
+ * Register numbers
+ */
+
+#define OPL4_REG_TEST0			0x00
+#define OPL4_REG_TEST1			0x01
+
+#define OPL4_REG_MEMORY_CONFIGURATION	0x02
+#define   OPL4_MODE_BIT			0x01
+#define   OPL4_MTYPE_BIT		0x02
+#define   OPL4_TONE_HEADER_MASK		0x1c
+#define   OPL4_DEVICE_ID_MASK		0xe0
+
+#define OPL4_REG_MEMORY_ADDRESS_HIGH	0x03
+#define OPL4_REG_MEMORY_ADDRESS_MID	0x04
+#define OPL4_REG_MEMORY_ADDRESS_LOW	0x05
+#define OPL4_REG_MEMORY_DATA		0x06
+
+/*
+ * Offsets to the register banks for voices. To get the
+ * register number just add the voice number to the bank offset.
+ *
+ * Wave Table Number low bits (0x08 to 0x1F)
+ */
+#define OPL4_REG_TONE_NUMBER		0x08
+
+/* Wave Table Number high bit, F-Number low bits (0x20 to 0x37) */
+#define OPL4_REG_F_NUMBER		0x20
+#define   OPL4_TONE_NUMBER_BIT8		0x01
+#define   OPL4_F_NUMBER_LOW_MASK	0xfe
+
+/* F-Number high bits, Octave, Pseudo-Reverb (0x38 to 0x4F) */
+#define OPL4_REG_OCTAVE			0x38
+#define   OPL4_F_NUMBER_HIGH_MASK	0x07
+#define   OPL4_BLOCK_MASK		0xf0
+#define   OPL4_PSEUDO_REVERB_BIT	0x08
+
+/* Total Level, Level Direct (0x50 to 0x67) */
+#define OPL4_REG_LEVEL			0x50
+#define   OPL4_TOTAL_LEVEL_MASK		0xfe
+#define   OPL4_LEVEL_DIRECT_BIT		0x01
+
+/* Key On, Damp, LFO RST, CH, Panpot (0x68 to 0x7F) */
+#define OPL4_REG_MISC			0x68
+#define   OPL4_KEY_ON_BIT		0x80
+#define   OPL4_DAMP_BIT			0x40
+#define   OPL4_LFO_RESET_BIT		0x20
+#define   OPL4_OUTPUT_CHANNEL_BIT	0x10
+#define   OPL4_PAN_POT_MASK		0x0f
+
+/* LFO, VIB (0x80 to 0x97) */
+#define OPL4_REG_LFO_VIBRATO		0x80
+#define   OPL4_LFO_FREQUENCY_MASK	0x38
+#define   OPL4_VIBRATO_DEPTH_MASK	0x07
+#define   OPL4_CHORUS_SEND_MASK		0xc0 /* ML only */
+
+/* Attack / Decay 1 rate (0x98 to 0xAF) */
+#define OPL4_REG_ATTACK_DECAY1		0x98
+#define   OPL4_ATTACK_RATE_MASK		0xf0
+#define   OPL4_DECAY1_RATE_MASK		0x0f
+
+/* Decay level / 2 rate (0xB0 to 0xC7) */
+#define OPL4_REG_LEVEL_DECAY2		0xb0
+#define   OPL4_DECAY_LEVEL_MASK		0xf0
+#define   OPL4_DECAY2_RATE_MASK		0x0f
+
+/* Release rate / Rate correction (0xC8 to 0xDF) */
+#define OPL4_REG_RELEASE_CORRECTION	0xc8
+#define   OPL4_RELEASE_RATE_MASK	0x0f
+#define   OPL4_RATE_INTERPOLATION_MASK	0xf0
+
+/* AM (0xE0 to 0xF7) */
+#define OPL4_REG_TREMOLO		0xe0
+#define   OPL4_TREMOLO_DEPTH_MASK	0x07
+#define   OPL4_REVERB_SEND_MASK		0xe0 /* ML only */
+
+/* Mixer */
+#define OPL4_REG_MIX_CONTROL_FM		0xf8
+#define OPL4_REG_MIX_CONTROL_PCM	0xf9
+#define   OPL4_MIX_LEFT_MASK		0x07
+#define   OPL4_MIX_RIGHT_MASK		0x38
+
+#define OPL4_REG_ATC			0xfa
+#define   OPL4_ATC_BIT			0x01 /* ???, ML only */
+
+/* bits in the OPL3 Status register */
+#define OPL4_STATUS_BUSY		0x01
+#define OPL4_STATUS_LOAD		0x02
+
+
+#define OPL4_MAX_VOICES 24
+
+#define SNDRV_SEQ_DEV_ID_OPL4 "opl4-synth"
+
+
+typedef struct opl4_sound {
+	u16 tone;
+	s16 pitch_offset;
+	u8 key_scaling;
+	s8 panpot;
+	u8 vibrato;
+	u8 tone_attenuate;
+	u8 volume_factor;
+	u8 reg_lfo_vibrato;
+	u8 reg_attack_decay1;
+	u8 reg_level_decay2;
+	u8 reg_release_correction;
+	u8 reg_tremolo;
+} opl4_sound_t;
+
+typedef struct opl4_region {
+	u8 key_min, key_max;
+	opl4_sound_t sound;
+} opl4_region_t;
+
+typedef struct opl4_region_ptr {
+	int count;
+	const opl4_region_t *regions;
+} opl4_region_ptr_t;
+
+typedef struct opl4_voice {
+	struct list_head list;
+	int number;
+	snd_midi_channel_t *chan;
+	int note;
+	int velocity;
+	const opl4_sound_t *sound;
+	u8 reg_f_number;
+	u8 reg_misc;
+} opl4_voice_t;
+
+struct opl4 {
+	unsigned long fm_port;
+	unsigned long pcm_port;
+	struct resource *res_fm_port;
+	struct resource *res_pcm_port;
+	unsigned short hardware;
+	spinlock_t reg_lock;
+	snd_card_t *card;
+
+#ifdef CONFIG_PROC_FS
+	snd_info_entry_t *proc_entry;
+	int memory_access;
+#endif
+	struct semaphore access_mutex;
+
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	int used;
+
+	int seq_dev_num;
+	int seq_client;
+	snd_seq_device_t *seq_dev;
+
+	snd_midi_channel_set_t *chset;
+	opl4_voice_t voices[OPL4_MAX_VOICES];
+	struct list_head off_voices;
+	struct list_head on_voices;
+	spinlock_t voices_lock;
+#endif
+};
+
+/* opl4_lib.c */
+void snd_opl4_write(opl4_t *opl4, u8 reg, u8 value);
+u8 snd_opl4_read(opl4_t *opl4, u8 reg);
+void snd_opl4_read_memory(opl4_t *opl4, char *buf, int offset, int size);
+void snd_opl4_write_memory(opl4_t *opl4, const char *buf, int offset, int size);
+
+/* opl4_mixer.c */
+int snd_opl4_create_mixer(opl4_t *opl4);
+
+#ifdef CONFIG_PROC_FS
+/* opl4_proc.c */
+int snd_opl4_create_proc(opl4_t *opl4);
+void snd_opl4_free_proc(opl4_t *opl4);
+#endif
+
+/* opl4_seq.c */
+extern int volume_boost;
+
+/* opl4_synth.c */
+void snd_opl4_synth_reset(opl4_t *opl4);
+void snd_opl4_synth_shutdown(opl4_t *opl4);
+void snd_opl4_note_on(void *p, int note, int vel, snd_midi_channel_t *chan);
+void snd_opl4_note_off(void *p, int note, int vel, snd_midi_channel_t *chan);
+void snd_opl4_terminate_note(void *p, int note, snd_midi_channel_t *chan);
+void snd_opl4_control(void *p, int type, snd_midi_channel_t *chan);
+void snd_opl4_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset);
+
+/* yrw801.c */
+int snd_yrw801_detect(opl4_t *opl4);
+extern const opl4_region_ptr_t snd_yrw801_regions[];
+
+#endif /* __OPL4_LOCAL_H */
diff -urN linux-2.5.70-bk6/sound/drivers/opl4/opl4_mixer.c linux-2.5.70-bk7/sound/drivers/opl4/opl4_mixer.c
--- linux-2.5.70-bk6/sound/drivers/opl4/opl4_mixer.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/opl4/opl4_mixer.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,93 @@
+/*
+ * OPL4 mixer functions
+ * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.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 "opl4_local.h"
+#include <sound/control.h>
+
+#define chip_t opl4_t
+
+static int snd_opl4_ctl_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 = 7;
+	return 0;
+}
+
+static int snd_opl4_ctl_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	opl4_t *opl4 = snd_kcontrol_chip(kcontrol);
+	u8 reg = kcontrol->private_value;
+	u8 value;
+
+	value = snd_opl4_read(opl4, reg);
+	ucontrol->value.integer.value[0] = 7 - (value & 7);
+	ucontrol->value.integer.value[1] = 7 - ((value >> 3) & 7);
+	return 0;
+}
+
+static int snd_opl4_ctl_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	opl4_t *opl4 = snd_kcontrol_chip(kcontrol);
+	u8 reg = kcontrol->private_value;
+	u8 value, old_value;
+
+	value = (7 - (ucontrol->value.integer.value[0] & 7)) |
+		((7 - (ucontrol->value.integer.value[1] & 7)) << 3);
+	old_value = snd_opl4_read(opl4, reg);
+	snd_opl4_write(opl4, reg, value);
+	return value != old_value;
+}
+
+static snd_kcontrol_new_t snd_opl4_controls[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "FM Playback Volume",
+		.info = snd_opl4_ctl_info,
+		.get = snd_opl4_ctl_get,
+		.put = snd_opl4_ctl_put,
+		.private_value = OPL4_REG_MIX_CONTROL_FM
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Wavetable Playback Volume",
+		.info = snd_opl4_ctl_info,
+		.get = snd_opl4_ctl_get,
+		.put = snd_opl4_ctl_put,
+		.private_value = OPL4_REG_MIX_CONTROL_PCM
+	}
+};
+
+int snd_opl4_create_mixer(opl4_t *opl4)
+{
+	snd_card_t *card = opl4->card;
+	int i, err;
+
+#if 0	/* already set by the codec driver */
+	strcpy(card->mixername, "OPL4 Mixer");
+#endif
+
+	for (i = 0; i < 2; ++i) {
+		err = snd_ctl_add(card, snd_ctl_new1(&snd_opl4_controls[i], opl4));
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
diff -urN linux-2.5.70-bk6/sound/drivers/opl4/opl4_proc.c linux-2.5.70-bk7/sound/drivers/opl4/opl4_proc.c
--- linux-2.5.70-bk6/sound/drivers/opl4/opl4_proc.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/opl4/opl4_proc.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,165 @@
+/*
+ * Functions for the OPL4 proc file
+ * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.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 "opl4_local.h"
+#include <sound/info.h>
+
+#ifdef CONFIG_PROC_FS
+
+static int snd_opl4_mem_proc_open(snd_info_entry_t *entry,
+				  unsigned short mode, void **file_private_data)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, entry->private_data, return -ENXIO);
+
+	down(&opl4->access_mutex);
+	if (opl4->memory_access) {
+		up(&opl4->access_mutex);
+		return -EBUSY;
+	}
+	opl4->memory_access++;
+	up(&opl4->access_mutex);
+	return 0;
+}
+
+static int snd_opl4_mem_proc_release(snd_info_entry_t *entry,
+				     unsigned short mode, void *file_private_data)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, entry->private_data, return -ENXIO);
+
+	down(&opl4->access_mutex);
+	opl4->memory_access--;
+	up(&opl4->access_mutex);
+	return 0;
+}
+
+static long snd_opl4_mem_proc_read(snd_info_entry_t *entry, void *file_private_data,
+				   struct file *file, char *_buf, long count)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, entry->private_data, return -ENXIO);
+	long size;
+	char* buf;
+
+	size = count;
+	if (file->f_pos + size > entry->size)
+		size = entry->size - file->f_pos;
+	if (size > 0) {
+		buf = kmalloc(size, GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;
+		snd_opl4_read_memory(opl4, buf, file->f_pos, size);
+		if (copy_to_user(_buf, buf, size)) {
+			kfree(buf);
+			return -EFAULT;
+		}
+		kfree(buf);
+		file->f_pos += size;
+		return size;
+	}
+	return 0;
+}
+
+static long snd_opl4_mem_proc_write(snd_info_entry_t *entry, void *file_private_data,
+				    struct file *file, const char *_buf, long count)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, entry->private_data, return -ENXIO);
+	long size;
+	char *buf;
+
+	size = count;
+	if (file->f_pos + size > entry->size)
+		size = entry->size - file->f_pos;
+	if (size > 0) {
+		buf = kmalloc(size, GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;
+		if (copy_from_user(buf, _buf, size)) {
+			kfree(buf);
+			return -EFAULT;
+		}
+		snd_opl4_write_memory(opl4, buf, file->f_pos, size);
+		kfree(buf);
+		file->f_pos += size;
+		return size;
+	}
+	return 0;
+}
+
+static long long snd_opl4_mem_proc_llseek(snd_info_entry_t *entry, void *file_private_data,
+					  struct file *file, long long offset, int orig)
+{
+	switch (orig) {
+	case 0: /* SEEK_SET */
+		file->f_pos = offset;
+		break;
+	case 1: /* SEEK_CUR */
+		file->f_pos += offset;
+		break;
+	case 2: /* SEEK_END, offset is negative */
+		file->f_pos = entry->size + offset;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (file->f_pos > entry->size)
+		file->f_pos = entry->size;
+	return file->f_pos;
+}
+
+static struct snd_info_entry_ops snd_opl4_mem_proc_ops = {
+	.open = snd_opl4_mem_proc_open,
+	.release = snd_opl4_mem_proc_release,
+	.read = snd_opl4_mem_proc_read,
+	.write = snd_opl4_mem_proc_write,
+	.llseek = snd_opl4_mem_proc_llseek,
+};
+
+int snd_opl4_create_proc(opl4_t *opl4)
+{
+	snd_info_entry_t *entry;
+
+	entry = snd_info_create_card_entry(opl4->card, "opl4-mem", opl4->card->proc_root);
+	if (entry) {
+		if (opl4->hardware < OPL3_HW_OPL4_ML) {
+			/* OPL4 can access 4 MB external ROM/SRAM */
+			entry->mode |= S_IWUSR;
+			entry->size = 4 * 1024 * 1024;
+		} else {
+			/* OPL4-ML has 1 MB internal ROM */
+			entry->size = 1 * 1024 * 1024;
+		}
+		entry->content = SNDRV_INFO_CONTENT_DATA;
+		entry->c.ops = &snd_opl4_mem_proc_ops;
+		entry->module = THIS_MODULE;
+		entry->private_data = opl4;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	opl4->proc_entry = entry;
+	return 0;
+}
+
+void snd_opl4_free_proc(opl4_t *opl4)
+{
+	if (opl4->proc_entry)
+		snd_info_unregister(opl4->proc_entry);
+}
+
+#endif /* CONFIG_PROC_FS */
diff -urN linux-2.5.70-bk6/sound/drivers/opl4/opl4_seq.c linux-2.5.70-bk7/sound/drivers/opl4/opl4_seq.c
--- linux-2.5.70-bk6/sound/drivers/opl4/opl4_seq.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/opl4/opl4_seq.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,224 @@
+/*
+ * OPL4 sequencer functions
+ *
+ * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed and/or modified under the
+ * terms of the GNU 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 BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include "opl4_local.h"
+#include <linux/init.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_DESCRIPTION("OPL4 wavetable synth driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_CLASSES("{sound}");
+
+int volume_boost = 8;
+
+MODULE_PARM(volume_boost, "i");
+MODULE_PARM_DESC(volume_boost, "Additional volume for OPL4 wavetable sounds.");
+MODULE_PARM_SYNTAX(volume_boost, "default:8");
+
+static int snd_opl4_seq_use_inc(opl4_t *opl4)
+{
+	if (!try_module_get(opl4->card->module))
+		return -EFAULT;
+	return 0;
+}
+
+static void snd_opl4_seq_use_dec(opl4_t *opl4)
+{
+	module_put(opl4->card->module);
+}
+
+static int snd_opl4_seq_use(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return -ENXIO);
+	int err;
+
+	down(&opl4->access_mutex);
+
+	if (opl4->used) {
+		up(&opl4->access_mutex);
+		return -EBUSY;
+	}
+	opl4->used++;
+
+	if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
+		err = snd_opl4_seq_use_inc(opl4);
+		if (err < 0) {
+			up(&opl4->access_mutex);
+			return err;
+		}
+	}
+
+	up(&opl4->access_mutex);
+
+	snd_opl4_synth_reset(opl4);
+	return 0;
+}
+
+static int snd_opl4_seq_unuse(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return -ENXIO);
+
+	snd_opl4_synth_shutdown(opl4);
+
+	down(&opl4->access_mutex);
+	opl4->used--;
+	up(&opl4->access_mutex);
+
+	if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM)
+		snd_opl4_seq_use_dec(opl4);
+	return 0;
+}
+
+static snd_midi_op_t opl4_ops = {
+	.note_on =		snd_opl4_note_on,
+	.note_off =		snd_opl4_note_off,
+	.note_terminate =	snd_opl4_terminate_note,
+	.control =		snd_opl4_control,
+	.sysex =		snd_opl4_sysex,
+};
+
+static int snd_opl4_seq_event_input(snd_seq_event_t *ev, int direct,
+				    void *private_data, int atomic, int hop)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return -ENXIO);
+
+	snd_midi_process_event(&opl4_ops, ev, opl4->chset);
+	return 0;
+}
+
+static void snd_opl4_seq_free_port(void *private_data)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return);
+
+	snd_midi_channel_free_set(opl4->chset);
+}
+
+static int snd_opl4_seq_new_device(snd_seq_device_t *dev)
+{
+	opl4_t *opl4;
+	int client;
+	snd_seq_client_callback_t callbacks;
+	snd_seq_client_info_t cinfo;
+	snd_seq_port_callback_t pcallbacks;
+
+	opl4 = *(opl4_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (!opl4)
+		return -EINVAL;
+
+	if (snd_yrw801_detect(opl4) < 0)
+		return -ENODEV;
+
+	opl4->chset = snd_midi_channel_alloc_set(16);
+	if (!opl4->chset)
+		return -ENOMEM;
+	opl4->chset->private_data = opl4;
+
+	/* allocate new client */
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.private_data = opl4;
+	callbacks.allow_output = callbacks.allow_input = 1;
+	client = snd_seq_create_kernel_client(opl4->card, opl4->seq_dev_num, &callbacks);
+	if (client < 0) {
+		snd_midi_channel_free_set(opl4->chset);
+		return client;
+	}
+	opl4->seq_client = client;
+	opl4->chset->client = client;
+
+	/* change name of client */
+	memset(&cinfo, 0, sizeof(cinfo));
+	cinfo.client = client;
+	cinfo.type = KERNEL_CLIENT;
+	strcpy(cinfo.name, "OPL4 Wavetable");
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+
+	/* create new port */
+	memset(&pcallbacks, 0, sizeof(pcallbacks));
+	pcallbacks.owner = THIS_MODULE;
+	pcallbacks.use = snd_opl4_seq_use;
+	pcallbacks.unuse = snd_opl4_seq_unuse;
+	pcallbacks.event_input = snd_opl4_seq_event_input;
+	pcallbacks.private_free = snd_opl4_seq_free_port;
+	pcallbacks.private_data = opl4;
+
+	opl4->chset->port = snd_seq_event_port_attach(client, &pcallbacks,
+						      SNDRV_SEQ_PORT_CAP_WRITE |
+						      SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
+						      SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
+						      SNDRV_SEQ_PORT_TYPE_MIDI_GM,
+						      16, 24,
+						      "OPL4 Wavetable Port");
+	if (opl4->chset->port < 0) {
+		int err = opl4->chset->port;
+		snd_midi_channel_free_set(opl4->chset);
+		snd_seq_delete_kernel_client(client);
+		opl4->seq_client = -1;
+		return err;
+	}
+	return 0;
+}
+
+static int snd_opl4_seq_delete_device(snd_seq_device_t *dev)
+{
+	opl4_t *opl4;
+
+	opl4 = *(opl4_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (!opl4)
+		return -EINVAL;
+
+	if (opl4->seq_client >= 0) {
+		snd_seq_delete_kernel_client(opl4->seq_client);
+		opl4->seq_client = -1;
+	}
+	return 0;
+}
+
+static int __init alsa_opl4_synth_init(void)
+{
+	static snd_seq_dev_ops_t ops = {
+		snd_opl4_seq_new_device,
+		snd_opl4_seq_delete_device
+	};
+
+	return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL4, &ops,
+					      sizeof(opl4_t*));
+}
+
+static void __exit alsa_opl4_synth_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL4);
+}
+
+module_init(alsa_opl4_synth_init)
+module_exit(alsa_opl4_synth_exit)
diff -urN linux-2.5.70-bk6/sound/drivers/opl4/opl4_synth.c linux-2.5.70-bk7/sound/drivers/opl4/opl4_synth.c
--- linux-2.5.70-bk6/sound/drivers/opl4/opl4_synth.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/opl4/opl4_synth.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,623 @@
+/*
+ * OPL4 MIDI synthesizer functions
+ *
+ * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed and/or modified under the
+ * terms of the GNU 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 BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include "opl4_local.h"
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <sound/asoundef.h>
+
+/* GM2 controllers */
+#ifndef MIDI_CTL_RELEASE_TIME
+#define MIDI_CTL_RELEASE_TIME	0x48
+#define MIDI_CTL_ATTACK_TIME	0x49
+#define MIDI_CTL_DECAY_TIME	0x4b
+#define MIDI_CTL_VIBRATO_RATE	0x4c
+#define MIDI_CTL_VIBRATO_DEPTH	0x4d
+#define MIDI_CTL_VIBRATO_DELAY	0x4e
+#endif
+
+/*
+ * This table maps 100/128 cents to F_NUMBER.
+ */
+static const s16 snd_opl4_pitch_map[0x600] = {
+	0x000,0x000,0x001,0x001,0x002,0x002,0x003,0x003,
+	0x004,0x004,0x005,0x005,0x006,0x006,0x006,0x007,
+	0x007,0x008,0x008,0x009,0x009,0x00a,0x00a,0x00b,
+	0x00b,0x00c,0x00c,0x00d,0x00d,0x00d,0x00e,0x00e,
+	0x00f,0x00f,0x010,0x010,0x011,0x011,0x012,0x012,
+	0x013,0x013,0x014,0x014,0x015,0x015,0x015,0x016,
+	0x016,0x017,0x017,0x018,0x018,0x019,0x019,0x01a,
+	0x01a,0x01b,0x01b,0x01c,0x01c,0x01d,0x01d,0x01e,
+	0x01e,0x01e,0x01f,0x01f,0x020,0x020,0x021,0x021,
+	0x022,0x022,0x023,0x023,0x024,0x024,0x025,0x025,
+	0x026,0x026,0x027,0x027,0x028,0x028,0x029,0x029,
+	0x029,0x02a,0x02a,0x02b,0x02b,0x02c,0x02c,0x02d,
+	0x02d,0x02e,0x02e,0x02f,0x02f,0x030,0x030,0x031,
+	0x031,0x032,0x032,0x033,0x033,0x034,0x034,0x035,
+	0x035,0x036,0x036,0x037,0x037,0x038,0x038,0x038,
+	0x039,0x039,0x03a,0x03a,0x03b,0x03b,0x03c,0x03c,
+	0x03d,0x03d,0x03e,0x03e,0x03f,0x03f,0x040,0x040,
+	0x041,0x041,0x042,0x042,0x043,0x043,0x044,0x044,
+	0x045,0x045,0x046,0x046,0x047,0x047,0x048,0x048,
+	0x049,0x049,0x04a,0x04a,0x04b,0x04b,0x04c,0x04c,
+	0x04d,0x04d,0x04e,0x04e,0x04f,0x04f,0x050,0x050,
+	0x051,0x051,0x052,0x052,0x053,0x053,0x054,0x054,
+	0x055,0x055,0x056,0x056,0x057,0x057,0x058,0x058,
+	0x059,0x059,0x05a,0x05a,0x05b,0x05b,0x05c,0x05c,
+	0x05d,0x05d,0x05e,0x05e,0x05f,0x05f,0x060,0x060,
+	0x061,0x061,0x062,0x062,0x063,0x063,0x064,0x064,
+	0x065,0x065,0x066,0x066,0x067,0x067,0x068,0x068,
+	0x069,0x069,0x06a,0x06a,0x06b,0x06b,0x06c,0x06c,
+	0x06d,0x06d,0x06e,0x06e,0x06f,0x06f,0x070,0x071,
+	0x071,0x072,0x072,0x073,0x073,0x074,0x074,0x075,
+	0x075,0x076,0x076,0x077,0x077,0x078,0x078,0x079,
+	0x079,0x07a,0x07a,0x07b,0x07b,0x07c,0x07c,0x07d,
+	0x07d,0x07e,0x07e,0x07f,0x07f,0x080,0x081,0x081,
+	0x082,0x082,0x083,0x083,0x084,0x084,0x085,0x085,
+	0x086,0x086,0x087,0x087,0x088,0x088,0x089,0x089,
+	0x08a,0x08a,0x08b,0x08b,0x08c,0x08d,0x08d,0x08e,
+	0x08e,0x08f,0x08f,0x090,0x090,0x091,0x091,0x092,
+	0x092,0x093,0x093,0x094,0x094,0x095,0x096,0x096,
+	0x097,0x097,0x098,0x098,0x099,0x099,0x09a,0x09a,
+	0x09b,0x09b,0x09c,0x09c,0x09d,0x09d,0x09e,0x09f,
+	0x09f,0x0a0,0x0a0,0x0a1,0x0a1,0x0a2,0x0a2,0x0a3,
+	0x0a3,0x0a4,0x0a4,0x0a5,0x0a6,0x0a6,0x0a7,0x0a7,
+	0x0a8,0x0a8,0x0a9,0x0a9,0x0aa,0x0aa,0x0ab,0x0ab,
+	0x0ac,0x0ad,0x0ad,0x0ae,0x0ae,0x0af,0x0af,0x0b0,
+	0x0b0,0x0b1,0x0b1,0x0b2,0x0b2,0x0b3,0x0b4,0x0b4,
+	0x0b5,0x0b5,0x0b6,0x0b6,0x0b7,0x0b7,0x0b8,0x0b8,
+	0x0b9,0x0ba,0x0ba,0x0bb,0x0bb,0x0bc,0x0bc,0x0bd,
+	0x0bd,0x0be,0x0be,0x0bf,0x0c0,0x0c0,0x0c1,0x0c1,
+	0x0c2,0x0c2,0x0c3,0x0c3,0x0c4,0x0c4,0x0c5,0x0c6,
+	0x0c6,0x0c7,0x0c7,0x0c8,0x0c8,0x0c9,0x0c9,0x0ca,
+	0x0cb,0x0cb,0x0cc,0x0cc,0x0cd,0x0cd,0x0ce,0x0ce,
+	0x0cf,0x0d0,0x0d0,0x0d1,0x0d1,0x0d2,0x0d2,0x0d3,
+	0x0d3,0x0d4,0x0d5,0x0d5,0x0d6,0x0d6,0x0d7,0x0d7,
+	0x0d8,0x0d8,0x0d9,0x0da,0x0da,0x0db,0x0db,0x0dc,
+	0x0dc,0x0dd,0x0de,0x0de,0x0df,0x0df,0x0e0,0x0e0,
+	0x0e1,0x0e1,0x0e2,0x0e3,0x0e3,0x0e4,0x0e4,0x0e5,
+	0x0e5,0x0e6,0x0e7,0x0e7,0x0e8,0x0e8,0x0e9,0x0e9,
+	0x0ea,0x0eb,0x0eb,0x0ec,0x0ec,0x0ed,0x0ed,0x0ee,
+	0x0ef,0x0ef,0x0f0,0x0f0,0x0f1,0x0f1,0x0f2,0x0f3,
+	0x0f3,0x0f4,0x0f4,0x0f5,0x0f5,0x0f6,0x0f7,0x0f7,
+	0x0f8,0x0f8,0x0f9,0x0f9,0x0fa,0x0fb,0x0fb,0x0fc,
+	0x0fc,0x0fd,0x0fd,0x0fe,0x0ff,0x0ff,0x100,0x100,
+	0x101,0x101,0x102,0x103,0x103,0x104,0x104,0x105,
+	0x106,0x106,0x107,0x107,0x108,0x108,0x109,0x10a,
+	0x10a,0x10b,0x10b,0x10c,0x10c,0x10d,0x10e,0x10e,
+	0x10f,0x10f,0x110,0x111,0x111,0x112,0x112,0x113,
+	0x114,0x114,0x115,0x115,0x116,0x116,0x117,0x118,
+	0x118,0x119,0x119,0x11a,0x11b,0x11b,0x11c,0x11c,
+	0x11d,0x11e,0x11e,0x11f,0x11f,0x120,0x120,0x121,
+	0x122,0x122,0x123,0x123,0x124,0x125,0x125,0x126,
+	0x126,0x127,0x128,0x128,0x129,0x129,0x12a,0x12b,
+	0x12b,0x12c,0x12c,0x12d,0x12e,0x12e,0x12f,0x12f,
+	0x130,0x131,0x131,0x132,0x132,0x133,0x134,0x134,
+	0x135,0x135,0x136,0x137,0x137,0x138,0x138,0x139,
+	0x13a,0x13a,0x13b,0x13b,0x13c,0x13d,0x13d,0x13e,
+	0x13e,0x13f,0x140,0x140,0x141,0x141,0x142,0x143,
+	0x143,0x144,0x144,0x145,0x146,0x146,0x147,0x148,
+	0x148,0x149,0x149,0x14a,0x14b,0x14b,0x14c,0x14c,
+	0x14d,0x14e,0x14e,0x14f,0x14f,0x150,0x151,0x151,
+	0x152,0x153,0x153,0x154,0x154,0x155,0x156,0x156,
+	0x157,0x157,0x158,0x159,0x159,0x15a,0x15b,0x15b,
+	0x15c,0x15c,0x15d,0x15e,0x15e,0x15f,0x160,0x160,
+	0x161,0x161,0x162,0x163,0x163,0x164,0x165,0x165,
+	0x166,0x166,0x167,0x168,0x168,0x169,0x16a,0x16a,
+	0x16b,0x16b,0x16c,0x16d,0x16d,0x16e,0x16f,0x16f,
+	0x170,0x170,0x171,0x172,0x172,0x173,0x174,0x174,
+	0x175,0x175,0x176,0x177,0x177,0x178,0x179,0x179,
+	0x17a,0x17a,0x17b,0x17c,0x17c,0x17d,0x17e,0x17e,
+	0x17f,0x180,0x180,0x181,0x181,0x182,0x183,0x183,
+	0x184,0x185,0x185,0x186,0x187,0x187,0x188,0x188,
+	0x189,0x18a,0x18a,0x18b,0x18c,0x18c,0x18d,0x18e,
+	0x18e,0x18f,0x190,0x190,0x191,0x191,0x192,0x193,
+	0x193,0x194,0x195,0x195,0x196,0x197,0x197,0x198,
+	0x199,0x199,0x19a,0x19a,0x19b,0x19c,0x19c,0x19d,
+	0x19e,0x19e,0x19f,0x1a0,0x1a0,0x1a1,0x1a2,0x1a2,
+	0x1a3,0x1a4,0x1a4,0x1a5,0x1a6,0x1a6,0x1a7,0x1a8,
+	0x1a8,0x1a9,0x1a9,0x1aa,0x1ab,0x1ab,0x1ac,0x1ad,
+	0x1ad,0x1ae,0x1af,0x1af,0x1b0,0x1b1,0x1b1,0x1b2,
+	0x1b3,0x1b3,0x1b4,0x1b5,0x1b5,0x1b6,0x1b7,0x1b7,
+	0x1b8,0x1b9,0x1b9,0x1ba,0x1bb,0x1bb,0x1bc,0x1bd,
+	0x1bd,0x1be,0x1bf,0x1bf,0x1c0,0x1c1,0x1c1,0x1c2,
+	0x1c3,0x1c3,0x1c4,0x1c5,0x1c5,0x1c6,0x1c7,0x1c7,
+	0x1c8,0x1c9,0x1c9,0x1ca,0x1cb,0x1cb,0x1cc,0x1cd,
+	0x1cd,0x1ce,0x1cf,0x1cf,0x1d0,0x1d1,0x1d1,0x1d2,
+	0x1d3,0x1d3,0x1d4,0x1d5,0x1d5,0x1d6,0x1d7,0x1d7,
+	0x1d8,0x1d9,0x1d9,0x1da,0x1db,0x1db,0x1dc,0x1dd,
+	0x1dd,0x1de,0x1df,0x1df,0x1e0,0x1e1,0x1e1,0x1e2,
+	0x1e3,0x1e4,0x1e4,0x1e5,0x1e6,0x1e6,0x1e7,0x1e8,
+	0x1e8,0x1e9,0x1ea,0x1ea,0x1eb,0x1ec,0x1ec,0x1ed,
+	0x1ee,0x1ee,0x1ef,0x1f0,0x1f0,0x1f1,0x1f2,0x1f3,
+	0x1f3,0x1f4,0x1f5,0x1f5,0x1f6,0x1f7,0x1f7,0x1f8,
+	0x1f9,0x1f9,0x1fa,0x1fb,0x1fb,0x1fc,0x1fd,0x1fe,
+	0x1fe,0x1ff,0x200,0x200,0x201,0x202,0x202,0x203,
+	0x204,0x205,0x205,0x206,0x207,0x207,0x208,0x209,
+	0x209,0x20a,0x20b,0x20b,0x20c,0x20d,0x20e,0x20e,
+	0x20f,0x210,0x210,0x211,0x212,0x212,0x213,0x214,
+	0x215,0x215,0x216,0x217,0x217,0x218,0x219,0x21a,
+	0x21a,0x21b,0x21c,0x21c,0x21d,0x21e,0x21e,0x21f,
+	0x220,0x221,0x221,0x222,0x223,0x223,0x224,0x225,
+	0x226,0x226,0x227,0x228,0x228,0x229,0x22a,0x22b,
+	0x22b,0x22c,0x22d,0x22d,0x22e,0x22f,0x230,0x230,
+	0x231,0x232,0x232,0x233,0x234,0x235,0x235,0x236,
+	0x237,0x237,0x238,0x239,0x23a,0x23a,0x23b,0x23c,
+	0x23c,0x23d,0x23e,0x23f,0x23f,0x240,0x241,0x241,
+	0x242,0x243,0x244,0x244,0x245,0x246,0x247,0x247,
+	0x248,0x249,0x249,0x24a,0x24b,0x24c,0x24c,0x24d,
+	0x24e,0x24f,0x24f,0x250,0x251,0x251,0x252,0x253,
+	0x254,0x254,0x255,0x256,0x257,0x257,0x258,0x259,
+	0x259,0x25a,0x25b,0x25c,0x25c,0x25d,0x25e,0x25f,
+	0x25f,0x260,0x261,0x262,0x262,0x263,0x264,0x265,
+	0x265,0x266,0x267,0x267,0x268,0x269,0x26a,0x26a,
+	0x26b,0x26c,0x26d,0x26d,0x26e,0x26f,0x270,0x270,
+	0x271,0x272,0x273,0x273,0x274,0x275,0x276,0x276,
+	0x277,0x278,0x279,0x279,0x27a,0x27b,0x27c,0x27c,
+	0x27d,0x27e,0x27f,0x27f,0x280,0x281,0x282,0x282,
+	0x283,0x284,0x285,0x285,0x286,0x287,0x288,0x288,
+	0x289,0x28a,0x28b,0x28b,0x28c,0x28d,0x28e,0x28e,
+	0x28f,0x290,0x291,0x291,0x292,0x293,0x294,0x294,
+	0x295,0x296,0x297,0x298,0x298,0x299,0x29a,0x29b,
+	0x29b,0x29c,0x29d,0x29e,0x29e,0x29f,0x2a0,0x2a1,
+	0x2a1,0x2a2,0x2a3,0x2a4,0x2a5,0x2a5,0x2a6,0x2a7,
+	0x2a8,0x2a8,0x2a9,0x2aa,0x2ab,0x2ab,0x2ac,0x2ad,
+	0x2ae,0x2af,0x2af,0x2b0,0x2b1,0x2b2,0x2b2,0x2b3,
+	0x2b4,0x2b5,0x2b5,0x2b6,0x2b7,0x2b8,0x2b9,0x2b9,
+	0x2ba,0x2bb,0x2bc,0x2bc,0x2bd,0x2be,0x2bf,0x2c0,
+	0x2c0,0x2c1,0x2c2,0x2c3,0x2c4,0x2c4,0x2c5,0x2c6,
+	0x2c7,0x2c7,0x2c8,0x2c9,0x2ca,0x2cb,0x2cb,0x2cc,
+	0x2cd,0x2ce,0x2ce,0x2cf,0x2d0,0x2d1,0x2d2,0x2d2,
+	0x2d3,0x2d4,0x2d5,0x2d6,0x2d6,0x2d7,0x2d8,0x2d9,
+	0x2da,0x2da,0x2db,0x2dc,0x2dd,0x2dd,0x2de,0x2df,
+	0x2e0,0x2e1,0x2e1,0x2e2,0x2e3,0x2e4,0x2e5,0x2e5,
+	0x2e6,0x2e7,0x2e8,0x2e9,0x2e9,0x2ea,0x2eb,0x2ec,
+	0x2ed,0x2ed,0x2ee,0x2ef,0x2f0,0x2f1,0x2f1,0x2f2,
+	0x2f3,0x2f4,0x2f5,0x2f5,0x2f6,0x2f7,0x2f8,0x2f9,
+	0x2f9,0x2fa,0x2fb,0x2fc,0x2fd,0x2fd,0x2fe,0x2ff,
+	0x300,0x301,0x302,0x302,0x303,0x304,0x305,0x306,
+	0x306,0x307,0x308,0x309,0x30a,0x30a,0x30b,0x30c,
+	0x30d,0x30e,0x30f,0x30f,0x310,0x311,0x312,0x313,
+	0x313,0x314,0x315,0x316,0x317,0x318,0x318,0x319,
+	0x31a,0x31b,0x31c,0x31c,0x31d,0x31e,0x31f,0x320,
+	0x321,0x321,0x322,0x323,0x324,0x325,0x326,0x326,
+	0x327,0x328,0x329,0x32a,0x32a,0x32b,0x32c,0x32d,
+	0x32e,0x32f,0x32f,0x330,0x331,0x332,0x333,0x334,
+	0x334,0x335,0x336,0x337,0x338,0x339,0x339,0x33a,
+	0x33b,0x33c,0x33d,0x33e,0x33e,0x33f,0x340,0x341,
+	0x342,0x343,0x343,0x344,0x345,0x346,0x347,0x348,
+	0x349,0x349,0x34a,0x34b,0x34c,0x34d,0x34e,0x34e,
+	0x34f,0x350,0x351,0x352,0x353,0x353,0x354,0x355,
+	0x356,0x357,0x358,0x359,0x359,0x35a,0x35b,0x35c,
+	0x35d,0x35e,0x35f,0x35f,0x360,0x361,0x362,0x363,
+	0x364,0x364,0x365,0x366,0x367,0x368,0x369,0x36a,
+	0x36a,0x36b,0x36c,0x36d,0x36e,0x36f,0x370,0x370,
+	0x371,0x372,0x373,0x374,0x375,0x376,0x377,0x377,
+	0x378,0x379,0x37a,0x37b,0x37c,0x37d,0x37d,0x37e,
+	0x37f,0x380,0x381,0x382,0x383,0x383,0x384,0x385,
+	0x386,0x387,0x388,0x389,0x38a,0x38a,0x38b,0x38c,
+	0x38d,0x38e,0x38f,0x390,0x391,0x391,0x392,0x393,
+	0x394,0x395,0x396,0x397,0x398,0x398,0x399,0x39a,
+	0x39b,0x39c,0x39d,0x39e,0x39f,0x39f,0x3a0,0x3a1,
+	0x3a2,0x3a3,0x3a4,0x3a5,0x3a6,0x3a7,0x3a7,0x3a8,
+	0x3a9,0x3aa,0x3ab,0x3ac,0x3ad,0x3ae,0x3ae,0x3af,
+	0x3b0,0x3b1,0x3b2,0x3b3,0x3b4,0x3b5,0x3b6,0x3b6,
+	0x3b7,0x3b8,0x3b9,0x3ba,0x3bb,0x3bc,0x3bd,0x3be,
+	0x3bf,0x3bf,0x3c0,0x3c1,0x3c2,0x3c3,0x3c4,0x3c5,
+	0x3c6,0x3c7,0x3c7,0x3c8,0x3c9,0x3ca,0x3cb,0x3cc,
+	0x3cd,0x3ce,0x3cf,0x3d0,0x3d1,0x3d1,0x3d2,0x3d3,
+	0x3d4,0x3d5,0x3d6,0x3d7,0x3d8,0x3d9,0x3da,0x3da,
+	0x3db,0x3dc,0x3dd,0x3de,0x3df,0x3e0,0x3e1,0x3e2,
+	0x3e3,0x3e4,0x3e4,0x3e5,0x3e6,0x3e7,0x3e8,0x3e9,
+	0x3ea,0x3eb,0x3ec,0x3ed,0x3ee,0x3ef,0x3ef,0x3f0,
+	0x3f1,0x3f2,0x3f3,0x3f4,0x3f5,0x3f6,0x3f7,0x3f8,
+	0x3f9,0x3fa,0x3fa,0x3fb,0x3fc,0x3fd,0x3fe,0x3ff
+};
+
+/*
+ * Attenuation according to GM recommendations, in -0.375 dB units.
+ * table[v] = 40 * log(v / 127) / -0.375
+ */
+static unsigned char snd_opl4_volume_table[128] = {
+	255,224,192,173,160,150,141,134,
+	128,122,117,113,109,105,102, 99,
+	 96, 93, 90, 88, 85, 83, 81, 79,
+	 77, 75, 73, 71, 70, 68, 67, 65,
+	 64, 62, 61, 59, 58, 57, 56, 54,
+	 53, 52, 51, 50, 49, 48, 47, 46,
+	 45, 44, 43, 42, 41, 40, 39, 39,
+	 38, 37, 36, 35, 34, 34, 33, 32,
+	 31, 31, 30, 29, 29, 28, 27, 27,
+	 26, 25, 25, 24, 24, 23, 22, 22,
+	 21, 21, 20, 19, 19, 18, 18, 17,
+	 17, 16, 16, 15, 15, 14, 14, 13,
+	 13, 12, 12, 11, 11, 10, 10,  9,
+	  9,  9,  8,  8,  7,  7,  6,  6,
+	  6,  5,  5,  4,  4,  4,  3,  3,
+	  2,  2,  2,  1,  1,  0,  0,  0
+};
+
+static void snd_opl4_write_mask(opl4_t *opl4, u8 reg, u8 mask, u8 value)
+{
+	snd_opl4_write(opl4, reg, ((snd_opl4_read(opl4, reg)) & ~mask) | (value & mask));
+}
+
+/*
+ * Initializes all voices.
+ */
+void snd_opl4_synth_reset(opl4_t *opl4)
+{
+	int i;
+
+	for (i = 0; i < OPL4_MAX_VOICES; i++)
+		snd_opl4_write(opl4, OPL4_REG_MISC + i, OPL4_DAMP_BIT);
+
+	spin_lock_init(&opl4->voices_lock);
+	INIT_LIST_HEAD(&opl4->off_voices);
+	INIT_LIST_HEAD(&opl4->on_voices);
+	memset(opl4->voices, 0, sizeof(opl4->voices));
+	for (i = 0; i < OPL4_MAX_VOICES; i++) {
+		opl4->voices[i].number = i;
+		list_add_tail(&opl4->voices[i].list, &opl4->off_voices);
+	}
+
+	snd_midi_channel_set_clear(opl4->chset);
+}
+
+/*
+ * Shuts down all voices.
+ */
+void snd_opl4_synth_shutdown(opl4_t *opl4)
+{
+	int i;
+
+	for (i = 0; i < OPL4_MAX_VOICES; i++)
+		snd_opl4_write_mask(opl4, OPL4_REG_MISC + i, OPL4_KEY_ON_BIT, 0);
+}
+
+/*
+ * Executes the callback for all voices playing the specified note.
+ */
+static void snd_opl4_do_for_note(opl4_t *opl4, int note, snd_midi_channel_t *chan,
+				 void (*func)(opl4_t *opl4, opl4_voice_t *voice))
+{
+	int i;
+	unsigned long flags;
+	opl4_voice_t *voice;
+
+	spin_lock_irqsave(&opl4->voices_lock, flags);
+	for (i = 0; i < OPL4_MAX_VOICES; i++) {
+		voice = &opl4->voices[i];
+		if (voice->chan == chan && voice->note == note) {
+			func(opl4, voice);
+		}
+	}
+	spin_unlock_irqrestore(&opl4->voices_lock, flags);
+}
+
+/*
+ * Executes the callback for all voices of to the specified channel.
+ */
+static void snd_opl4_do_for_channel(opl4_t *opl4, snd_midi_channel_t *chan,
+				    void (*func)(opl4_t *opl4, opl4_voice_t *voice))
+{
+	int i;
+	unsigned long flags;
+	opl4_voice_t *voice;
+
+	spin_lock_irqsave(&opl4->voices_lock, flags);
+	for (i = 0; i < OPL4_MAX_VOICES; i++) {
+		voice = &opl4->voices[i];
+		if (voice->chan == chan) {
+			func(opl4, voice);
+		}
+	}
+	spin_unlock_irqrestore(&opl4->voices_lock, flags);
+}
+
+/*
+ * Executes the callback for all active voices.
+ */
+static void snd_opl4_do_for_all(opl4_t *opl4,
+				void (*func)(opl4_t *opl4, opl4_voice_t *voice))
+{
+	int i;
+	unsigned long flags;
+	opl4_voice_t *voice;
+
+	spin_lock_irqsave(&opl4->voices_lock, flags);
+	for (i = 0; i < OPL4_MAX_VOICES; i++) {
+		voice = &opl4->voices[i];
+		if (voice->chan)
+			func(opl4, voice);
+	}
+	spin_unlock_irqrestore(&opl4->voices_lock, flags);
+}
+
+static void snd_opl4_update_volume(opl4_t *opl4, opl4_voice_t *voice)
+{
+	int att;
+
+	att = voice->sound->tone_attenuate;
+	att += snd_opl4_volume_table[opl4->chset->gs_master_volume & 0x7f];
+	att += snd_opl4_volume_table[voice->chan->gm_volume & 0x7f];
+	att += snd_opl4_volume_table[voice->chan->gm_expression & 0x7f];
+	att += snd_opl4_volume_table[voice->velocity];
+	att = 0x7f - (0x7f - att) * (voice->sound->volume_factor) / 0xfe - volume_boost;
+	if (att < 0)
+		att = 0;
+	else if (att > 0x7e)
+		att = 0x7e;
+	snd_opl4_write(opl4, OPL4_REG_LEVEL + voice->number, att << 1);
+}
+
+static void snd_opl4_update_pan(opl4_t *opl4, opl4_voice_t *voice)
+{
+	int pan = voice->sound->panpot;
+
+	if (!voice->chan->drum_channel)
+		pan += (voice->chan->control[MIDI_CTL_MSB_PAN] - 0x40) >> 3;
+	if (pan < -7)
+		pan = -7;
+	else if (pan > 7)
+		pan = 7;
+	voice->reg_misc = (voice->reg_misc & ~OPL4_PAN_POT_MASK)
+		| (pan & OPL4_PAN_POT_MASK);
+	snd_opl4_write(opl4, OPL4_REG_MISC + voice->number, voice->reg_misc);
+}
+
+static void snd_opl4_update_vibrato_depth(opl4_t *opl4, opl4_voice_t *voice)
+{
+	int depth;
+
+	if (voice->chan->drum_channel)
+		return;
+	depth = (7 - voice->sound->vibrato)
+		* (voice->chan->control[MIDI_CTL_VIBRATO_DEPTH] & 0x7f);
+	depth = (depth >> 7) + voice->sound->vibrato;
+	snd_opl4_write_mask(opl4, OPL4_REG_LFO_VIBRATO + voice->number,
+			    OPL4_VIBRATO_DEPTH_MASK, depth);
+}
+
+static void snd_opl4_update_pitch(opl4_t *opl4, opl4_voice_t *voice)
+{
+	snd_midi_channel_t *chan = voice->chan;
+	int note, pitch, octave;
+
+	note = chan->drum_channel ? 60 : voice->note;
+	/*
+	 * pitch is in 100/128 cents, so 0x80 is one semitone and
+	 * 0x600 is one octave.
+	 */
+	pitch = ((note - 60) << 7) * voice->sound->key_scaling / 100 + (60 << 7);
+	pitch += voice->sound->pitch_offset;
+	if (!chan->drum_channel)
+		pitch += chan->gm_rpn_coarse_tuning;
+	pitch += chan->gm_rpn_fine_tuning >> 7;
+	pitch += chan->midi_pitchbend * chan->gm_rpn_pitch_bend_range / 0x2000;
+	if (pitch < 0)
+		pitch = 0;
+	else if (pitch >= 0x6000)
+		pitch = 0x5fff;
+	octave = pitch / 0x600 - 8;
+	pitch = snd_opl4_pitch_map[pitch % 0x600];
+
+	snd_opl4_write(opl4, OPL4_REG_OCTAVE + voice->number,
+		       (octave << 4) | ((pitch >> 7) & OPL4_F_NUMBER_HIGH_MASK));
+	voice->reg_f_number = (voice->reg_f_number & OPL4_TONE_NUMBER_BIT8)
+		| ((pitch << 1) & OPL4_F_NUMBER_LOW_MASK);
+	snd_opl4_write(opl4, OPL4_REG_F_NUMBER + voice->number, voice->reg_f_number);
+}
+
+static void snd_opl4_update_tone_parameters(opl4_t *opl4, opl4_voice_t *voice)
+{
+	snd_opl4_write(opl4, OPL4_REG_LFO_VIBRATO + voice->number,
+		       voice->sound->reg_lfo_vibrato);
+	snd_opl4_write(opl4, OPL4_REG_ATTACK_DECAY1 + voice->number,
+		       voice->sound->reg_attack_decay1);
+	snd_opl4_write(opl4, OPL4_REG_LEVEL_DECAY2 + voice->number,
+		       voice->sound->reg_level_decay2);
+	snd_opl4_write(opl4, OPL4_REG_RELEASE_CORRECTION + voice->number,
+		       voice->sound->reg_release_correction);
+	snd_opl4_write(opl4, OPL4_REG_TREMOLO + voice->number,
+		       voice->sound->reg_tremolo);
+}
+
+/* allocate one voice */
+static opl4_voice_t *snd_opl4_get_voice(opl4_t *opl4)
+{
+	/* first, try to get the oldest key-off voice */
+	if (!list_empty(&opl4->off_voices))
+		return list_entry(opl4->off_voices.next, opl4_voice_t, list);
+	/* then get the oldest key-on voice */
+	snd_assert(!list_empty(&opl4->on_voices), );
+	return list_entry(opl4->on_voices.next, opl4_voice_t, list);
+}
+
+static void snd_opl4_wait_for_wave_headers(opl4_t *opl4)
+{
+	int timeout = 200;
+
+	while ((inb(opl4->fm_port) & OPL4_STATUS_LOAD) && --timeout > 0)
+		udelay(10);
+}
+
+void snd_opl4_note_on(void *private_data, int note, int vel, snd_midi_channel_t *chan)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return);
+	const opl4_region_ptr_t *regions;
+	opl4_voice_t *voice[2];
+	const opl4_sound_t *sound[2];
+	int voices = 0, i;
+	unsigned long flags;
+
+	/* determine the number of voices and voice parameters */
+	i = chan->drum_channel ? 0x80 : (chan->midi_program & 0x7f);
+	regions = &snd_yrw801_regions[i];
+	for (i = 0; i < regions->count; i++) {
+		if (note >= regions->regions[i].key_min &&
+		    note <= regions->regions[i].key_max) {
+			sound[voices] = &regions->regions[i].sound;
+			if (++voices >= 2)
+				break;
+		}
+	}
+
+	/* allocate and initialize the needed voices */
+	spin_lock_irqsave(&opl4->voices_lock, flags);
+	for (i = 0; i < voices; i++) {
+		voice[i] = snd_opl4_get_voice(opl4);
+		list_del(&voice[i]->list);
+		list_add_tail(&voice[i]->list, &opl4->on_voices);
+		voice[i]->chan = chan;
+		voice[i]->note = note;
+		voice[i]->velocity = vel & 0x7f;
+		voice[i]->sound = sound[i];
+	}
+	spin_unlock_irqrestore(&opl4->voices_lock, flags);
+
+	/* set tone number (triggers header loading) */
+	for (i = 0; i < voices; i++) {
+		voice[i]->reg_f_number =
+			(sound[i]->tone >> 8) & OPL4_TONE_NUMBER_BIT8;
+		snd_opl4_write(opl4, OPL4_REG_F_NUMBER + voice[i]->number,
+			       voice[i]->reg_f_number);
+		snd_opl4_write(opl4, OPL4_REG_TONE_NUMBER + voice[i]->number,
+			       sound[i]->tone & 0xff);
+	}
+
+	/* set parameters which can be set while loading */
+	for (i = 0; i < voices; i++) {
+		voice[i]->reg_misc = OPL4_LFO_RESET_BIT;
+		snd_opl4_update_pan(opl4, voice[i]);
+		snd_opl4_update_pitch(opl4, voice[i]);
+		snd_opl4_update_volume(opl4, voice[i]);
+	}
+
+	/* wait for completion of loading */
+	snd_opl4_wait_for_wave_headers(opl4);
+
+	/* set remaining parameters */
+	for (i = 0; i < voices; i++) {
+		snd_opl4_update_tone_parameters(opl4, voice[i]);
+		snd_opl4_update_vibrato_depth(opl4, voice[i]);
+	}
+
+	/* finally, switch on all voices */
+	for (i = 0; i < voices; i++) {
+		voice[i]->reg_misc =
+			(voice[i]->reg_misc & 0x1f) | OPL4_KEY_ON_BIT;
+		snd_opl4_write(opl4, OPL4_REG_MISC + voice[i]->number,
+			       voice[i]->reg_misc);
+	}
+}
+
+static void snd_opl4_voice_off(opl4_t *opl4, opl4_voice_t *voice)
+{
+	list_del(&voice->list);
+	list_add_tail(&voice->list, &opl4->off_voices);
+
+	voice->reg_misc &= ~OPL4_KEY_ON_BIT;
+	snd_opl4_write(opl4, OPL4_REG_MISC + voice->number, voice->reg_misc);
+}
+
+void snd_opl4_note_off(void *private_data, int note, int vel, snd_midi_channel_t *chan)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return);
+
+	snd_opl4_do_for_note(opl4, note, chan, snd_opl4_voice_off);
+}
+
+static void snd_opl4_terminate_voice(opl4_t *opl4, opl4_voice_t *voice)
+{
+	list_del(&voice->list);
+	list_add_tail(&voice->list, &opl4->off_voices);
+
+	voice->reg_misc = (voice->reg_misc & ~OPL4_KEY_ON_BIT) | OPL4_DAMP_BIT;
+	snd_opl4_write(opl4, OPL4_REG_MISC + voice->number, voice->reg_misc);
+}
+
+void snd_opl4_terminate_note(void *private_data, int note, snd_midi_channel_t *chan)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return);
+
+	snd_opl4_do_for_note(opl4, note, chan, snd_opl4_terminate_voice);
+}
+
+void snd_opl4_control(void *private_data, int type, snd_midi_channel_t *chan)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return);
+
+	switch (type) {
+	case MIDI_CTL_MSB_MODWHEEL:
+		chan->control[MIDI_CTL_VIBRATO_DEPTH] = chan->control[MIDI_CTL_MSB_MODWHEEL];
+		snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_vibrato_depth);
+		break;
+	case MIDI_CTL_MSB_MAIN_VOLUME:
+		snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_volume);
+		break;
+	case MIDI_CTL_MSB_PAN:
+		snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_pan);
+		break;
+	case MIDI_CTL_MSB_EXPRESSION:
+		snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_volume);
+		break;
+	case MIDI_CTL_VIBRATO_RATE:
+		/* not yet supported */
+		break;
+	case MIDI_CTL_VIBRATO_DEPTH:
+		snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_vibrato_depth);
+		break;
+	case MIDI_CTL_VIBRATO_DELAY:
+		/* not yet supported */
+		break;
+	case MIDI_CTL_E1_REVERB_DEPTH:
+		/*
+		 * Each OPL4 voice has a bit called "Pseudo-Reverb", but
+		 * IMHO _not_ using it enhances the listening experience.
+		 */
+		break;
+	case MIDI_CTL_PITCHBEND:
+		snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_pitch);
+		break;
+	}
+}
+
+void snd_opl4_sysex(void *private_data, unsigned char *buf, int len,
+		    int parsed, snd_midi_channel_set_t *chset)
+{
+	opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return);
+
+	if (parsed == SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME)
+		snd_opl4_do_for_all(opl4, snd_opl4_update_volume);
+}
diff -urN linux-2.5.70-bk6/sound/drivers/opl4/yrw801.c linux-2.5.70-bk7/sound/drivers/opl4/yrw801.c
--- linux-2.5.70-bk6/sound/drivers/opl4/yrw801.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/opl4/yrw801.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,961 @@
+/*
+ * Information about the Yamaha YRW801 wavetable ROM chip
+ *
+ * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed and/or modified under the
+ * terms of the GNU 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 BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include "opl4_local.h"
+
+int snd_yrw801_detect(opl4_t *opl4)
+{
+	char buf[15];
+
+	snd_opl4_read_memory(opl4, buf, 0x001200, 15);
+	if (memcmp(buf, "CopyrightYAMAHA", 15))
+		return -ENODEV;
+	snd_opl4_read_memory(opl4, buf, 0x1ffffe, 2);
+	if (buf[0] != 0x01)
+		return -ENODEV;
+	snd_printdd("YRW801 ROM version %02x.%02x\n", buf[0], buf[1]);
+	return 0;
+}
+
+/*
+ * The instrument definitions are stored statically because, in practice, the
+ * OPL4 is always coupled with a YRW801. Dynamic instrument loading would be
+ * required if downloading sample data to external SRAM was actually supported
+ * by this driver.
+ */
+
+static const opl4_region_t regions_00[] = { /* Acoustic Grand Piano */
+	{0x14, 0x27, {0x12c,7474,100, 0,0,0x00,0xc8,0x20,0xf2,0x13,0x08,0x0}},
+	{0x28, 0x2d, {0x12d,6816,100, 0,0,0x00,0xc8,0x20,0xf2,0x14,0x08,0x0}},
+	{0x2e, 0x33, {0x12e,5899,100, 0,0,0x00,0xc8,0x20,0xf2,0x14,0x08,0x0}},
+	{0x34, 0x39, {0x12f,5290,100, 0,0,0x00,0xc8,0x20,0xf2,0x14,0x08,0x0}},
+	{0x3a, 0x3f, {0x130,4260,100, 0,0,0x0a,0xc8,0x20,0xf2,0x14,0x08,0x0}},
+	{0x40, 0x45, {0x131,3625,100, 0,0,0x0a,0xc8,0x20,0xf2,0x14,0x08,0x0}},
+	{0x46, 0x4b, {0x132,3116,100, 0,0,0x04,0xc8,0x20,0xf2,0x14,0x08,0x0}},
+	{0x4c, 0x52, {0x133,2081,100, 0,0,0x03,0xc8,0x20,0xf2,0x14,0x18,0x0}},
+	{0x53, 0x58, {0x134,1444,100, 0,0,0x07,0xc8,0x20,0xf3,0x14,0x18,0x0}},
+	{0x59, 0x6d, {0x135,1915,100, 0,0,0x00,0xc8,0x20,0xf4,0x15,0x08,0x0}}
+};
+static const opl4_region_t regions_01[] = { /* Bright Acoustic Piano */
+	{0x14, 0x2d, {0x12c,7474,100, 0,0,0x00,0xc8,0x20,0xf2,0x13,0x08,0x0}},
+	{0x2e, 0x33, {0x12d,6816,100, 0,0,0x00,0xc8,0x20,0xf2,0x14,0x08,0x0}},
+	{0x34, 0x39, {0x12e,5899,100, 0,0,0x00,0xc8,0x20,0xf2,0x14,0x08,0x0}},
+	{0x3a, 0x3f, {0x12f,5290,100, 0,0,0x00,0xc8,0x20,0xf2,0x14,0x08,0x0}},
+	{0x40, 0x45, {0x130,4260,100, 0,0,0x0a,0xc8,0x20,0xf2,0x14,0x08,0x0}},
+	{0x46, 0x4b, {0x131,3625,100, 0,0,0x0a,0xc8,0x20,0xf2,0x14,0x08,0x0}},
+	{0x4c, 0x52, {0x132,3116,100, 0,0,0x04,0xc8,0x20,0xf2,0x14,0x08,0x0}},
+	{0x53, 0x58, {0x133,2081,100, 0,0,0x07,0xc8,0x20,0xf2,0x14,0x18,0x0}},
+	{0x59, 0x5e, {0x134,1444,100, 0,0,0x0a,0xc8,0x20,0xf3,0x14,0x18,0x0}},
+	{0x5f, 0x6d, {0x135,1915,100, 0,0,0x00,0xc8,0x20,0xf4,0x15,0x08,0x0}}
+};
+static const opl4_region_t regions_02[] = { /* Electric Grand Piano */
+	{0x14, 0x2d, {0x12c,7476,100, 1,0,0x00,0xae,0x20,0xf2,0x13,0x07,0x0}},
+	{0x2e, 0x33, {0x12d,6818,100, 1,0,0x00,0xae,0x20,0xf2,0x14,0x07,0x0}},
+	{0x34, 0x39, {0x12e,5901,100, 1,0,0x00,0xae,0x20,0xf2,0x14,0x07,0x0}},
+	{0x3a, 0x3f, {0x12f,5292,100, 1,0,0x00,0xae,0x20,0xf2,0x14,0x07,0x0}},
+	{0x40, 0x45, {0x130,4262,100, 1,0,0x00,0xae,0x20,0xf2,0x14,0x07,0x0}},
+	{0x46, 0x4b, {0x131,3627,100, 1,0,0x00,0xae,0x20,0xf2,0x14,0x07,0x0}},
+	{0x4c, 0x52, {0x132,3118,100, 1,0,0x00,0xae,0x20,0xf2,0x14,0x07,0x0}},
+	{0x53, 0x58, {0x133,2083,100, 1,0,0x00,0xae,0x20,0xf2,0x14,0x17,0x0}},
+	{0x59, 0x5e, {0x134,1446,100, 1,0,0x00,0xae,0x20,0xf3,0x14,0x17,0x0}},
+	{0x5f, 0x6d, {0x135,1917,100, 1,0,0x00,0xae,0x20,0xf4,0x15,0x07,0x0}},
+	{0x00, 0x7f, {0x06c,6375,100,-1,0,0x00,0xc2,0x28,0xf4,0x23,0x18,0x0}}
+};
+static const opl4_region_t regions_03[] = { /* Honky-Tonk Piano */
+	{0x14, 0x27, {0x12c,7474,100, 0,0,0x00,0xb4,0x20,0xf2,0x13,0x08,0x0}},
+	{0x28, 0x2d, {0x12d,6816,100, 0,0,0x00,0xb4,0x20,0xf2,0x14,0x08,0x0}},
+	{0x2e, 0x33, {0x12e,5899,100, 0,0,0x00,0xb4,0x20,0xf2,0x14,0x08,0x0}},
+	{0x34, 0x39, {0x12f,5290,100, 0,0,0x00,0xb4,0x20,0xf2,0x14,0x08,0x0}},
+	{0x3a, 0x3f, {0x130,4260,100, 0,0,0x0a,0xb4,0x20,0xf2,0x14,0x08,0x0}},
+	{0x40, 0x45, {0x131,3625,100, 0,0,0x0a,0xb4,0x20,0xf2,0x14,0x08,0x0}},
+	{0x46, 0x4b, {0x132,3116,100, 0,0,0x04,0xb4,0x20,0xf2,0x14,0x08,0x0}},
+	{0x4c, 0x52, {0x133,2081,100, 0,0,0x03,0xb4,0x20,0xf2,0x14,0x18,0x0}},
+	{0x53, 0x58, {0x134,1444,100, 0,0,0x07,0xb4,0x20,0xf3,0x14,0x18,0x0}},
+	{0x59, 0x6d, {0x135,1915,100, 0,0,0x00,0xb4,0x20,0xf4,0x15,0x08,0x0}},
+	{0x14, 0x27, {0x12c,7486,100, 0,0,0x00,0xb4,0x20,0xf2,0x13,0x08,0x0}},
+	{0x28, 0x2d, {0x12d,6803,100, 0,0,0x00,0xb4,0x20,0xf2,0x14,0x08,0x0}},
+	{0x2e, 0x33, {0x12e,5912,100, 0,0,0x00,0xb4,0x20,0xf2,0x14,0x08,0x0}},
+	{0x34, 0x39, {0x12f,5275,100, 0,0,0x00,0xb4,0x20,0xf2,0x14,0x08,0x0}},
+	{0x3a, 0x3f, {0x130,4274,100, 0,0,0x0a,0xb4,0x20,0xf2,0x14,0x08,0x0}},
+	{0x40, 0x45, {0x131,3611,100, 0,0,0x0a,0xb4,0x20,0xf2,0x14,0x08,0x0}},
+	{0x46, 0x4b, {0x132,3129,100, 0,0,0x04,0xb4,0x20,0xf2,0x14,0x08,0x0}},
+	{0x4c, 0x52, {0x133,2074,100, 0,0,0x07,0xb4,0x20,0xf2,0x14,0x18,0x0}},
+	{0x53, 0x58, {0x134,1457,100, 0,0,0x01,0xb4,0x20,0xf3,0x14,0x18,0x0}},
+	{0x59, 0x6d, {0x135,1903,100, 0,0,0x00,0xb4,0x20,0xf4,0x15,0x08,0x0}}
+};
+static const opl4_region_t regions_04[] = { /* Electric Piano 1 */
+	{0x15, 0x6c, {0x00b,6570,100, 0,0,0x00,0x28,0x38,0xf0,0x00,0x0c,0x0}},
+	{0x00, 0x7f, {0x06c,6375,100, 0,2,0x00,0xb0,0x22,0xf4,0x23,0x19,0x0}}
+};
+static const opl4_region_t regions_05[] = { /* Electric Piano 2 */
+	{0x14, 0x27, {0x12c,7476,100, 0,3,0x00,0xa2,0x1b,0xf2,0x13,0x08,0x0}},
+	{0x28, 0x2d, {0x12d,6818,100, 0,3,0x00,0xa2,0x1b,0xf2,0x14,0x08,0x0}},
+	{0x2e, 0x33, {0x12e,5901,100, 0,3,0x00,0xa2,0x1b,0xf2,0x14,0x08,0x0}},
+	{0x34, 0x39, {0x12f,5292,100, 0,3,0x00,0xa2,0x1b,0xf2,0x14,0x08,0x0}},
+	{0x3a, 0x3f, {0x130,4262,100, 0,3,0x0a,0xa2,0x1b,0xf2,0x14,0x08,0x0}},
+	{0x40, 0x45, {0x131,3627,100, 0,3,0x0a,0xa2,0x1b,0xf2,0x14,0x08,0x0}},
+	{0x46, 0x4b, {0x132,3118,100, 0,3,0x04,0xa2,0x1b,0xf2,0x14,0x08,0x0}},
+	{0x4c, 0x52, {0x133,2083,100, 0,3,0x03,0xa2,0x1b,0xf2,0x14,0x18,0x0}},
+	{0x53, 0x58, {0x134,1446,100, 0,3,0x07,0xa2,0x1b,0xf3,0x14,0x18,0x0}},
+	{0x59, 0x6d, {0x135,1917,100, 0,3,0x00,0xa2,0x1b,0xf4,0x15,0x08,0x0}},
+	{0x14, 0x2d, {0x12c,7472,100, 0,0,0x00,0xa2,0x18,0xf2,0x13,0x08,0x0}},
+	{0x2e, 0x33, {0x12d,6814,100, 0,0,0x00,0xa2,0x18,0xf2,0x14,0x08,0x0}},
+	{0x34, 0x39, {0x12e,5897,100, 0,0,0x00,0xa2,0x18,0xf2,0x14,0x08,0x0}},
+	{0x3a, 0x3f, {0x12f,5288,100, 0,0,0x00,0xa2,0x18,0xf2,0x14,0x08,0x0}},
+	{0x40, 0x45, {0x130,4258,100, 0,0,0x0a,0xa2,0x18,0xf2,0x14,0x08,0x0}},
+	{0x46, 0x4b, {0x131,3623,100, 0,0,0x0a,0xa2,0x18,0xf2,0x14,0x08,0x0}},
+	{0x4c, 0x52, {0x132,3114,100, 0,0,0x04,0xa2,0x18,0xf2,0x14,0x08,0x0}},
+	{0x53, 0x58, {0x133,2079,100, 0,0,0x07,0xa2,0x18,0xf2,0x14,0x18,0x0}},
+	{0x59, 0x5e, {0x134,1442,100, 0,0,0x0a,0xa2,0x18,0xf3,0x14,0x18,0x0}},
+	{0x5f, 0x6d, {0x135,1913,100, 0,0,0x00,0xa2,0x18,0xf4,0x15,0x08,0x0}}
+};
+static const opl4_region_t regions_06[] = { /* Harpsichord */
+	{0x15, 0x39, {0x080,5158,100, 0,0,0x00,0xb2,0x20,0xf5,0x24,0x19,0x0}},
+	{0x3a, 0x3f, {0x081,4408,100, 0,0,0x00,0xb2,0x20,0xf5,0x25,0x09,0x0}},
+	{0x40, 0x45, {0x082,3622,100, 0,0,0x00,0xb2,0x20,0xf5,0x25,0x09,0x0}},
+	{0x46, 0x4d, {0x083,2843,100, 0,0,0x00,0xb2,0x20,0xf5,0x25,0x19,0x0}},
+	{0x4e, 0x6c, {0x084,1307,100, 0,0,0x00,0xb2,0x20,0xf5,0x25,0x29,0x0}}
+};
+static const opl4_region_t regions_07[] = { /* Clavinet */
+	{0x15, 0x51, {0x027,5009,100, 0,0,0x00,0xd2,0x28,0xf5,0x13,0x2b,0x0}},
+	{0x52, 0x6c, {0x028,3495,100, 0,0,0x00,0xd2,0x28,0xf5,0x13,0x3b,0x0}}
+};
+static const opl4_region_t regions_08[] = { /* Celesta */
+	{0x15, 0x6c, {0x02b,3267,100, 0,0,0x00,0xdc,0x20,0xf4,0x15,0x07,0x3}}
+};
+static const opl4_region_t regions_09[] = { /* Glockenspiel */
+	{0x15, 0x78, {0x0f3, 285,100, 0,0,0x00,0xc2,0x28,0xf6,0x25,0x25,0x0}}
+};
+static const opl4_region_t regions_0a[] = { /* Music Box */
+	{0x15, 0x6c, {0x0f3,3362,100, 0,0,0x00,0xb6,0x20,0xa6,0x25,0x25,0x0}},
+	{0x15, 0x6c, {0x101,4773,100, 0,0,0x00,0xaa,0x20,0xd4,0x14,0x16,0x0}}
+};
+static const opl4_region_t regions_0b[] = { /* Vibraphone */
+	{0x15, 0x6c, {0x101,4778,100, 0,0,0x00,0xc0,0x28,0xf4,0x14,0x16,0x4}}
+};
+static const opl4_region_t regions_0c[] = { /* Marimba */
+	{0x15, 0x3f, {0x0f4,4778,100, 0,0,0x00,0xc4,0x38,0xf7,0x47,0x08,0x0}},
+	{0x40, 0x4c, {0x0f5,3217,100, 0,0,0x00,0xc4,0x38,0xf7,0x47,0x08,0x0}},
+	{0x4d, 0x5a, {0x0f5,3217,100, 0,0,0x00,0xc4,0x38,0xf7,0x48,0x08,0x0}},
+	{0x5b, 0x7f, {0x0f5,3218,100, 0,0,0x00,0xc4,0x38,0xf7,0x48,0x18,0x0}}
+};
+static const opl4_region_t regions_0d[] = { /* Xylophone */
+	{0x00, 0x7f, {0x136,1729,100, 0,0,0x00,0xd2,0x38,0xf0,0x06,0x36,0x0}}
+};
+static const opl4_region_t regions_0e[] = { /* Tubular Bell */
+	{0x01, 0x7f, {0x0ff,3999,100, 0,1,0x00,0x90,0x21,0xf4,0xa3,0x25,0x1}}
+};
+static const opl4_region_t regions_0f[] = { /* Dulcimer */
+	{0x00, 0x7f, {0x03f,4236,100, 0,1,0x00,0xbc,0x29,0xf5,0x16,0x07,0x0}},
+	{0x00, 0x7f, {0x040,4236,100, 0,2,0x0e,0x94,0x2a,0xf5,0x16,0x07,0x0}}
+};
+static const opl4_region_t regions_10[] = { /* Drawbar Organ */
+	{0x01, 0x7f, {0x08e,4394,100, 0,2,0x14,0xc2,0x3a,0xf0,0x00,0x0a,0x0}}
+};
+static const opl4_region_t regions_11[] = { /* Percussive Organ */
+	{0x15, 0x3b, {0x08c,6062,100, 0,3,0x00,0xbe,0x3b,0xf0,0x00,0x09,0x0}},
+	{0x3c, 0x6c, {0x08d,2984,100, 0,3,0x00,0xbe,0x3b,0xf0,0x00,0x09,0x0}}
+};
+static const opl4_region_t regions_12[] = { /* Rock Organ */
+	{0x15, 0x30, {0x128,6574,100, 0,1,0x00,0xcc,0x39,0xf0,0x00,0x0a,0x0}},
+	{0x31, 0x3c, {0x129,5040,100, 0,1,0x00,0xcc,0x39,0xf0,0x00,0x0a,0x0}},
+	{0x3d, 0x48, {0x12a,3498,100, 0,1,0x00,0xcc,0x39,0xf0,0x00,0x0a,0x0}},
+	{0x49, 0x54, {0x12b,1957,100, 0,1,0x00,0xcc,0x39,0xf0,0x00,0x0a,0x0}},
+	{0x55, 0x6c, {0x127, 423,100, 0,1,0x00,0xcc,0x39,0xf0,0x00,0x0a,0x0}}
+};
+static const opl4_region_t regions_13[] = { /* Church Organ */
+	{0x15, 0x29, {0x087,7466,100, 0,1,0x00,0xc4,0x11,0xf0,0x00,0x09,0x0}},
+	{0x2a, 0x30, {0x088,6456,100, 0,1,0x00,0xc4,0x11,0xf0,0x00,0x09,0x0}},
+	{0x31, 0x38, {0x089,5428,100, 0,1,0x00,0xc4,0x11,0xf0,0x00,0x09,0x0}},
+	{0x39, 0x41, {0x08a,4408,100, 0,1,0x00,0xc4,0x11,0xf0,0x00,0x09,0x0}},
+	{0x42, 0x6c, {0x08b,3406,100, 0,1,0x00,0xc4,0x11,0xf0,0x00,0x09,0x0}}
+};
+static const opl4_region_t regions_14[] = { /* Reed Organ */
+	{0x00, 0x53, {0x0ac,5570,100, 0,0,0x06,0xc0,0x38,0xf0,0x00,0x09,0x1}},
+	{0x54, 0x7f, {0x0ad,2497,100, 0,0,0x00,0xc0,0x38,0xf0,0x00,0x09,0x1}}
+};
+static const opl4_region_t regions_15[] = { /* Accordion */
+	{0x15, 0x4c, {0x006,4261,100, 0,2,0x00,0xa4,0x22,0x90,0x00,0x09,0x0}},
+	{0x4d, 0x6c, {0x007,1530,100, 0,2,0x00,0xa4,0x22,0x90,0x00,0x09,0x0}},
+	{0x15, 0x6c, {0x070,4391,100, 0,3,0x00,0x8a,0x23,0xa0,0x00,0x09,0x0}}
+};
+static const opl4_region_t regions_16[] = { /* Harmonica */
+	{0x15, 0x6c, {0x070,4408,100, 0,0,0x00,0xae,0x30,0xa0,0x00,0x09,0x2}}
+};
+static const opl4_region_t regions_17[] = { /* Tango Accordion */
+	{0x00, 0x53, {0x0ac,5573,100, 0,0,0x00,0xae,0x38,0xf0,0x00,0x09,0x0}},
+	{0x54, 0x7f, {0x0ad,2500,100, 0,0,0x00,0xae,0x38,0xf0,0x00,0x09,0x0}},
+	{0x15, 0x6c, {0x041,8479,100, 0,2,0x00,0x6a,0x3a,0x75,0x20,0x0a,0x0}}
+};
+static const opl4_region_t regions_18[] = { /* Nylon Guitar */
+	{0x15, 0x2f, {0x0b3,6964,100, 0,0,0x05,0xca,0x28,0xf5,0x34,0x09,0x0}},
+	{0x30, 0x36, {0x0b7,5567,100, 0,0,0x0c,0xca,0x28,0xf5,0x34,0x09,0x0}},
+	{0x37, 0x3c, {0x0b5,4653,100, 0,0,0x00,0xca,0x28,0xf6,0x34,0x09,0x0}},
+	{0x3d, 0x43, {0x0b4,3892,100, 0,0,0x00,0xca,0x28,0xf6,0x35,0x09,0x0}},
+	{0x44, 0x60, {0x0b6,2723,100, 0,0,0x00,0xca,0x28,0xf6,0x35,0x19,0x0}}
+};
+static const opl4_region_t regions_19[] = { /* Steel Guitar */
+	{0x15, 0x31, {0x00c,6937,100, 0,0,0x00,0xbc,0x28,0xf0,0x04,0x19,0x0}},
+	{0x32, 0x38, {0x00d,5410,100, 0,0,0x00,0xbc,0x28,0xf0,0x05,0x09,0x0}},
+	{0x39, 0x47, {0x00e,4379,100, 0,0,0x00,0xbc,0x28,0xf5,0x94,0x09,0x0}},
+	{0x48, 0x6c, {0x00f,2843,100, 0,0,0x00,0xbc,0x28,0xf6,0x95,0x09,0x0}}
+};
+static const opl4_region_t regions_1a[] = { /* Jazz Guitar */
+	{0x15, 0x31, {0x05a,6832,100, 0,0,0x00,0xca,0x28,0xf6,0x34,0x09,0x0}},
+	{0x32, 0x3f, {0x05b,4897,100, 0,0,0x00,0xca,0x28,0xf6,0x34,0x09,0x0}},
+	{0x40, 0x6c, {0x05c,3218,100, 0,0,0x00,0xca,0x28,0xf6,0x34,0x09,0x0}}
+};
+static const opl4_region_t regions_1b[] = { /* Clean Guitar */
+	{0x15, 0x2c, {0x061,7053,100, 0,1,0x00,0xb4,0x29,0xf5,0x54,0x0a,0x0}},
+	{0x2d, 0x31, {0x060,6434,100, 0,1,0x00,0xb4,0x29,0xf5,0x54,0x0a,0x0}},
+	{0x32, 0x38, {0x063,5764,100, 0,1,0x00,0xbe,0x29,0xf5,0x55,0x0a,0x0}},
+	{0x39, 0x3f, {0x062,4627,100, 0,1,0x00,0xb4,0x29,0xf5,0x55,0x0a,0x0}},
+	{0x40, 0x44, {0x065,3963,100, 0,1,0x00,0xb4,0x29,0xf5,0x55,0x1a,0x0}},
+	{0x45, 0x4b, {0x064,3313,100, 0,1,0x00,0xb4,0x29,0xf5,0x55,0x1a,0x0}},
+	{0x4c, 0x54, {0x066,2462,100, 0,1,0x00,0xb4,0x29,0xf5,0x55,0x2a,0x0}},
+	{0x55, 0x6c, {0x067,1307,100, 0,1,0x00,0xb4,0x29,0xf6,0x56,0x0a,0x0}}
+};
+static const opl4_region_t regions_1c[] = { /* Muted Guitar */
+	{0x01, 0x7f, {0x068,4408,100, 0,0,0x00,0xcc,0x28,0xf6,0x15,0x09,0x0}}
+};
+static const opl4_region_t regions_1d[] = { /* Overdriven Guitar */
+	{0x00, 0x40, {0x0a5,6589,100, 0,1,0x00,0xc0,0x29,0xf2,0x11,0x09,0x0}},
+	{0x41, 0x7f, {0x0a6,5428,100, 0,1,0x00,0xc0,0x29,0xf2,0x11,0x09,0x0}}
+};
+static const opl4_region_t regions_1e[] = { /* Distortion Guitar */
+	{0x15, 0x2a, {0x051,6928,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}},
+	{0x2b, 0x2e, {0x052,6433,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}},
+	{0x2f, 0x32, {0x053,5944,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}},
+	{0x33, 0x36, {0x054,5391,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}},
+	{0x37, 0x3a, {0x055,4897,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}},
+	{0x3b, 0x3e, {0x056,4408,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}},
+	{0x3f, 0x42, {0x057,3892,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}},
+	{0x43, 0x46, {0x058,3361,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}},
+	{0x47, 0x6c, {0x059,2784,100, 0,1,0x00,0xbc,0x21,0xa2,0x12,0x0a,0x0}}
+};
+static const opl4_region_t regions_1f[] = { /* Guitar Harmonics */
+	{0x15, 0x44, {0x05e,5499,100, 0,0,0x00,0xce,0x28,0xf4,0x24,0x09,0x0}},
+	{0x45, 0x49, {0x05d,4850,100, 0,0,0x00,0xe2,0x28,0xf4,0x24,0x09,0x0}},
+	{0x4a, 0x6c, {0x05f,4259,100, 0,0,0x00,0xce,0x28,0xf4,0x24,0x09,0x0}}
+};
+static const opl4_region_t regions_20[] = { /* Acoustic Bass */
+	{0x15, 0x30, {0x004,8053,100, 0,0,0x00,0xe2,0x18,0xf5,0x15,0x09,0x0}},
+	{0x31, 0x6c, {0x005,4754,100, 0,0,0x00,0xe2,0x18,0xf5,0x15,0x09,0x0}}
+};
+static const opl4_region_t regions_21[] = { /* Fingered Bass */
+	{0x01, 0x20, {0x04a,8762,100, 0,0,0x00,0xde,0x18,0xf6,0x14,0x09,0x0}},
+	{0x21, 0x25, {0x04b,8114,100, 0,0,0x00,0xde,0x18,0xf6,0x14,0x09,0x0}},
+	{0x26, 0x2a, {0x04c,7475,100, 0,0,0x00,0xde,0x18,0xf6,0x14,0x09,0x0}},
+	{0x2b, 0x7f, {0x04d,6841,100, 0,0,0x00,0xde,0x18,0xf6,0x14,0x09,0x0}}
+};
+static const opl4_region_t regions_22[] = { /* Picked Bass */
+	{0x15, 0x23, {0x04f,7954,100, 0,0,0x00,0xcc,0x18,0xf3,0x90,0x0a,0x0}},
+	{0x24, 0x2a, {0x050,7318,100, 0,0,0x05,0xcc,0x18,0xf3,0x90,0x1a,0x0}},
+	{0x2b, 0x2f, {0x06b,6654,100, 0,0,0x00,0xcc,0x18,0xf3,0x90,0x2a,0x0}},
+	{0x30, 0x47, {0x069,6031,100, 0,0,0x00,0xcc,0x18,0xf5,0xb0,0x0a,0x0}},
+	{0x48, 0x6c, {0x06a,5393,100, 0,0,0x00,0xcc,0x18,0xf5,0xb0,0x0a,0x0}}
+};
+static const opl4_region_t regions_23[] = { /* Fretless Bass */
+	{0x01, 0x7f, {0x04e,5297,100, 0,0,0x00,0xd2,0x10,0xf3,0x63,0x19,0x0}}
+};
+static const opl4_region_t regions_24[] = { /* Slap Bass 1 */
+	{0x15, 0x6c, {0x0a3,7606,100, 0,1,0x00,0xde,0x19,0xf5,0x32,0x1a,0x0}}
+};
+static const opl4_region_t regions_25[] = { /* Slap Bass 2 */
+	{0x01, 0x7f, {0x0a2,6694,100, 0,0,0x00,0xda,0x20,0xb0,0x02,0x09,0x0}}
+};
+static const opl4_region_t regions_26[] = { /* Synth Bass 1 */
+	{0x15, 0x6c, {0x0be,7466,100, 0,1,0x00,0xb8,0x39,0xf4,0x14,0x09,0x0}}
+};
+static const opl4_region_t regions_27[] = { /* Synth Bass 2 */
+	{0x00, 0x7f, {0x117,8103,100, 0,1,0x00,0xca,0x39,0xf3,0x50,0x08,0x0}}
+};
+static const opl4_region_t regions_28[] = { /* Violin */
+	{0x15, 0x3a, {0x105,5158,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}},
+	{0x3b, 0x3f, {0x102,4754,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}},
+	{0x40, 0x41, {0x106,4132,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}},
+	{0x42, 0x44, {0x107,4033,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}},
+	{0x45, 0x47, {0x108,3580,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}},
+	{0x48, 0x4a, {0x10a,2957,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}},
+	{0x4b, 0x4c, {0x10b,2724,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}},
+	{0x4d, 0x4e, {0x10c,2530,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}},
+	{0x4f, 0x51, {0x10d,2166,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}},
+	{0x52, 0x6c, {0x109,1825,100, 0,3,0x00,0xcc,0x3b,0xf3,0x20,0x09,0x0}}
+};
+static const opl4_region_t regions_29[] = { /* Viola */
+	{0x15, 0x32, {0x103,5780,100, 0,3,0x00,0xc4,0x3b,0xa3,0x20,0x09,0x0}},
+	{0x33, 0x35, {0x104,5534,100, 0,3,0x00,0xc4,0x3b,0xa3,0x20,0x09,0x0}},
+	{0x36, 0x38, {0x105,5158,100, 0,3,0x00,0xc4,0x3b,0xa3,0x20,0x09,0x0}},
+	{0x39, 0x3d, {0x102,4754,100, 0,3,0x00,0xca,0x3b,0xa3,0x20,0x09,0x0}},
+	{0x3e, 0x3f, {0x106,4132,100, 0,3,0x00,0xc4,0x3b,0xa3,0x20,0x09,0x0}},
+	{0x40, 0x42, {0x107,4033,100, 0,3,0x00,0xc4,0x3b,0xa3,0x20,0x09,0x0}},
+	{0x43, 0x45, {0x108,3580,100, 0,3,0x00,0xd0,0x3b,0xa3,0x20,0x09,0x0}},
+	{0x46, 0x48, {0x10a,2957,100, 0,3,0x00,0xca,0x3b,0xa3,0x20,0x09,0x0}},
+	{0x49, 0x4a, {0x10b,2724,100, 0,3,0x00,0xd0,0x3b,0xa3,0x20,0x09,0x0}},
+	{0x4b, 0x4c, {0x10c,2530,100, 0,3,0x00,0xca,0x3b,0xa3,0x20,0x09,0x0}},
+	{0x4d, 0x4f, {0x10d,2166,100, 0,3,0x00,0xd0,0x3b,0xa3,0x20,0x09,0x0}},
+	{0x50, 0x6c, {0x109,1825,100, 0,3,0x00,0xd0,0x3b,0xa3,0x20,0x09,0x0}}
+};
+static const opl4_region_t regions_2a[] = { /* Cello */
+	{0x15, 0x2d, {0x112,6545,100, 0,3,0x00,0xc0,0x33,0xa0,0x00,0x08,0x0}},
+	{0x2e, 0x37, {0x113,5764,100, 0,3,0x00,0xc0,0x33,0xa0,0x00,0x08,0x0}},
+	{0x38, 0x3e, {0x115,4378,100, 0,3,0x00,0xc0,0x33,0xa0,0x00,0x18,0x0}},
+	{0x3f, 0x44, {0x116,3998,100, 0,3,0x00,0xc0,0x33,0xa0,0x00,0x18,0x0}},
+	{0x45, 0x6c, {0x114,3218,100, 0,3,0x00,0xc0,0x33,0xa0,0x00,0x18,0x0}}
+};
+static const opl4_region_t regions_2b[] = { /* Contrabass */
+	{0x15, 0x29, {0x110,7713,100, 0,1,0x00,0xc2,0x19,0x90,0x00,0x09,0x0}},
+	{0x2a, 0x6c, {0x111,6162,100, 0,1,0x00,0xc2,0x19,0x90,0x00,0x09,0x0}}
+};
+static const opl4_region_t regions_2c[] = { /* Tremolo Strings */
+	{0x15, 0x3b, {0x0b0,4810,100, 0,0,0x0a,0xde,0x38,0xf0,0x00,0x07,0x6}},
+	{0x3c, 0x41, {0x035,4035,100, 0,0,0x05,0xde,0x38,0xf0,0x00,0x07,0x6}},
+	{0x42, 0x47, {0x033,3129,100, 0,0,0x05,0xde,0x38,0xf0,0x00,0x07,0x6}},
+	{0x48, 0x52, {0x034,2625,100, 0,0,0x05,0xde,0x38,0xf0,0x00,0x07,0x6}},
+	{0x53, 0x6c, {0x0af, 936,100, 0,0,0x00,0xde,0x38,0xf0,0x00,0x07,0x6}}
+};
+static const opl4_region_t regions_2d[] = { /* Pizzicato Strings */
+	{0x15, 0x32, {0x0b8,6186,100, 0,0,0x00,0xbc,0x28,0xf0,0x00,0x05,0x0}},
+	{0x33, 0x3b, {0x0b9,5031,100, 0,0,0x00,0xbc,0x28,0xf0,0x00,0x05,0x0}},
+	{0x3c, 0x42, {0x0bb,4146,100, 0,0,0x00,0xbc,0x28,0xf0,0x00,0x05,0x0}},
+	{0x43, 0x48, {0x0ba,3245,100, 0,0,0x00,0xc2,0x28,0xf0,0x00,0x05,0x0}},
+	{0x49, 0x6c, {0x0bc,2352,100, 0,0,0x00,0xbc,0x28,0xf0,0x00,0x05,0x0}}
+};
+static const opl4_region_t regions_2e[] = { /* Harp */
+	{0x15, 0x46, {0x07e,3740,100, 0,1,0x00,0xd2,0x29,0xf5,0x25,0x07,0x0}},
+	{0x47, 0x6c, {0x07f,2319,100, 0,1,0x00,0xd2,0x29,0xf5,0x25,0x07,0x0}}
+};
+static const opl4_region_t regions_2f[] = { /* Timpani */
+	{0x15, 0x6c, {0x100,6570,100, 0,0,0x00,0xf8,0x28,0xf0,0x05,0x16,0x0}}
+};
+static const opl4_region_t regions_30[] = { /* Strings */
+	{0x15, 0x3b, {0x13c,4806,100, 0,0,0x00,0xc8,0x20,0x80,0x00,0x07,0x0}},
+	{0x3c, 0x41, {0x13e,4035,100, 0,0,0x00,0xc8,0x20,0x80,0x00,0x07,0x0}},
+	{0x42, 0x47, {0x13d,3122,100, 0,0,0x00,0xc8,0x20,0x80,0x00,0x07,0x0}},
+	{0x48, 0x52, {0x13f,2629,100, 0,0,0x00,0xbe,0x20,0x80,0x00,0x07,0x0}},
+	{0x53, 0x6c, {0x140, 950,100, 0,0,0x00,0xbe,0x20,0x80,0x00,0x07,0x0}}
+};
+static const opl4_region_t regions_31[] = { /* Slow Strings */
+	{0x15, 0x3b, {0x0b0,4810,100, 0,1,0x0a,0xbe,0x19,0xf0,0x00,0x07,0x0}},
+	{0x3c, 0x41, {0x035,4035,100, 0,1,0x05,0xbe,0x19,0xf0,0x00,0x07,0x0}},
+	{0x42, 0x47, {0x033,3129,100, 0,1,0x05,0xbe,0x19,0xf0,0x00,0x07,0x0}},
+	{0x48, 0x52, {0x034,2625,100, 0,1,0x05,0xbe,0x19,0xf0,0x00,0x07,0x0}},
+	{0x53, 0x6c, {0x0af, 936,100, 0,1,0x00,0xbe,0x19,0xf0,0x00,0x07,0x0}}
+};
+static const opl4_region_t regions_32[] = { /* Synth Strings 1 */
+	{0x05, 0x71, {0x002,6045,100,-2,0,0x00,0xa6,0x20,0x93,0x22,0x06,0x0}},
+	{0x15, 0x6c, {0x0ae,3261,100, 2,0,0x00,0xc6,0x20,0x70,0x01,0x06,0x0}}
+};
+static const opl4_region_t regions_33[] = { /* Synth Strings 2 */
+	{0x15, 0x6c, {0x002,4513,100, 5,1,0x00,0xb4,0x19,0x70,0x00,0x06,0x0}},
+	{0x15, 0x6c, {0x002,4501,100,-5,1,0x00,0xb4,0x19,0x70,0x00,0x06,0x0}}
+};
+static const opl4_region_t regions_34[] = { /* Choir Aahs */
+	{0x15, 0x3a, {0x018,5010,100, 0,2,0x00,0xc2,0x1a,0x70,0x00,0x08,0x0}},
+	{0x3b, 0x40, {0x019,4370,100, 0,2,0x00,0xc2,0x1a,0x70,0x00,0x08,0x0}},
+	{0x41, 0x47, {0x01a,3478,100, 0,2,0x00,0xc2,0x1a,0x70,0x00,0x08,0x0}},
+	{0x48, 0x6c, {0x01b,2197,100, 0,2,0x00,0xc2,0x1a,0x70,0x00,0x08,0x0}}
+};
+static const opl4_region_t regions_35[] = { /* Voice Oohs */
+	{0x15, 0x6c, {0x029,3596,100, 0,0,0x00,0xe6,0x20,0xf7,0x20,0x08,0x0}}
+};
+static const opl4_region_t regions_36[] = { /* Synth Voice */
+	{0x15, 0x6c, {0x02a,3482,100, 0,1,0x00,0xc2,0x19,0x85,0x21,0x07,0x0}}
+};
+static const opl4_region_t regions_37[] = { /* Orchestra Hit */
+	{0x15, 0x6c, {0x049,4394,100, 0,0,0x00,0xfe,0x30,0x80,0x05,0x05,0x0}}
+};
+static const opl4_region_t regions_38[] = { /* Trumpet */
+	{0x15, 0x3c, {0x0f6,4706,100, 0,2,0x00,0xd6,0x32,0xf3,0x20,0x0a,0x0}},
+	{0x3d, 0x43, {0x0f8,3894,100, 0,2,0x00,0xd6,0x32,0xf3,0x20,0x0a,0x0}},
+	{0x44, 0x48, {0x0f7,3118,100, 0,2,0x00,0xd6,0x32,0xf3,0x20,0x0a,0x0}},
+	{0x49, 0x4e, {0x0fa,2322,100, 0,2,0x00,0xd6,0x32,0xf3,0x20,0x0a,0x0}},
+	{0x4f, 0x55, {0x0f9,1634,100, 0,2,0x00,0xd6,0x32,0xf3,0x20,0x0a,0x0}},
+	{0x56, 0x6c, {0x0fb, 786,100, 0,2,0x00,0xd6,0x32,0xf3,0x20,0x0a,0x0}}
+};
+static const opl4_region_t regions_39[] = { /* Trombone */
+	{0x15, 0x3a, {0x0f0,5053,100, 0,1,0x00,0xd6,0x21,0xf0,0x00,0x09,0x0}},
+	{0x3b, 0x3f, {0x0f1,4290,100, 0,1,0x00,0xd6,0x21,0xf0,0x00,0x09,0x0}},
+	{0x40, 0x6c, {0x0f2,3580,100, 0,1,0x00,0xd6,0x21,0xf0,0x00,0x09,0x0}}
+};
+static const opl4_region_t regions_3a[] = { /* Tuba */
+	{0x15, 0x2d, {0x085,7096,100, 0,1,0x00,0xde,0x21,0xf5,0x10,0x09,0x0}},
+	{0x2e, 0x6c, {0x086,6014,100, 0,1,0x00,0xde,0x21,0xf5,0x10,0x09,0x0}}
+};
+static const opl4_region_t regions_3b[] = { /* Muted Trumpet */
+	{0x15, 0x45, {0x0b1,4135,100, 0,0,0x00,0xcc,0x28,0xf3,0x10,0x0a,0x1}},
+	{0x46, 0x6c, {0x0b2,2599,100, 0,0,0x00,0xcc,0x28,0x83,0x10,0x0a,0x1}}
+};
+static const opl4_region_t regions_3c[] = { /* French Horns */
+	{0x15, 0x49, {0x07c,3624,100, 0,2,0x00,0xd0,0x1a,0xf0,0x00,0x09,0x0}},
+	{0x4a, 0x6c, {0x07d,2664,100, 0,2,0x00,0xd0,0x1a,0xf0,0x00,0x09,0x0}}
+};
+static const opl4_region_t regions_3d[] = { /* Brass Section */
+	{0x15, 0x42, {0x0fc,4375,100, 0,0,0x00,0xd6,0x28,0xf0,0x00,0x0a,0x0}},
+	{0x43, 0x6c, {0x0fd,2854,100, 0,0,0x00,0xd6,0x28,0xf0,0x00,0x0a,0x0}}
+};
+static const opl4_region_t regions_3e[] = { /* Synth Brass 1 */
+	{0x01, 0x27, {0x0d3,9094,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}},
+	{0x28, 0x2d, {0x0da,8335,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}},
+	{0x2e, 0x33, {0x0d4,7558,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}},
+	{0x34, 0x39, {0x0db,6785,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}},
+	{0x3a, 0x3f, {0x0d5,6042,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}},
+	{0x40, 0x45, {0x0dc,5257,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}},
+	{0x46, 0x4b, {0x0d6,4493,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}},
+	{0x4c, 0x51, {0x0dd,3741,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}},
+	{0x52, 0x57, {0x0d7,3012,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}},
+	{0x58, 0x5d, {0x0de,2167,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}},
+	{0x5e, 0x63, {0x0d8,1421,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}},
+	{0x64, 0x7f, {0x0d9,-115,100,-1,0,0x00,0xbe,0x18,0xa5,0x11,0x08,0x0}},
+	{0x01, 0x27, {0x118,9103,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}},
+	{0x28, 0x2d, {0x119,8340,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}},
+	{0x2e, 0x33, {0x11a,7565,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}},
+	{0x34, 0x39, {0x11b,6804,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}},
+	{0x3a, 0x3f, {0x11c,6042,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}},
+	{0x40, 0x45, {0x11d,5277,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}},
+	{0x46, 0x4b, {0x11e,4520,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}},
+	{0x4c, 0x51, {0x11f,3741,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}},
+	{0x52, 0x57, {0x120,3012,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}},
+	{0x58, 0x5d, {0x121,2166,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}},
+	{0x5e, 0x64, {0x122,1421,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}},
+	{0x65, 0x7f, {0x123,-115,100, 1,1,0x00,0xbe,0x19,0x85,0x23,0x08,0x0}}
+};
+static const opl4_region_t regions_3f[] = { /* Synth Brass 2 */
+	{0x01, 0x27, {0x118,9113,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}},
+	{0x28, 0x2d, {0x119,8350,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}},
+	{0x2e, 0x33, {0x11a,7575,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}},
+	{0x34, 0x39, {0x11b,6814,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}},
+	{0x3a, 0x3f, {0x11c,6052,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}},
+	{0x40, 0x45, {0x11d,5287,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}},
+	{0x46, 0x4b, {0x11e,4530,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}},
+	{0x4c, 0x51, {0x11f,3751,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}},
+	{0x52, 0x57, {0x120,3022,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}},
+	{0x58, 0x5d, {0x121,2176,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}},
+	{0x5e, 0x64, {0x122,1431,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}},
+	{0x65, 0x7f, {0x123,-105,100, 3,6,0x00,0xae,0x26,0x85,0x23,0x08,0x0}},
+	{0x00, 0x7f, {0x124,4034,100,-3,2,0x00,0xea,0x22,0x85,0x23,0x08,0x0}}
+};
+static const opl4_region_t regions_40[] = { /* Soprano Sax */
+	{0x15, 0x3f, {0x0e3,4228,100, 0,1,0x00,0xc8,0x21,0xf5,0x20,0x0a,0x0}},
+	{0x40, 0x45, {0x0e4,3495,100, 0,1,0x00,0xc8,0x21,0xf5,0x20,0x0a,0x0}},
+	{0x46, 0x4b, {0x0e5,2660,100, 0,1,0x00,0xd6,0x21,0xf5,0x20,0x0a,0x0}},
+	{0x4c, 0x51, {0x0e6,2002,100, 0,1,0x00,0xd6,0x21,0xf5,0x20,0x0a,0x0}},
+	{0x52, 0x59, {0x0e7,1186,100, 0,1,0x00,0xd6,0x21,0xf5,0x20,0x0a,0x0}},
+	{0x59, 0x6c, {0x0e8,1730,100, 0,1,0x00,0xc8,0x21,0xf5,0x20,0x0a,0x0}}
+};
+static const opl4_region_t regions_41[] = { /* Alto Sax */
+	{0x15, 0x32, {0x092,6204,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}},
+	{0x33, 0x35, {0x096,5812,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}},
+	{0x36, 0x3a, {0x099,5318,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}},
+	{0x3b, 0x3b, {0x08f,5076,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}},
+	{0x3c, 0x3e, {0x093,4706,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}},
+	{0x3f, 0x41, {0x097,4321,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}},
+	{0x42, 0x44, {0x09a,3893,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}},
+	{0x45, 0x47, {0x090,3497,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}},
+	{0x48, 0x4a, {0x094,3119,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}},
+	{0x4b, 0x4d, {0x098,2726,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}},
+	{0x4e, 0x50, {0x09b,2393,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}},
+	{0x51, 0x53, {0x091,2088,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}},
+	{0x54, 0x6c, {0x095,1732,100, 0,1,0x00,0xbe,0x19,0xf5,0x20,0x0b,0x0}}
+};
+static const opl4_region_t regions_42[] = { /* Tenor Sax */
+	{0x24, 0x30, {0x0e9,6301,100, 0,1,0x00,0xbc,0x19,0xf4,0x10,0x0b,0x0}},
+	{0x31, 0x34, {0x0ea,5781,100, 0,1,0x00,0xbc,0x19,0xf4,0x10,0x0b,0x0}},
+	{0x35, 0x3a, {0x0eb,5053,100, 0,1,0x00,0xbc,0x19,0xf4,0x10,0x0b,0x0}},
+	{0x3b, 0x41, {0x0ed,4165,100, 0,1,0x00,0xbc,0x19,0xf4,0x10,0x0b,0x0}},
+	{0x42, 0x47, {0x0ec,3218,100, 0,1,0x00,0xbc,0x19,0xf4,0x10,0x0b,0x0}},
+	{0x48, 0x51, {0x0ee,2462,100, 0,1,0x00,0xbc,0x19,0xf4,0x10,0x0b,0x0}},
+	{0x52, 0x6c, {0x0ef,1421,100, 0,1,0x00,0xbc,0x19,0xf4,0x10,0x0b,0x0}}
+};
+static const opl4_region_t regions_43[] = { /* Baritone Sax */
+	{0x15, 0x2d, {0x0df,6714,100, 0,1,0x00,0xce,0x19,0xf0,0x00,0x0a,0x0}},
+	{0x2e, 0x34, {0x0e1,5552,100, 0,1,0x00,0xce,0x19,0xf0,0x00,0x0a,0x0}},
+	{0x35, 0x39, {0x0e2,5178,100, 0,1,0x00,0xce,0x19,0xf0,0x00,0x0a,0x0}},
+	{0x3a, 0x6c, {0x0e0,4437,100, 0,1,0x00,0xce,0x19,0xf0,0x00,0x0a,0x0}}
+};
+static const opl4_region_t regions_44[] = { /* Oboe */
+	{0x15, 0x3c, {0x042,4493,100, 0,1,0x00,0xe6,0x39,0xf4,0x10,0x0a,0x0}},
+	{0x3d, 0x43, {0x044,3702,100, 0,1,0x00,0xdc,0x39,0xf4,0x10,0x0a,0x0}},
+	{0x44, 0x49, {0x043,2956,100, 0,1,0x00,0xdc,0x39,0xf4,0x10,0x0a,0x0}},
+	{0x4a, 0x4f, {0x046,2166,100, 0,1,0x00,0xdc,0x39,0xf4,0x10,0x0a,0x0}},
+	{0x50, 0x55, {0x045,1420,100, 0,1,0x00,0xdc,0x39,0xf4,0x10,0x0a,0x0}},
+	{0x56, 0x6c, {0x047, 630,100, 0,1,0x00,0xe6,0x39,0xf4,0x10,0x0a,0x0}}
+};
+static const opl4_region_t regions_45[] = { /* English Horn */
+	{0x15, 0x38, {0x03c,5098,100, 0,1,0x00,0xc4,0x31,0xf0,0x00,0x09,0x0}},
+	{0x39, 0x3e, {0x03b,4291,100, 0,1,0x00,0xc4,0x31,0xf0,0x00,0x09,0x0}},
+	{0x3f, 0x6c, {0x03d,3540,100, 0,1,0x00,0xc4,0x31,0xf0,0x00,0x09,0x0}}
+};
+static const opl4_region_t regions_46[] = { /* Bassoon */
+	{0x15, 0x22, {0x038,7833,100, 0,1,0x00,0xc6,0x31,0xf0,0x00,0x0b,0x0}},
+	{0x23, 0x2e, {0x03a,7070,100, 0,1,0x00,0xc6,0x31,0xf0,0x00,0x0b,0x0}},
+	{0x2f, 0x6c, {0x039,6302,100, 0,1,0x00,0xc6,0x31,0xf0,0x00,0x0b,0x0}}
+};
+static const opl4_region_t regions_47[] = { /* Clarinet */
+	{0x15, 0x3b, {0x09e,5900,100, 0,1,0x00,0xc8,0x29,0xf3,0x20,0x0a,0x0}},
+	{0x3c, 0x41, {0x0a0,5158,100, 0,1,0x00,0xc8,0x29,0xf3,0x20,0x0a,0x0}},
+	{0x42, 0x4a, {0x09f,4260,100, 0,1,0x00,0xc8,0x29,0xf3,0x20,0x0a,0x0}},
+	{0x4b, 0x6c, {0x0a1,2957,100, 0,1,0x00,0xc8,0x29,0xf3,0x20,0x0a,0x0}}
+};
+static const opl4_region_t regions_48[] = { /* Piccolo */
+	{0x15, 0x40, {0x071,4803,100, 0,0,0x00,0xe6,0x38,0xf0,0x00,0x0a,0x2}},
+	{0x41, 0x4d, {0x072,3314,100, 0,0,0x00,0xe6,0x38,0xf0,0x00,0x0a,0x2}},
+	{0x4e, 0x53, {0x073,1731,100, 0,0,0x00,0xe6,0x38,0xf0,0x00,0x0a,0x2}},
+	{0x54, 0x5f, {0x074,2085,100, 0,0,0x00,0xe6,0x38,0xf0,0x00,0x0a,0x2}},
+	{0x60, 0x6c, {0x075,1421,100, 0,0,0x00,0xe6,0x38,0xf0,0x00,0x0a,0x2}}
+};
+static const opl4_region_t regions_49[] = { /* Flute */
+	{0x15, 0x40, {0x071,4803,100, 0,0,0x00,0xdc,0x38,0xf0,0x00,0x0a,0x2}},
+	{0x41, 0x4d, {0x072,3314,100, 0,0,0x00,0xdc,0x38,0xf0,0x00,0x0a,0x2}},
+	{0x4e, 0x6c, {0x073,1731,100, 0,0,0x00,0xe6,0x38,0xf0,0x00,0x0a,0x2}}
+};
+static const opl4_region_t regions_4a[] = { /* Recorder */
+	{0x15, 0x6f, {0x0bd,4897,100, 0,0,0x00,0xec,0x30,0x70,0x00,0x09,0x1}}
+};
+static const opl4_region_t regions_4b[] = { /* Pan Flute */
+	{0x15, 0x6c, {0x077,2359,100, 0,0,0x00,0xde,0x38,0xf0,0x00,0x09,0x3}}
+};
+static const opl4_region_t regions_4c[] = { /* Bottle Blow */
+	{0x15, 0x6c, {0x077,2359,100, 0,0,0x00,0xc8,0x38,0xf0,0x00,0x09,0x1}},
+	{0x01, 0x7f, {0x125,7372,100, 0,0,0x1e,0x80,0x00,0xf0,0x00,0x09,0x0}}
+};
+static const opl4_region_t regions_4d[] = { /* Shakuhachi */
+	{0x00, 0x7f, {0x0ab,4548,100, 0,0,0x00,0xd6,0x30,0xf0,0x00,0x0a,0x3}},
+	{0x15, 0x6c, {0x076,3716,100, 0,0,0x00,0xa2,0x28,0x70,0x00,0x09,0x2}}
+};
+static const opl4_region_t regions_4e[] = { /* Whistle */
+	{0x00, 0x7f, {0x0aa,1731,100, 0,4,0x00,0xd2,0x2c,0x70,0x00,0x0a,0x0}}
+};
+static const opl4_region_t regions_4f[] = { /* Ocarina */
+	{0x00, 0x7f, {0x0aa,1731,100, 0,1,0x00,0xce,0x29,0x90,0x00,0x0a,0x1}}
+};
+static const opl4_region_t regions_50[] = { /* Square Lead */
+	{0x01, 0x2a, {0x0cc,9853,100, 3,0,0x00,0xac,0x38,0xc6,0x21,0x09,0x0}},
+	{0x2b, 0x36, {0x0cd,6785,100, 3,0,0x00,0xac,0x38,0xc6,0x21,0x09,0x0}},
+	{0x37, 0x42, {0x0ca,5248,100, 3,0,0x00,0xac,0x38,0xc6,0x21,0x09,0x0}},
+	{0x43, 0x4e, {0x0cf,3713,100, 3,0,0x00,0xac,0x38,0xc6,0x21,0x09,0x0}},
+	{0x4f, 0x5a, {0x0ce,2176,100, 3,0,0x00,0xac,0x38,0xc6,0x21,0x09,0x0}},
+	{0x5b, 0x7f, {0x0cb, 640,100, 3,0,0x00,0xac,0x38,0xc6,0x21,0x09,0x0}},
+	{0x01, 0x2a, {0x0cc,9844,100,-3,0,0x00,0xac,0x08,0xc6,0x21,0x09,0x0}},
+	{0x2b, 0x36, {0x0cd,6776,100,-3,0,0x00,0xac,0x08,0xc6,0x21,0x09,0x0}},
+	{0x37, 0x42, {0x0ca,5239,100,-3,0,0x00,0xac,0x08,0xc6,0x21,0x09,0x0}},
+	{0x43, 0x4e, {0x0cf,3704,100,-3,0,0x00,0xac,0x08,0xc6,0x21,0x09,0x0}},
+	{0x4f, 0x5a, {0x0ce,2167,100,-3,0,0x00,0xac,0x08,0xc6,0x21,0x09,0x0}},
+	{0x5b, 0x7f, {0x0cb, 631,100,-3,0,0x00,0xac,0x08,0xc6,0x21,0x09,0x0}}
+};
+static const opl4_region_t regions_51[] = { /* Sawtooth Lead */
+	{0x01, 0x27, {0x118,9108,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x28, 0x2d, {0x119,8345,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x2e, 0x33, {0x11a,7570,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x34, 0x39, {0x11b,6809,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x3a, 0x3f, {0x11c,6047,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x40, 0x45, {0x11d,5282,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x46, 0x4b, {0x11e,4525,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x4c, 0x51, {0x11f,3746,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x52, 0x57, {0x120,3017,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x58, 0x5d, {0x121,2171,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x5e, 0x66, {0x122,1426,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x67, 0x7f, {0x123,-110,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x01, 0x27, {0x118,9098,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x28, 0x2d, {0x119,8335,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x2e, 0x33, {0x11a,7560,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x34, 0x39, {0x11b,6799,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x3a, 0x3f, {0x11c,6037,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x40, 0x45, {0x11d,5272,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x46, 0x4b, {0x11e,4515,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x4c, 0x51, {0x11f,3736,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x52, 0x57, {0x120,3007,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x58, 0x5d, {0x121,2161,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x5e, 0x66, {0x122,1416,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}},
+	{0x67, 0x7f, {0x123,-120,100, 0,0,0x00,0xc8,0x30,0xf2,0x22,0x0a,0x0}}
+};
+static const opl4_region_t regions_52[] = { /* Calliope Lead */
+	{0x00, 0x7f, {0x0aa,1731,100, 0,0,0x00,0xc2,0x28,0x90,0x00,0x0a,0x2}},
+	{0x15, 0x6c, {0x076,3716,100, 0,0,0x00,0xb6,0x28,0xb0,0x00,0x09,0x2}}
+};
+static const opl4_region_t regions_53[] = { /* Chiffer Lead */
+	{0x00, 0x7f, {0x13a,3665,100, 0,2,0x00,0xcc,0x2a,0xf0,0x10,0x09,0x1}},
+	{0x01, 0x7f, {0x0fe,3660,100, 0,0,0x00,0xbe,0x28,0xf3,0x10,0x17,0x0}}
+};
+static const opl4_region_t regions_54[] = { /* Charang Lead */
+	{0x00, 0x40, {0x0a5,6594,100, 0,3,0x00,0xba,0x33,0xf2,0x11,0x09,0x0}},
+	{0x41, 0x7f, {0x0a6,5433,100, 0,3,0x00,0xba,0x33,0xf2,0x11,0x09,0x0}},
+	{0x01, 0x27, {0x118,9098,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}},
+	{0x28, 0x2d, {0x119,8335,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}},
+	{0x2e, 0x33, {0x11a,7560,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}},
+	{0x34, 0x39, {0x11b,6799,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}},
+	{0x3a, 0x3f, {0x11c,6037,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}},
+	{0x40, 0x45, {0x11d,5272,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}},
+	{0x46, 0x4b, {0x11e,4515,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}},
+	{0x4c, 0x51, {0x11f,3736,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}},
+	{0x52, 0x57, {0x120,3007,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}},
+	{0x58, 0x5d, {0x121,2161,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}},
+	{0x5e, 0x66, {0x122,1416,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}},
+	{0x67, 0x7f, {0x123,-120,100, 0,2,0x00,0xa4,0x2a,0xf2,0x22,0x0e,0x0}}
+};
+static const opl4_region_t regions_55[] = { /* Voice Lead */
+	{0x00, 0x7f, {0x0aa,1739,100, 0,6,0x00,0x8c,0x2e,0x90,0x00,0x0a,0x0}},
+	{0x15, 0x6c, {0x02a,3474,100, 0,1,0x00,0xd8,0x29,0xf0,0x05,0x0a,0x0}}
+};
+static const opl4_region_t regions_56[] = { /* 5ths Lead */
+	{0x01, 0x27, {0x118,8468,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}},
+	{0x28, 0x2d, {0x119,7705,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}},
+	{0x2e, 0x33, {0x11a,6930,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}},
+	{0x34, 0x39, {0x11b,6169,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}},
+	{0x3a, 0x3f, {0x11c,5407,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}},
+	{0x40, 0x45, {0x11d,4642,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}},
+	{0x46, 0x4b, {0x11e,3885,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}},
+	{0x4c, 0x51, {0x11f,3106,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}},
+	{0x52, 0x57, {0x120,2377,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}},
+	{0x58, 0x5d, {0x121,1531,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}},
+	{0x5e, 0x64, {0x122, 786,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}},
+	{0x65, 0x7f, {0x123,-750,100, 0,2,0x00,0xd0,0x32,0xf5,0x20,0x08,0x0}},
+	{0x05, 0x71, {0x002,4503,100, 0,1,0x00,0xb8,0x31,0xb3,0x20,0x0b,0x0}}
+};
+static const opl4_region_t regions_57[] = { /* Bass & Lead */
+	{0x00, 0x7f, {0x117,8109,100, 0,1,0x00,0xbc,0x29,0xf3,0x50,0x08,0x0}},
+	{0x01, 0x27, {0x118,9097,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}},
+	{0x28, 0x2d, {0x119,8334,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}},
+	{0x2e, 0x33, {0x11a,7559,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}},
+	{0x34, 0x39, {0x11b,6798,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}},
+	{0x3a, 0x3f, {0x11c,6036,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}},
+	{0x40, 0x45, {0x11d,5271,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}},
+	{0x46, 0x4b, {0x11e,4514,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}},
+	{0x4c, 0x51, {0x11f,3735,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}},
+	{0x52, 0x57, {0x120,3006,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}},
+	{0x58, 0x5d, {0x121,2160,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}},
+	{0x5e, 0x66, {0x122,1415,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}},
+	{0x67, 0x7f, {0x123,-121,100, 0,2,0x00,0xbc,0x2a,0xf2,0x20,0x0a,0x0}}
+};
+static const opl4_region_t regions_58[] = { /* New Age Pad */
+	{0x15, 0x6c, {0x002,4501,100, 0,4,0x00,0xa4,0x24,0x80,0x01,0x05,0x0}},
+	{0x15, 0x6c, {0x0f3,4253,100, 0,3,0x00,0x8c,0x23,0xa2,0x14,0x06,0x1}}
+};
+static const opl4_region_t regions_59[] = { /* Warm Pad */
+	{0x15, 0x6c, {0x04e,5306,100, 2,2,0x00,0x92,0x2a,0x34,0x23,0x05,0x2}},
+	{0x15, 0x6c, {0x029,3575,100,-2,2,0x00,0xbe,0x22,0x31,0x23,0x06,0x0}}
+};
+static const opl4_region_t regions_5a[] = { /* Polysynth Pad */
+	{0x01, 0x27, {0x118,9111,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}},
+	{0x28, 0x2d, {0x119,8348,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}},
+	{0x2e, 0x33, {0x11a,7573,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}},
+	{0x34, 0x39, {0x11b,6812,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}},
+	{0x3a, 0x3f, {0x11c,6050,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}},
+	{0x40, 0x45, {0x11d,5285,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}},
+	{0x46, 0x4b, {0x11e,4528,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}},
+	{0x4c, 0x51, {0x11f,3749,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}},
+	{0x52, 0x57, {0x120,3020,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}},
+	{0x58, 0x5d, {0x121,2174,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}},
+	{0x5e, 0x66, {0x122,1429,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}},
+	{0x67, 0x7f, {0x123,-107,100, 0,3,0x00,0xae,0x23,0xf2,0x20,0x07,0x1}},
+	{0x00, 0x7f, {0x124,4024,100, 0,2,0x00,0xae,0x22,0xe5,0x20,0x08,0x0}}
+};
+static const opl4_region_t regions_5b[] = { /* Choir Pad */
+	{0x15, 0x3a, {0x018,5010,100, 0,5,0x00,0xb0,0x25,0x70,0x00,0x06,0x0}},
+	{0x3b, 0x40, {0x019,4370,100, 0,5,0x00,0xb0,0x25,0x70,0x00,0x06,0x0}},
+	{0x41, 0x47, {0x01a,3478,100, 0,5,0x00,0xb0,0x25,0x70,0x00,0x06,0x0}},
+	{0x48, 0x6c, {0x01b,2197,100, 0,5,0x00,0xb0,0x25,0x70,0x00,0x06,0x0}},
+	{0x15, 0x6c, {0x02a,3482,100, 0,4,0x00,0x98,0x24,0x65,0x21,0x06,0x0}}
+};
+static const opl4_region_t regions_5c[] = { /* Bowed Pad */
+	{0x15, 0x6c, {0x101,4790,100,-1,1,0x00,0xbe,0x19,0x44,0x14,0x16,0x0}},
+	{0x00, 0x7f, {0x0aa,1720,100, 1,1,0x00,0x94,0x19,0x40,0x00,0x06,0x0}}
+};
+static const opl4_region_t regions_5d[] = { /* Metallic Pad */
+	{0x15, 0x31, {0x00c,6943,100, 0,2,0x00,0xa0,0x0a,0x60,0x03,0x06,0x0}},
+	{0x32, 0x38, {0x00d,5416,100, 0,2,0x00,0xa0,0x0a,0x60,0x03,0x06,0x0}},
+	{0x39, 0x47, {0x00e,4385,100, 0,2,0x00,0xa0,0x0a,0x60,0x03,0x06,0x0}},
+	{0x48, 0x6c, {0x00f,2849,100, 0,2,0x00,0xa0,0x0a,0x60,0x03,0x06,0x0}},
+	{0x00, 0x7f, {0x03f,4224,100, 0,1,0x00,0x9c,0x31,0x65,0x16,0x07,0x0}}
+};
+static const opl4_region_t regions_5e[] = { /* Halo Pad */
+	{0x00, 0x7f, {0x124,4038,100, 0,2,0x00,0xa6,0x1a,0x85,0x23,0x08,0x0}},
+	{0x15, 0x6c, {0x02a,3471,100, 0,3,0x00,0xc0,0x1b,0xc0,0x05,0x06,0x0}}
+};
+static const opl4_region_t regions_5f[] = { /* Sweep Pad */
+	{0x01, 0x27, {0x0d3,9100,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}},
+	{0x28, 0x2d, {0x0da,8341,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}},
+	{0x2e, 0x33, {0x0d4,7564,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}},
+	{0x34, 0x39, {0x0db,6791,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}},
+	{0x3a, 0x3f, {0x0d5,6048,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}},
+	{0x40, 0x45, {0x0dc,5263,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}},
+	{0x46, 0x4b, {0x0d6,4499,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}},
+	{0x4c, 0x51, {0x0dd,3747,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}},
+	{0x52, 0x57, {0x0d7,3018,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}},
+	{0x58, 0x5d, {0x0de,2173,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}},
+	{0x5e, 0x63, {0x0d8,1427,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}},
+	{0x64, 0x7f, {0x0d9,-109,100, 0,1,0x00,0xce,0x19,0x13,0x11,0x06,0x0}},
+	{0x01, 0x27, {0x0d3,9088,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}},
+	{0x28, 0x2d, {0x0da,8329,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}},
+	{0x2e, 0x33, {0x0d4,7552,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}},
+	{0x34, 0x39, {0x0db,6779,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}},
+	{0x3a, 0x3f, {0x0d5,6036,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}},
+	{0x40, 0x45, {0x0dc,5251,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}},
+	{0x46, 0x4b, {0x0d6,4487,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}},
+	{0x4c, 0x51, {0x0dd,3735,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}},
+	{0x52, 0x57, {0x0d7,3006,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}},
+	{0x58, 0x5d, {0x0de,2161,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}},
+	{0x5e, 0x63, {0x0d8,1415,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}},
+	{0x64, 0x7f, {0x0d9,-121,100, 0,0,0x00,0xce,0x18,0x13,0x11,0x06,0x0}}
+};
+static const opl4_region_t regions_60[] = { /* Ice Rain */
+	{0x01, 0x7f, {0x04e,9345,100, 0,2,0x00,0xcc,0x22,0xa3,0x63,0x17,0x0}},
+	{0x00, 0x7f, {0x143,5586, 20, 0,2,0x00,0x6e,0x2a,0xf0,0x05,0x05,0x0}}
+};
+static const opl4_region_t regions_61[] = { /* Soundtrack */
+	{0x15, 0x6c, {0x002,4501,100, 0,2,0x00,0xb6,0x2a,0x60,0x01,0x05,0x0}},
+	{0x15, 0x6c, {0x0f3,1160,100, 0,5,0x00,0xa8,0x2d,0x52,0x14,0x06,0x2}}
+};
+static const opl4_region_t regions_62[] = { /* Crystal */
+	{0x15, 0x6c, {0x0f3,1826,100, 0,3,0x00,0xb8,0x33,0xf6,0x25,0x25,0x0}},
+	{0x15, 0x2c, {0x06d,7454,100, 0,3,0x00,0xac,0x3b,0x85,0x24,0x06,0x0}},
+	{0x2d, 0x36, {0x06e,5925,100, 0,3,0x00,0xac,0x3b,0x85,0x24,0x06,0x0}},
+	{0x37, 0x6c, {0x06f,4403,100, 0,3,0x09,0xac,0x3b,0x85,0x24,0x06,0x0}}
+};
+static const opl4_region_t regions_63[] = { /* Atmosphere */
+	{0x05, 0x71, {0x002,4509,100, 0,2,0x00,0xc8,0x32,0x73,0x22,0x06,0x1}},
+	{0x15, 0x2f, {0x0b3,6964,100, 0,2,0x05,0xc2,0x32,0xf5,0x34,0x07,0x2}},
+	{0x30, 0x36, {0x0b7,5567,100, 0,2,0x0c,0xc2,0x32,0xf5,0x34,0x07,0x2}},
+	{0x37, 0x3c, {0x0b5,4653,100, 0,2,0x00,0xc2,0x32,0xf6,0x34,0x07,0x2}},
+	{0x3d, 0x43, {0x0b4,3892,100, 0,2,0x00,0xc2,0x32,0xf6,0x35,0x07,0x2}},
+	{0x44, 0x60, {0x0b6,2723,100, 0,2,0x00,0xc2,0x32,0xf6,0x35,0x17,0x2}}
+};
+static const opl4_region_t regions_64[] = { /* Brightness */
+	{0x00, 0x7f, {0x137,5285,100, 0,2,0x00,0xbe,0x2a,0xa5,0x18,0x08,0x0}},
+	{0x15, 0x6c, {0x02a,3481,100, 0,1,0x00,0xc8,0x29,0x80,0x05,0x05,0x0}}
+};
+static const opl4_region_t regions_65[] = { /* Goblins */
+	{0x15, 0x6c, {0x002,4501,100,-1,2,0x00,0xca,0x2a,0x40,0x01,0x05,0x0}},
+	{0x15, 0x6c, {0x009,9679, 20, 1,4,0x00,0x3c,0x0c,0x22,0x11,0x06,0x0}}
+};
+static const opl4_region_t regions_66[] = { /* Echoes */
+	{0x15, 0x6c, {0x02a,3487,100, 0,3,0x00,0xae,0x2b,0xf5,0x21,0x06,0x0}},
+	{0x00, 0x7f, {0x124,4027,100, 0,3,0x00,0xae,0x2b,0x85,0x23,0x07,0x0}}
+};
+static const opl4_region_t regions_67[] = { /* Sci-Fi */
+	{0x15, 0x31, {0x00c,6940,100, 0,3,0x00,0xc8,0x2b,0x90,0x05,0x06,0x3}},
+	{0x32, 0x38, {0x00d,5413,100, 0,3,0x00,0xc8,0x2b,0x90,0x05,0x06,0x3}},
+	{0x39, 0x47, {0x00e,4382,100, 0,3,0x00,0xc8,0x2b,0x90,0x05,0x06,0x3}},
+	{0x48, 0x6c, {0x00f,2846,100, 0,3,0x00,0xc8,0x2b,0x90,0x05,0x06,0x3}},
+	{0x15, 0x6c, {0x002,4498,100, 0,2,0x00,0xd4,0x22,0x80,0x01,0x05,0x0}}
+};
+static const opl4_region_t regions_68[] = { /* Sitar */
+	{0x00, 0x7f, {0x10f,4408,100, 0,2,0x00,0xc4,0x32,0xf4,0x15,0x16,0x1}}
+};
+static const opl4_region_t regions_69[] = { /* Banjo */
+	{0x15, 0x34, {0x013,5685,100, 0,0,0x00,0xdc,0x38,0xf6,0x15,0x09,0x0}},
+	{0x35, 0x38, {0x014,5009,100, 0,0,0x00,0xdc,0x38,0xf6,0x15,0x09,0x0}},
+	{0x39, 0x3c, {0x012,4520,100, 0,0,0x00,0xdc,0x38,0xf6,0x15,0x09,0x0}},
+	{0x3d, 0x44, {0x015,3622,100, 0,0,0x00,0xdc,0x38,0xf6,0x15,0x09,0x0}},
+	{0x45, 0x4c, {0x017,2661,100, 0,0,0x00,0xdc,0x38,0xf6,0x15,0x09,0x0}},
+	{0x4d, 0x6d, {0x016,1632,100, 0,0,0x00,0xdc,0x38,0xf6,0x15,0x09,0x0}}
+};
+static const opl4_region_t regions_6a[] = { /* Shamisen */
+	{0x15, 0x6c, {0x10e,3273,100, 0,0,0x00,0xc0,0x28,0xf7,0x76,0x08,0x0}}
+};
+static const opl4_region_t regions_6b[] = { /* Koto */
+	{0x00, 0x7f, {0x0a9,4033,100, 0,0,0x00,0xc6,0x20,0xf0,0x06,0x07,0x0}}
+};
+static const opl4_region_t regions_6c[] = { /* Kalimba */
+	{0x00, 0x7f, {0x137,3749,100, 0,0,0x00,0xce,0x38,0xf5,0x18,0x08,0x0}}
+};
+static const opl4_region_t regions_6d[] = { /* Bagpipe */
+	{0x15, 0x39, {0x0a4,7683,100, 0,4,0x00,0xc0,0x1c,0xf0,0x00,0x09,0x0}},
+	{0x15, 0x39, {0x0a7,7680,100, 0,1,0x00,0xaa,0x19,0xf0,0x00,0x09,0x0}},
+	{0x3a, 0x6c, {0x0a8,3697,100, 0,1,0x00,0xaa,0x19,0xf0,0x00,0x09,0x0}}
+};
+static const opl4_region_t regions_6e[] = { /* Fiddle */
+	{0x15, 0x3a, {0x105,5158,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}},
+	{0x3b, 0x3f, {0x102,4754,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}},
+	{0x40, 0x41, {0x106,4132,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}},
+	{0x42, 0x44, {0x107,4033,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}},
+	{0x45, 0x47, {0x108,3580,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}},
+	{0x48, 0x4a, {0x10a,2957,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}},
+	{0x4b, 0x4c, {0x10b,2724,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}},
+	{0x4d, 0x4e, {0x10c,2530,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}},
+	{0x4f, 0x51, {0x10d,2166,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}},
+	{0x52, 0x6c, {0x109,1825,100, 0,1,0x00,0xca,0x31,0xf3,0x20,0x09,0x0}}
+};
+static const opl4_region_t regions_6f[] = { /* Shanai */
+	{0x15, 0x6c, {0x041,6946,100, 0,1,0x00,0xc4,0x31,0x95,0x20,0x09,0x0}}
+};
+static const opl4_region_t regions_70[] = { /* Tinkle Bell */
+	{0x15, 0x73, {0x0f3,1821,100, 0,3,0x00,0xc8,0x3b,0xd6,0x25,0x25,0x0}},
+	{0x00, 0x7f, {0x137,5669,100, 0,3,0x00,0x66,0x3b,0xf5,0x18,0x08,0x0}}
+};
+static const opl4_region_t regions_71[] = { /* Agogo */
+	{0x15, 0x74, {0x00b,2474,100, 0,0,0x00,0xd2,0x38,0xf0,0x00,0x09,0x0}}
+};
+static const opl4_region_t regions_72[] = { /* Steel Drums */
+	{0x01, 0x7f, {0x0fe,3670,100, 0,0,0x00,0xca,0x38,0xf3,0x06,0x17,0x1}},
+	{0x15, 0x6c, {0x100,9602,100, 0,0,0x00,0x54,0x38,0xb0,0x05,0x16,0x1}}
+};
+static const opl4_region_t regions_73[] = { /* Woodblock */
+	{0x15, 0x6c, {0x02c,2963, 50, 0,0,0x07,0xd4,0x00,0xf0,0x00,0x09,0x0}}
+};
+static const opl4_region_t regions_74[] = { /* Taiko Drum */
+	{0x13, 0x6c, {0x03e,1194, 50, 0,0,0x00,0xaa,0x38,0xf0,0x04,0x04,0x0}}
+};
+static const opl4_region_t regions_75[] = { /* Melodic Tom */
+	{0x15, 0x6c, {0x0c7,6418, 50, 0,0,0x00,0xe4,0x38,0xf0,0x05,0x01,0x0}}
+};
+static const opl4_region_t regions_76[] = { /* Synth Drum */
+	{0x15, 0x6c, {0x026,3898, 50, 0,0,0x00,0xd0,0x38,0xf0,0x04,0x04,0x0}}
+};
+static const opl4_region_t regions_77[] = { /* Reverse Cymbal */
+	{0x15, 0x6c, {0x031,4138, 50, 0,0,0x00,0xfe,0x38,0x3a,0xf0,0x09,0x0}}
+};
+static const opl4_region_t regions_78[] = { /* Guitar Fret Noise */
+	{0x15, 0x6c, {0x138,5266,100, 0,0,0x00,0xa0,0x38,0xf0,0x00,0x09,0x0}}
+};
+static const opl4_region_t regions_79[] = { /* Breath Noise */
+	{0x01, 0x7f, {0x125,4269,100, 0,0,0x1e,0xd0,0x38,0xf0,0x00,0x09,0x0}}
+};
+static const opl4_region_t regions_7a[] = { /* Seashore */
+	{0x15, 0x6c, {0x008,2965, 20,-2,0,0x00,0xfe,0x00,0x20,0x03,0x04,0x0}},
+	{0x01, 0x7f, {0x037,4394, 20, 2,0,0x14,0xfe,0x00,0x20,0x04,0x05,0x0}}
+};
+static const opl4_region_t regions_7b[] = { /* Bird Tweet */
+	{0x15, 0x6c, {0x009,8078,  5,-4,7,0x00,0xc2,0x0f,0x22,0x12,0x07,0x0}},
+	{0x15, 0x6c, {0x009,3583,  5, 4,5,0x00,0xae,0x15,0x72,0x12,0x07,0x0}}
+};
+static const opl4_region_t regions_7c[] = { /* Telephone Ring */
+	{0x15, 0x6c, {0x003,3602, 10, 0,0,0x00,0xce,0x00,0xf0,0x00,0x0f,0x0}}
+};
+static const opl4_region_t regions_7d[] = { /* Helicopter */
+	{0x0c, 0x7f, {0x001,2965, 10,-2,0,0x00,0xe0,0x08,0x30,0x01,0x07,0x0}},
+	{0x01, 0x7f, {0x037,4394, 10, 2,0,0x44,0x76,0x00,0x30,0x01,0x07,0x0}}
+};
+static const opl4_region_t regions_7e[] = { /* Applause */
+	{0x15, 0x6c, {0x036,8273, 20,-6,7,0x00,0xc4,0x0f,0x70,0x01,0x05,0x0}},
+	{0x15, 0x6c, {0x036,8115,  5, 6,7,0x00,0xc6,0x07,0x70,0x01,0x05,0x0}}
+};
+static const opl4_region_t regions_7f[] = { /* Gun Shot */
+	{0x15, 0x6c, {0x139,2858, 20, 0,0,0x00,0xbe,0x38,0xf0,0x03,0x00,0x0}}
+};
+static const opl4_region_t regions_drums[] = {
+	{0x18, 0x18, {0x0cb,6397,100, 3,0,0x00,0xf4,0x38,0xc9,0x1c,0x0c,0x0}},
+	{0x19, 0x19, {0x0c4,3714,100, 0,0,0x00,0xe0,0x00,0x97,0x19,0x09,0x0}},
+	{0x1a, 0x1a, {0x0c4,3519,100, 0,0,0x00,0xea,0x00,0x61,0x01,0x07,0x0}},
+	{0x1b, 0x1b, {0x0c4,3586,100, 0,0,0x00,0xea,0x00,0xf7,0x19,0x09,0x0}},
+	{0x1c, 0x1c, {0x0c4,3586,100, 0,0,0x00,0xea,0x00,0x81,0x01,0x07,0x0}},
+	{0x1e, 0x1e, {0x0c3,4783,100, 0,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}},
+	{0x1f, 0x1f, {0x0d1,4042,100, 0,0,0x00,0xd6,0x00,0xf0,0x05,0x05,0x0}},
+	{0x20, 0x20, {0x0d2,5943,100, 0,0,0x00,0xcc,0x00,0xf0,0x00,0x09,0x0}},
+	{0x21, 0x21, {0x011,3842,100, 0,0,0x00,0xea,0x00,0xf0,0x16,0x06,0x0}},
+	{0x23, 0x23, {0x011,4098,100, 0,0,0x00,0xea,0x00,0xf0,0x16,0x06,0x0}},
+	{0x24, 0x24, {0x011,4370,100, 0,0,0x00,0xea,0x00,0xf0,0x00,0x06,0x0}},
+	{0x25, 0x25, {0x0d2,4404,100, 0,0,0x00,0xd6,0x00,0xf0,0x00,0x06,0x0}},
+	{0x26, 0x26, {0x0d1,4298,100, 0,0,0x00,0xd6,0x00,0xf0,0x05,0x05,0x0}},
+	{0x27, 0x27, {0x00a,4403,100,-1,0,0x00,0xd6,0x00,0xf0,0x00,0x09,0x0}},
+	{0x28, 0x28, {0x0d1,4554,100, 0,0,0x00,0xdc,0x00,0xf0,0x07,0x07,0x0}},
+	{0x29, 0x29, {0x0c8,4242,100,-4,0,0x00,0xd6,0x00,0xf6,0x16,0x06,0x0}},
+	{0x2a, 0x2a, {0x079,6160,100, 2,0,0x00,0xe0,0x00,0xf5,0x19,0x09,0x0}},
+	{0x2b, 0x2b, {0x0c8,4626,100,-3,0,0x00,0xd6,0x00,0xf6,0x16,0x06,0x0}},
+	{0x2c, 0x2c, {0x07b,6039,100, 2,0,0x00,0xd6,0x00,0xf0,0x00,0x09,0x0}},
+	{0x2d, 0x2d, {0x0c8,5394,100,-2,0,0x00,0xd6,0x00,0xf6,0x16,0x06,0x0}},
+	{0x2e, 0x2e, {0x07a,5690,100, 2,0,0x00,0xd6,0x00,0xf0,0x00,0x05,0x0}},
+	{0x2f, 0x2f, {0x0c7,5185,100, 2,0,0x00,0xe0,0x00,0xf6,0x17,0x07,0x0}},
+	{0x30, 0x30, {0x0c7,5650,100, 3,0,0x00,0xe0,0x00,0xf6,0x17,0x07,0x0}},
+	{0x31, 0x31, {0x031,4395,100, 2,0,0x00,0xea,0x00,0xf0,0x05,0x05,0x0}},
+	{0x32, 0x32, {0x0c7,6162,100, 4,0,0x00,0xe0,0x00,0xf6,0x17,0x07,0x0}},
+	{0x33, 0x33, {0x02e,4391,100,-2,0,0x00,0xea,0x00,0xf0,0x05,0x05,0x0}},
+	{0x34, 0x34, {0x07a,3009,100,-2,0,0x00,0xea,0x00,0xf2,0x15,0x05,0x0}},
+	{0x35, 0x35, {0x021,4522,100,-3,0,0x00,0xd6,0x00,0xf0,0x05,0x05,0x0}},
+	{0x36, 0x36, {0x025,5163,100, 1,0,0x00,0xe0,0x00,0xf0,0x00,0x09,0x0}},
+	{0x37, 0x37, {0x031,5287,100,-1,0,0x00,0xea,0x00,0xf5,0x16,0x06,0x0}},
+	{0x38, 0x38, {0x01d,4395,100, 2,0,0x00,0xe0,0x00,0xf0,0x00,0x09,0x0}},
+	{0x39, 0x39, {0x031,4647,100,-2,0,0x00,0xea,0x00,0xf4,0x16,0x06,0x0}},
+	{0x3a, 0x3a, {0x09d,4426,100,-4,0,0x00,0xe0,0x00,0xf4,0x17,0x07,0x0}},
+	{0x3b, 0x3b, {0x02e,4659,100,-2,0,0x00,0xea,0x00,0xf0,0x06,0x06,0x0}},
+	{0x3c, 0x3c, {0x01c,4769,100, 4,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}},
+	{0x3d, 0x3d, {0x01c,4611,100, 4,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}},
+	{0x3e, 0x3e, {0x01e,4402,100,-3,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}},
+	{0x3f, 0x3f, {0x01f,4387,100,-3,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}},
+	{0x40, 0x40, {0x01f,3983,100,-2,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}},
+	{0x41, 0x41, {0x09c,4526,100, 2,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}},
+	{0x42, 0x42, {0x09c,4016,100, 2,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}},
+	{0x43, 0x43, {0x00b,4739,100,-4,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}},
+	{0x44, 0x44, {0x00b,4179,100,-4,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}},
+	{0x45, 0x45, {0x02f,4787,100,-4,0,0x00,0xd6,0x00,0xf0,0x00,0x09,0x0}},
+	{0x46, 0x46, {0x030,4665,100,-4,0,0x00,0xd6,0x00,0xf0,0x00,0x09,0x0}},
+	{0x47, 0x47, {0x144,4519,100, 4,0,0x00,0xea,0x00,0xf0,0x00,0x0b,0x0}},
+	{0x48, 0x48, {0x144,4111,100, 4,0,0x00,0xea,0x00,0xf0,0x00,0x0b,0x0}},
+	{0x49, 0x49, {0x024,6408,100, 3,0,0x00,0xe0,0x00,0xf0,0x00,0x09,0x0}},
+	{0x4a, 0x4a, {0x024,4144,100, 3,0,0x00,0xcc,0x00,0xf0,0x00,0x09,0x0}},
+	{0x4b, 0x4b, {0x020,4001,100, 2,0,0x00,0xe0,0x00,0xf0,0x00,0x09,0x0}},
+	{0x4c, 0x4c, {0x02c,4402,100, 4,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}},
+	{0x4d, 0x4d, {0x02c,3612,100, 4,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}},
+	{0x4e, 0x4e, {0x022,4129,100,-2,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}},
+	{0x4f, 0x4f, {0x023,4147,100,-2,0,0x00,0xea,0x00,0xf0,0x00,0x09,0x0}},
+	{0x50, 0x50, {0x032,4412,100,-4,0,0x00,0xd6,0x00,0xf0,0x08,0x09,0x0}},
+	{0x51, 0x51, {0x032,4385,100,-4,0,0x00,0xd6,0x00,0xf0,0x00,0x09,0x0}},
+	{0x52, 0x52, {0x02f,5935,100,-1,0,0x00,0xd6,0x00,0xf0,0x00,0x09,0x0}}
+};
+
+#define REGION(num) { ARRAY_SIZE(regions ## num), regions ## num }
+const opl4_region_ptr_t snd_yrw801_regions[0x81] = {
+	REGION(_00), REGION(_01), REGION(_02), REGION(_03),
+	REGION(_04), REGION(_05), REGION(_06), REGION(_07),
+	REGION(_08), REGION(_09), REGION(_0a), REGION(_0b),
+	REGION(_0c), REGION(_0d), REGION(_0e), REGION(_0f),
+	REGION(_10), REGION(_11), REGION(_12), REGION(_13),
+	REGION(_14), REGION(_15), REGION(_16), REGION(_17),
+	REGION(_18), REGION(_19), REGION(_1a), REGION(_1b),
+	REGION(_1c), REGION(_1d), REGION(_1e), REGION(_1f),
+	REGION(_20), REGION(_21), REGION(_22), REGION(_23),
+	REGION(_24), REGION(_25), REGION(_26), REGION(_27),
+	REGION(_28), REGION(_29), REGION(_2a), REGION(_2b),
+	REGION(_2c), REGION(_2d), REGION(_2e), REGION(_2f),
+	REGION(_30), REGION(_31), REGION(_32), REGION(_33),
+	REGION(_34), REGION(_35), REGION(_36), REGION(_37),
+	REGION(_38), REGION(_39), REGION(_3a), REGION(_3b),
+	REGION(_3c), REGION(_3d), REGION(_3e), REGION(_3f),
+	REGION(_40), REGION(_41), REGION(_42), REGION(_43),
+	REGION(_44), REGION(_45), REGION(_46), REGION(_47),
+	REGION(_48), REGION(_49), REGION(_4a), REGION(_4b),
+	REGION(_4c), REGION(_4d), REGION(_4e), REGION(_4f),
+	REGION(_50), REGION(_51), REGION(_52), REGION(_53),
+	REGION(_54), REGION(_55), REGION(_56), REGION(_57),
+	REGION(_58), REGION(_59), REGION(_5a), REGION(_5b),
+	REGION(_5c), REGION(_5d), REGION(_5e), REGION(_5f),
+	REGION(_60), REGION(_61), REGION(_62), REGION(_63),
+	REGION(_64), REGION(_65), REGION(_66), REGION(_67),
+	REGION(_68), REGION(_69), REGION(_6a), REGION(_6b),
+	REGION(_6c), REGION(_6d), REGION(_6e), REGION(_6f),
+	REGION(_70), REGION(_71), REGION(_72), REGION(_73),
+	REGION(_74), REGION(_75), REGION(_76), REGION(_77),
+	REGION(_78), REGION(_79), REGION(_7a), REGION(_7b),
+	REGION(_7c), REGION(_7d), REGION(_7e), REGION(_7f),
+	REGION(_drums)
+};
diff -urN linux-2.5.70-bk6/sound/drivers/vx/Makefile linux-2.5.70-bk7/sound/drivers/vx/Makefile
--- linux-2.5.70-bk6/sound/drivers/vx/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/vx/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,11 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-vx-lib-objs := vx_core.o vx_hwdep.o vx_pcm.o vx_mixer.o vx_cmd.o vx_uer.o
+
+obj-$(CONFIG_SND_VXPOCKET) += snd-vx-lib.o
+obj-$(CONFIG_SND_VXP440) += snd-vx-lib.o
+obj-$(CONFIG_SND_VX222) += snd-vx-lib.o
+
diff -urN linux-2.5.70-bk6/sound/drivers/vx/vx_cmd.c linux-2.5.70-bk7/sound/drivers/vx/vx_cmd.c
--- linux-2.5.70-bk6/sound/drivers/vx/vx_cmd.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/vx/vx_cmd.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,109 @@
+/*
+ * Driver for Digigram VX soundcards
+ *
+ * DSP commands
+ *
+ * Copyright (c) 2002 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 <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/vx_core.h>
+#include "vx_cmd.h"
+
+/*
+ * Array of DSP commands
+ */
+struct vx_cmd_info vx_dsp_cmds[] = {
+[CMD_VERSION] =			{ 0x010000, 2, RMH_SSIZE_FIXED, 1 },
+[CMD_SUPPORTED] =		{ 0x020000, 1, RMH_SSIZE_FIXED, 2 },
+[CMD_TEST_IT] =			{ 0x040000, 1, RMH_SSIZE_FIXED, 1 },
+[CMD_SEND_IRQA] =		{ 0x070001, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_IBL] =			{ 0x080000, 1, RMH_SSIZE_FIXED, 4 },
+[CMD_ASYNC] =			{ 0x0A0000, 1, RMH_SSIZE_ARG, 0 },
+[CMD_RES_PIPE] =		{ 0x400000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_FREE_PIPE] =		{ 0x410000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_CONF_PIPE] =		{ 0x42A101, 2, RMH_SSIZE_FIXED, 0 },
+[CMD_ABORT_CONF_PIPE] =		{ 0x42A100, 2, RMH_SSIZE_FIXED, 0 },
+[CMD_PARAM_OUTPUT_PIPE] =	{ 0x43A000, 2, RMH_SSIZE_FIXED, 0 },
+[CMD_STOP_PIPE] =		{ 0x470004, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_PIPE_STATE] =		{ 0x480000, 1, RMH_SSIZE_FIXED, 1 },
+[CMD_PIPE_SPL_COUNT] =		{ 0x49A000, 2, RMH_SSIZE_FIXED, 2 },
+[CMD_CAN_START_PIPE] =		{ 0x4b0000, 1, RMH_SSIZE_FIXED, 1 },
+[CMD_SIZE_HBUFFER] =		{ 0x4C0000, 1, RMH_SSIZE_FIXED, 1 },
+[CMD_START_STREAM] =		{ 0x80A000, 2, RMH_SSIZE_FIXED, 0 },
+[CMD_START_ONE_STREAM] =	{ 0x800000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_PAUSE_STREAM] =		{ 0x81A000, 2, RMH_SSIZE_FIXED, 0 },
+[CMD_PAUSE_ONE_STREAM] =	{ 0x810000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_STREAM_OUT_LEVEL_ADJUST] =	{ 0x828000, 2, RMH_SSIZE_FIXED, 0 },
+[CMD_STOP_STREAM] =		{ 0x830000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_FORMAT_STREAM_OUT] =	{ 0x868000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_FORMAT_STREAM_IN] =	{ 0x878800, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_GET_STREAM_STATE] =	{ 0x890001, 2, RMH_SSIZE_FIXED, 1 },
+[CMD_DROP_BYTES_AWAY] =		{ 0x8A8000, 2, RMH_SSIZE_FIXED, 0 },
+[CMD_GET_REMAINING_BYTES] =	{ 0x8D0800, 1, RMH_SSIZE_FIXED, 2 },
+[CMD_CONNECT_AUDIO] =		{ 0xC10000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_AUDIO_LEVEL_ADJUST] =	{ 0xC2A000, 3, RMH_SSIZE_FIXED, 0 },
+[CMD_AUDIO_VU_PIC_METER] =	{ 0xC3A003, 2, RMH_SSIZE_FIXED, 1 },
+[CMD_GET_AUDIO_LEVELS] =	{ 0xC4A000, 2, RMH_SSIZE_FIXED, 0 },
+[CMD_GET_NOTIFY_EVENT] =	{ 0x4D0000, 1, RMH_SSIZE_ARG, 0 },
+[CMD_INFO_NOTIFIED] =		{ 0x0B0000, 1, RMH_SSIZE_FIXED, 2 },
+[CMD_ACCESS_IO_FCT] =		{ 0x098000, 1, RMH_SSIZE_ARG, 0 },
+[CMD_STATUS_R_BUFFERS] =	{ 0x440000, 1, RMH_SSIZE_ARG, 0 },
+[CMD_UPDATE_R_BUFFERS] =	{ 0x848000, 4, RMH_SSIZE_FIXED, 0 },
+[CMD_LOAD_EFFECT_CONTEXT] =	{ 0x0c8000, 3, RMH_SSIZE_FIXED, 1 },
+[CMD_EFFECT_ONE_PIPE] =		{ 0x458000, 0, RMH_SSIZE_FIXED, 0 },
+[CMD_MODIFY_CLOCK] =		{ 0x0d0000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_STREAM1_OUT_SET_N_LEVELS] ={ 0x858000, 3, RMH_SSIZE_FIXED, 0 },
+[CMD_PURGE_STREAM_DCMDS] =	{ 0x8b8000, 3, RMH_SSIZE_FIXED, 0 },
+[CMD_NOTIFY_PIPE_TIME] =	{ 0x4e0000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_LOAD_EFFECT_CONTEXT_PACKET] = { 0x0c8000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_RELIC_R_BUFFER] =		{ 0x8e0800, 1, RMH_SSIZE_FIXED, 1 },
+[CMD_RESYNC_AUDIO_INPUTS] =	{ 0x0e0000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_NOTIFY_STREAM_TIME] =	{ 0x8f0000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_STREAM_SAMPLE_COUNT] =	{ 0x900000, 1, RMH_SSIZE_FIXED, 2 },
+[CMD_CONFIG_TIME_CODE] =	{ 0x050000, 2, RMH_SSIZE_FIXED, 0 },
+[CMD_GET_TIME_CODE] =		{ 0x060000, 1, RMH_SSIZE_FIXED, 5 },
+[CMD_MANAGE_SIGNAL] =		{ 0x0f0000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_PARAMETER_STREAM_OUT] =	{ 0x91A000, 3, RMH_SSIZE_FIXED, 0 },
+[CMD_READ_BOARD_FREQ] =		{ 0x030000, 1, RMH_SSIZE_FIXED, 2 },
+[CMD_GET_STREAM_LEVELS] =	{ 0x8c0000, 1, RMH_SSIZE_FIXED, 3 },
+[CMD_PURGE_PIPE_DCMDS] =	{ 0x4f8000, 3, RMH_SSIZE_FIXED, 0 },
+// [CMD_SET_STREAM_OUT_EFFECTS] =	{ 0x888000, 34, RMH_SSIZE_FIXED, 0 },
+// [CMD_GET_STREAM_OUT_EFFECTS] =	{ 0x928000, 2, RMH_SSIZE_FIXED, 32 },
+[CMD_CONNECT_MONITORING] =	{ 0xC00000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_STREAM2_OUT_SET_N_LEVELS] = { 0x938000, 3, RMH_SSIZE_FIXED, 0 },
+[CMD_CANCEL_R_BUFFERS] =	{ 0x948000, 4, RMH_SSIZE_FIXED, 0 },
+[CMD_NOTIFY_END_OF_BUFFER] =	{ 0x950000, 1, RMH_SSIZE_FIXED, 0 },
+[CMD_GET_STREAM_VU_METER] =	{ 0x95A000, 2, RMH_SSIZE_ARG, 0 },
+};
+
+/**
+ * vx_init_rmh - initialize the RMH instance
+ * @rmh: the rmh pointer to be initialized
+ * @cmd: the rmh command to be set
+ */
+void vx_init_rmh(struct vx_rmh *rmh, unsigned int cmd)
+{
+	snd_assert(cmd < CMD_LAST_INDEX, return);
+	rmh->LgCmd = vx_dsp_cmds[cmd].length;
+	rmh->LgStat = vx_dsp_cmds[cmd].st_length;
+	rmh->DspStat = vx_dsp_cmds[cmd].st_type;
+	rmh->Cmd[0] = vx_dsp_cmds[cmd].opcode;
+}
+
diff -urN linux-2.5.70-bk6/sound/drivers/vx/vx_cmd.h linux-2.5.70-bk7/sound/drivers/vx/vx_cmd.h
--- linux-2.5.70-bk6/sound/drivers/vx/vx_cmd.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/vx/vx_cmd.h	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,248 @@
+/*
+ * Driver for Digigram VX soundcards
+ *
+ * Definitions of DSP commands
+ *
+ * Copyright (c) 2002 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 __VX_CMD_H
+#define __VX_CMD_H
+
+enum {
+	CMD_VERSION,
+	CMD_SUPPORTED,
+	CMD_TEST_IT,
+	CMD_SEND_IRQA,
+	CMD_IBL,
+	CMD_ASYNC,
+	CMD_RES_PIPE,
+	CMD_FREE_PIPE,
+	CMD_CONF_PIPE,
+	CMD_ABORT_CONF_PIPE,
+	CMD_PARAM_OUTPUT_PIPE,
+	CMD_STOP_PIPE,
+	CMD_PIPE_STATE,
+	CMD_PIPE_SPL_COUNT,
+	CMD_CAN_START_PIPE,
+	CMD_SIZE_HBUFFER,
+	CMD_START_STREAM,
+	CMD_START_ONE_STREAM,
+	CMD_PAUSE_STREAM,
+	CMD_PAUSE_ONE_STREAM,
+	CMD_STREAM_OUT_LEVEL_ADJUST,
+	CMD_STOP_STREAM,
+	CMD_FORMAT_STREAM_OUT,
+	CMD_FORMAT_STREAM_IN,
+	CMD_GET_STREAM_STATE,
+	CMD_DROP_BYTES_AWAY,
+	CMD_GET_REMAINING_BYTES,
+	CMD_CONNECT_AUDIO,
+	CMD_AUDIO_LEVEL_ADJUST,
+	CMD_AUDIO_VU_PIC_METER,
+	CMD_GET_AUDIO_LEVELS,
+	CMD_GET_NOTIFY_EVENT,
+	CMD_INFO_NOTIFIED,
+	CMD_ACCESS_IO_FCT,
+	CMD_STATUS_R_BUFFERS,
+	CMD_UPDATE_R_BUFFERS,
+	CMD_LOAD_EFFECT_CONTEXT,
+	CMD_EFFECT_ONE_PIPE,
+	CMD_MODIFY_CLOCK,
+	CMD_STREAM1_OUT_SET_N_LEVELS,
+	CMD_PURGE_STREAM_DCMDS,
+	CMD_NOTIFY_PIPE_TIME,
+	CMD_LOAD_EFFECT_CONTEXT_PACKET,
+	CMD_RELIC_R_BUFFER,
+	CMD_RESYNC_AUDIO_INPUTS,
+	CMD_NOTIFY_STREAM_TIME,
+	CMD_STREAM_SAMPLE_COUNT,
+	CMD_CONFIG_TIME_CODE,
+	CMD_GET_TIME_CODE,
+	CMD_MANAGE_SIGNAL,
+	CMD_PARAMETER_STREAM_OUT,
+	CMD_READ_BOARD_FREQ,
+	CMD_GET_STREAM_LEVELS,
+	CMD_PURGE_PIPE_DCMDS,
+	// CMD_SET_STREAM_OUT_EFFECTS,
+	// CMD_GET_STREAM_OUT_EFFECTS,
+	CMD_CONNECT_MONITORING,
+	CMD_STREAM2_OUT_SET_N_LEVELS,
+	CMD_CANCEL_R_BUFFERS,
+	CMD_NOTIFY_END_OF_BUFFER,
+	CMD_GET_STREAM_VU_METER,
+	CMD_LAST_INDEX
+};
+
+struct vx_cmd_info {
+	unsigned int opcode;	/* command word */
+	int length;		/* command length (in words) */
+	int st_type;		/* status type (RMH_SSIZE_XXX) */
+	int st_length;		/* fixed length */
+};
+
+/* Family and code op of some DSP requests. */
+#define CODE_OP_PIPE_TIME                       0x004e0000
+#define CODE_OP_START_STREAM                    0x00800000
+#define CODE_OP_PAUSE_STREAM                    0x00810000
+#define CODE_OP_OUT_STREAM_LEVEL                0x00820000
+#define CODE_OP_UPDATE_R_BUFFERS                0x00840000
+#define CODE_OP_OUT_STREAM1_LEVEL_CURVE         0x00850000
+#define CODE_OP_OUT_STREAM2_LEVEL_CURVE         0x00930000
+#define CODE_OP_OUT_STREAM_FORMAT               0x00860000
+#define CODE_OP_STREAM_TIME                     0x008f0000
+#define CODE_OP_OUT_STREAM_EXTRAPARAMETER       0x00910000
+#define CODE_OP_OUT_AUDIO_LEVEL                 0x00c20000
+
+#define NOTIFY_LAST_COMMAND     0x00400000
+
+/* Values for a user delay */
+#define DC_DIFFERED_DELAY       (1<<BIT_DIFFERED_COMMAND)
+#define DC_NOTIFY_DELAY         (1<<BIT_NOTIFIED_COMMAND)
+#define DC_HBUFFER_DELAY        (1<<BIT_TIME_RELATIVE_TO_BUFFER)
+#define DC_MULTIPLE_DELAY       (1<<BIT_RESERVED)
+#define DC_STREAM_TIME_DELAY    (1<<BIT_STREAM_TIME)
+#define DC_CANCELLED_DELAY      (1<<BIT_CANCELLED_COMMAND)
+
+/* Values for tiDelayed field in TIME_INFO structure,
+ * and for pbPause field in PLAY_BUFFER_INFO structure
+ */
+#define BIT_DIFFERED_COMMAND                0
+#define BIT_NOTIFIED_COMMAND                1
+#define BIT_TIME_RELATIVE_TO_BUFFER         2
+#define BIT_RESERVED                        3
+#define BIT_STREAM_TIME                     4
+#define BIT_CANCELLED_COMMAND               5
+
+/* Access to the "Size" field of the response of the CMD_GET_NOTIFY_EVENT request. */
+#define GET_NOTIFY_EVENT_SIZE_FIELD_MASK    0x000000ff
+
+/* DSP commands general masks */
+#define OPCODE_MASK                 0x00ff0000
+#define DSP_DIFFERED_COMMAND_MASK   0x0000C000
+
+/* Notifications (NOTIFY_INFO) */
+#define ALL_CMDS_NOTIFIED                   0x0000  // reserved
+#define START_STREAM_NOTIFIED               0x0001
+#define PAUSE_STREAM_NOTIFIED               0x0002
+#define OUT_STREAM_LEVEL_NOTIFIED           0x0003
+#define OUT_STREAM_PARAMETER_NOTIFIED       0x0004  // left for backward compatibility
+#define OUT_STREAM_FORMAT_NOTIFIED          0x0004
+#define PIPE_TIME_NOTIFIED                  0x0005
+#define OUT_AUDIO_LEVEL_NOTIFIED            0x0006
+#define OUT_STREAM_LEVEL_CURVE_NOTIFIED     0x0007
+#define STREAM_TIME_NOTIFIED                0x0008
+#define OUT_STREAM_EXTRAPARAMETER_NOTIFIED  0x0009
+#define UNKNOWN_COMMAND_NOTIFIED            0xffff
+
+/* Output pipe parameters setting */
+#define MASK_VALID_PIPE_MPEG_PARAM      0x000040
+#define MASK_VALID_PIPE_BACKWARD_PARAM  0x000020
+#define MASK_SET_PIPE_MPEG_PARAM        0x000002
+#define MASK_SET_PIPE_BACKWARD_PARAM    0x000001
+
+#define MASK_DSP_WORD           0x00FFFFFF
+#define MASK_ALL_STREAM         0x00FFFFFF
+#define MASK_DSP_WORD_LEVEL     0x000001FF
+#define MASK_FIRST_FIELD        0x0000001F
+#define FIELD_SIZE              5
+
+#define COMMAND_RECORD_MASK     0x000800
+
+/* PipeManagement definition bits (PIPE_DECL_INFO) */
+#define P_UNDERRUN_SKIP_SOUND_MASK				0x01
+#define P_PREPARE_FOR_MPEG3_MASK				0x02
+#define P_DO_NOT_RESET_ANALOG_LEVELS			0x04
+#define P_ALLOW_UNDER_ALLOCATION_MASK			0x08
+#define P_DATA_MODE_MASK				0x10
+#define P_ASIO_BUFFER_MANAGEMENT_MASK			0x20
+
+#define BIT_SKIP_SOUND					0x08	// bit 3
+#define BIT_DATA_MODE					0x10	// bit 4
+    
+/* Bits in the CMD_MODIFY_CLOCK request. */
+#define CMD_MODIFY_CLOCK_FD_BIT     0x00000001
+#define CMD_MODIFY_CLOCK_T_BIT      0x00000002
+#define CMD_MODIFY_CLOCK_S_BIT      0x00000004
+
+/* Access to the results of the CMD_GET_TIME_CODE RMH. */
+#define TIME_CODE_V_MASK            0x00800000
+#define TIME_CODE_N_MASK            0x00400000
+#define TIME_CODE_B_MASK            0x00200000
+#define TIME_CODE_W_MASK            0x00100000
+
+/* Values for the CMD_MANAGE_SIGNAL RMH. */
+#define MANAGE_SIGNAL_TIME_CODE     0x01
+#define MANAGE_SIGNAL_MIDI          0x02
+
+/* Values for the CMD_CONFIG_TIME_CODE RMH. */
+#define CONFIG_TIME_CODE_CANCEL     0x00001000
+    
+/* Mask to get only the effective time from the
+ * high word out of the 2 returned by the DSP
+ */
+#define PCX_TIME_HI_MASK        0x000fffff
+
+/* Values for setting a H-Buffer time */
+#define HBUFFER_TIME_HIGH       0x00200000
+#define HBUFFER_TIME_LOW        0x00000000
+
+#define NOTIFY_MASK_TIME_HIGH   0x00400000
+#define MULTIPLE_MASK_TIME_HIGH 0x00100000
+#define STREAM_MASK_TIME_HIGH   0x00800000
+
+
+/*
+ *
+ */
+extern struct vx_cmd_info vx_dsp_cmds[];
+
+void vx_init_rmh(struct vx_rmh *rmh, unsigned int cmd);
+
+/**
+ * vx_send_pipe_cmd_params - fill first command word for pipe commands
+ * @rmh: the rmh to be modified
+ * @is_capture: 0 = playback, 1 = capture operation
+ * @param1: first pipe-parameter
+ * @param2: second pipe-parameter
+ */
+static inline void vx_set_pipe_cmd_params(struct vx_rmh *rmh, int is_capture,
+					  int param1, int param2)
+{
+	if (is_capture)
+		rmh->Cmd[0] |= COMMAND_RECORD_MASK;
+	rmh->Cmd[0] |= (((u32)param1 & MASK_FIRST_FIELD) << FIELD_SIZE) & MASK_DSP_WORD;
+		
+	if (param2)
+		rmh->Cmd[0] |= ((u32)param2 & MASK_FIRST_FIELD) & MASK_DSP_WORD;
+	
+}
+
+/**
+ * vx_set_stream_cmd_params - fill first command word for stream commands
+ * @rmh: the rmh to be modified
+ * @is_capture: 0 = playback, 1 = capture operation
+ * @pipe: the pipe index (zero-based)
+ */
+static inline void vx_set_stream_cmd_params(struct vx_rmh *rmh, int is_capture, int pipe)
+{
+	if (is_capture)
+		rmh->Cmd[0] |= COMMAND_RECORD_MASK;
+	rmh->Cmd[0] |= (((u32)pipe & MASK_FIRST_FIELD) << FIELD_SIZE) & MASK_DSP_WORD;
+}
+
+#endif /* __VX_CMD_H */
diff -urN linux-2.5.70-bk6/sound/drivers/vx/vx_core.c linux-2.5.70-bk7/sound/drivers/vx/vx_core.c
--- linux-2.5.70-bk6/sound/drivers/vx/vx_core.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/vx/vx_core.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,808 @@
+/*
+ * Driver for Digigram VX soundcards
+ *
+ * Hardware core part
+ *
+ * Copyright (c) 2002 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/delay.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/asoundef.h>
+#include <sound/info.h>
+#include <asm/io.h>
+#include <sound/vx_core.h>
+#include "vx_cmd.h"
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("Common routines for Digigram VX drivers");
+MODULE_LICENSE("GPL");
+
+
+/*
+ * snd_vx_delay - delay for the specified time
+ * @xmsec: the time to delay in msec
+ */
+void snd_vx_delay(vx_core_t *chip, int xmsec)
+{
+	if (! (chip->chip_status & VX_STAT_IN_SUSPEND) && ! in_interrupt() &&
+	    xmsec >= 1000 / HZ) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout((xmsec * HZ + 999) / 1000);
+	} else {
+		mdelay(xmsec);
+	}
+}
+
+/*
+ * vx_check_reg_bit - wait for the specified bit is set/reset on a register
+ * @reg: register to check
+ * @mask: bit mask
+ * @bit: resultant bit to be checked
+ * @time: time-out of loop in msec
+ *
+ * returns zero if a bit matches, or a negative error code.
+ */
+int snd_vx_check_reg_bit(vx_core_t *chip, int reg, int mask, int bit, int time)
+{
+	unsigned long end_time = jiffies + (time * HZ + 999) / 1000;
+#ifdef CONFIG_SND_DEBUG
+	static char *reg_names[VX_REG_MAX] = {
+		"ICR", "CVR", "ISR", "IVR", "RXH", "RXM", "RXL",
+		"DMA", "CDSP", "RFREQ", "RUER/V2", "DATA", "MEMIRQ",
+		"ACQ", "BIT0", "BIT1", "MIC0", "MIC1", "MIC2",
+		"MIC3", "INTCSR", "CNTRL", "GPIOC",
+		"LOFREQ", "HIFREQ", "CSUER", "RUER"
+	};
+#endif
+	do {
+		if ((snd_vx_inb(chip, reg) & mask) == bit)
+			return 0;
+		//snd_vx_delay(chip, 10);
+	} while (time_after_eq(end_time, jiffies));
+	snd_printd(KERN_DEBUG "vx_check_reg_bit: timeout, reg=%s, mask=0x%x, val=0x%x\n", reg_names[reg], mask, snd_vx_inb(chip, reg));
+	return -EIO;
+}
+
+/*
+ * vx_send_irq_dsp - set command irq bit
+ * @num: the requested IRQ type, IRQ_XXX
+ *
+ * this triggers the specified IRQ request
+ * returns 0 if successful, or a negative error code.
+ * 
+ */
+static int vx_send_irq_dsp(vx_core_t *chip, int num)
+{
+	int nirq;
+
+	/* wait for Hc = 0 */
+	if (snd_vx_check_reg_bit(chip, VX_CVR, CVR_HC, 0, 200) < 0)
+		return -EIO;
+
+	nirq = num;
+	if (vx_has_new_dsp(chip))
+		nirq += VXP_IRQ_OFFSET;
+	vx_outb(chip, CVR, (nirq >> 1) | CVR_HC);
+	return 0;
+}
+
+
+/*
+ * vx_reset_chk - reset CHK bit on ISR
+ *
+ * returns 0 if successful, or a negative error code.
+ */
+static int vx_reset_chk(vx_core_t *chip)
+{
+	/* Reset irq CHK */
+	if (vx_send_irq_dsp(chip, IRQ_RESET_CHK) < 0)
+		return -EIO;
+	/* Wait until CHK = 0 */
+	if (vx_check_isr(chip, ISR_CHK, 0, 200) < 0)
+		return -EIO;
+	return 0;
+}
+
+/*
+ * vx_transfer_end - terminate message transfer
+ * @cmd: IRQ message to send (IRQ_MESS_XXX_END)
+ *
+ * returns 0 if successful, or a negative error code.
+ * the error code can be VX-specific, retrieved via vx_get_error().
+ * NB: call with spinlock held!
+ */
+static int vx_transfer_end(vx_core_t *chip, int cmd)
+{
+	int err;
+
+	if ((err = vx_reset_chk(chip)) < 0)
+		return err;
+
+	/* irq MESS_READ/WRITE_END */
+	if ((err = vx_send_irq_dsp(chip, cmd)) < 0)
+		return err;
+
+	/* Wait CHK = 1 */
+	if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
+		return err;
+
+	/* If error, Read RX */
+	if ((err = vx_inb(chip, ISR)) & ISR_ERR) {
+		if ((err = vx_wait_for_rx_full(chip)) < 0) {
+			snd_printd(KERN_DEBUG "transfer_end: error in rx_full\n");
+			return err;
+		}
+		err = vx_inb(chip, RXH) << 16;
+		err |= vx_inb(chip, RXM) << 8;
+		err |= vx_inb(chip, RXL);
+		snd_printd(KERN_DEBUG "transfer_end: error = 0x%x\n", err);
+		return -(VX_ERR_MASK | err);
+	}
+	return 0;
+}
+
+/*
+ * vx_read_status - return the status rmh
+ * @rmh: rmh record to store the status
+ *
+ * returns 0 if successful, or a negative error code.
+ * the error code can be VX-specific, retrieved via vx_get_error().
+ * NB: call with spinlock held!
+ */
+static int vx_read_status(vx_core_t *chip, struct vx_rmh *rmh)
+{
+	int i, err, val, size;
+
+	/* no read necessary? */
+	if (rmh->DspStat == RMH_SSIZE_FIXED && rmh->LgStat == 0)
+		return 0;
+
+	/* Wait for RX full (with timeout protection)
+	 * The first word of status is in RX
+	 */
+	err = vx_wait_for_rx_full(chip);
+	if (err < 0)
+		return err;
+
+	/* Read RX */
+	val = vx_inb(chip, RXH) << 16;
+	val |= vx_inb(chip, RXM) << 8;
+	val |= vx_inb(chip, RXL);
+
+	/* If status given by DSP, let's decode its size */
+	switch (rmh->DspStat) {
+	case RMH_SSIZE_ARG:
+		size = val & 0xff;
+		rmh->Stat[0] = val & 0xffff00;
+		rmh->LgStat = size + 1;
+		break;
+	case RMH_SSIZE_MASK:
+		/* Let's count the arg numbers from a mask */
+		rmh->Stat[0] = val;
+		size = 0;
+		while (val) {
+			if (val & 0x01)
+				size++;
+			val >>= 1;
+		}
+		rmh->LgStat = size + 1;
+		break;
+	default:
+		/* else retrieve the status length given by the driver */
+		size = rmh->LgStat;
+		rmh->Stat[0] = val;  /* Val is the status 1st word */
+		size--;              /* hence adjust remaining length */
+		break;
+        }
+
+	if (size < 1)
+		return 0;
+	snd_assert(size <= SIZE_MAX_STATUS, return -EINVAL);
+
+	for (i = 1; i <= size; i++) {
+		/* trigger an irq MESS_WRITE_NEXT */
+		err = vx_send_irq_dsp(chip, IRQ_MESS_WRITE_NEXT);
+		if (err < 0)
+			return err;
+		/* Wait for RX full (with timeout protection) */
+		err = vx_wait_for_rx_full(chip);
+		if (err < 0)
+			return err;
+		rmh->Stat[i] = vx_inb(chip, RXH) << 16;
+		rmh->Stat[i] |= vx_inb(chip, RXM) <<  8;
+		rmh->Stat[i] |= vx_inb(chip, RXL);
+	}
+
+	return vx_transfer_end(chip, IRQ_MESS_WRITE_END);
+}
+
+
+#define MASK_MORE_THAN_1_WORD_COMMAND   0x00008000
+#define MASK_1_WORD_COMMAND             0x00ff7fff
+
+/*
+ * vx_send_msg_nolock - send a DSP message and read back the status
+ * @rmh: the rmh record to send and receive
+ *
+ * returns 0 if successful, or a negative error code.
+ * the error code can be VX-specific, retrieved via vx_get_error().
+ * 
+ * this function doesn't call spinlock at all.
+ */
+int vx_send_msg_nolock(vx_core_t *chip, struct vx_rmh *rmh)
+{
+	int i, err;
+	
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return -EBUSY;
+
+	if ((err = vx_reset_chk(chip)) < 0) {
+		snd_printd(KERN_DEBUG "vx_send_msg: vx_reset_chk error\n");
+		return err;
+	}
+
+#if 0
+	printk(KERN_DEBUG "rmh: cmd = 0x%06x, length = %d, stype = %d\n",
+	       rmh->Cmd[0], rmh->LgCmd, rmh->DspStat);
+	if (rmh->LgCmd > 1) {
+		printk(KERN_DEBUG "  ");
+		for (i = 1; i < rmh->LgCmd; i++)
+			printk("0x%06x ", rmh->Cmd[i]);
+		printk("\n");
+	}
+#endif
+	/* Check bit M is set according to length of the command */
+	if (rmh->LgCmd > 1)
+		rmh->Cmd[0] |= MASK_MORE_THAN_1_WORD_COMMAND;
+	else
+		rmh->Cmd[0] &= MASK_1_WORD_COMMAND;
+
+	/* Wait for TX empty */
+	if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) {
+		snd_printd(KERN_DEBUG "vx_send_msg: wait tx empty error\n");
+		return err;
+	}
+
+	/* Write Cmd[0] */
+	vx_outb(chip, TXH, (rmh->Cmd[0] >> 16) & 0xff);
+	vx_outb(chip, TXM, (rmh->Cmd[0] >> 8) & 0xff);
+	vx_outb(chip, TXL, rmh->Cmd[0] & 0xff);
+
+	/* Trigger irq MESSAGE */
+	if ((err = vx_send_irq_dsp(chip, IRQ_MESSAGE)) < 0) {
+		snd_printd(KERN_DEBUG "vx_send_msg: send IRQ_MESSAGE error\n");
+		return err;
+	}
+
+	/* Wait for CHK = 1 */
+	if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
+		return err;
+
+	/* If error, get error value from RX */
+	if (vx_inb(chip, ISR) & ISR_ERR) {
+		if ((err = vx_wait_for_rx_full(chip)) < 0) {
+			snd_printd(KERN_DEBUG "vx_send_msg: rx_full read error\n");
+			return err;
+		}
+		err = vx_inb(chip, RXH) << 16;
+		err |= vx_inb(chip, RXM) << 8;
+		err |= vx_inb(chip, RXL);
+		snd_printd(KERN_DEBUG "msg got error = 0x%x at cmd[0]\n", err);
+		err = -(VX_ERR_MASK | err);
+		return err;
+	}
+
+	/* Send the other words */
+	if (rmh->LgCmd > 1) {
+		for (i = 1; i < rmh->LgCmd; i++) {
+			/* Wait for TX ready */
+			if ((err = vx_wait_isr_bit(chip, ISR_TX_READY)) < 0) {
+				snd_printd(KERN_DEBUG "vx_send_msg: tx_ready error\n");
+				return err;
+			}
+
+			/* Write Cmd[i] */
+			vx_outb(chip, TXH, (rmh->Cmd[i] >> 16) & 0xff);
+			vx_outb(chip, TXM, (rmh->Cmd[i] >> 8) & 0xff);
+			vx_outb(chip, TXL, rmh->Cmd[i] & 0xff);
+
+			/* Trigger irq MESS_READ_NEXT */
+			if ((err = vx_send_irq_dsp(chip, IRQ_MESS_READ_NEXT)) < 0) {
+				snd_printd(KERN_DEBUG "vx_send_msg: IRQ_READ_NEXT error\n");
+				return err;
+			}
+		}
+		/* Wait for TX empty */
+		if ((err = vx_wait_isr_bit(chip, ISR_TX_READY)) < 0) {
+			snd_printd(KERN_DEBUG "vx_send_msg: TX_READY error\n");
+			return err;
+		}
+		/* End of transfer */
+		err = vx_transfer_end(chip, IRQ_MESS_READ_END);
+		if (err < 0)
+			return err;
+	}
+
+	return vx_read_status(chip, rmh);
+}
+
+
+/*
+ * vx_send_msg - send a DSP message with spinlock
+ * @rmh: the rmh record to send and receive
+ *
+ * returns 0 if successful, or a negative error code.
+ * see vx_send_msg_nolock().
+ */
+int vx_send_msg(vx_core_t *chip, struct vx_rmh *rmh)
+{
+	int err;
+
+	spin_lock_bh(&chip->lock);
+	err = vx_send_msg_nolock(chip, rmh);
+	spin_unlock_bh(&chip->lock);
+	return err;
+}
+
+
+/*
+ * vx_send_rih_nolock - send an RIH to xilinx
+ * @cmd: the command to send
+ *
+ * returns 0 if successful, or a negative error code.
+ * the error code can be VX-specific, retrieved via vx_get_error().
+ *
+ * this function doesn't call spinlock at all.
+ *
+ * unlike RMH, no command is sent to DSP.
+ */
+int vx_send_rih_nolock(vx_core_t *chip, int cmd)
+{
+	int err;
+
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return -EBUSY;
+
+#if 0
+	printk(KERN_DEBUG "send_rih: cmd = 0x%x\n", cmd);
+#endif
+	if ((err = vx_reset_chk(chip)) < 0)
+		return err;
+	/* send the IRQ */
+	if ((err = vx_send_irq_dsp(chip, cmd)) < 0)
+		return err;
+	/* Wait CHK = 1 */
+	if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
+		return err;
+	/* If error, read RX */
+	if (vx_inb(chip, ISR) & ISR_ERR) {
+		if ((err = vx_wait_for_rx_full(chip)) < 0)
+			return err;
+		err = vx_inb(chip, RXH) << 16;
+		err |= vx_inb(chip, RXM) << 8;
+		err |= vx_inb(chip, RXL);
+		return -(VX_ERR_MASK | err);
+	}
+	return 0;
+}
+
+
+/*
+ * vx_send_rih - send an RIH with spinlock
+ * @cmd: the command to send
+ *
+ * see vx_send_rih_nolock().
+ */
+int vx_send_rih(vx_core_t *chip, int cmd)
+{
+	int err;
+
+	spin_lock_bh(&chip->lock);
+	err = vx_send_rih_nolock(chip, cmd);
+	spin_unlock_bh(&chip->lock);
+	return err;
+}
+
+#define END_OF_RESET_WAIT_TIME		500	/* us */
+
+/**
+ * snd_vx_boot_xilinx - boot up the xilinx interface
+ * @boot: the boot record to load
+ */
+int snd_vx_load_boot_image(vx_core_t *chip, const snd_hwdep_dsp_image_t *boot)
+{
+	unsigned int i;
+	int no_fillup = vx_has_new_dsp(chip);
+
+	/* check the length of boot image */
+	snd_assert(boot->length > 0, return -EINVAL);
+	snd_assert(boot->length % 3 == 0, return -EINVAL);
+	snd_assert(boot->image, return -EINVAL);
+#if 0
+	{
+		/* more strict check */
+		unsigned int c = ((u32)boot->image[0] << 16) | ((u32)boot->image[1] << 8) | boot->image[2];
+		snd_assert(boot->length == (c + 2) * 3, return -EINVAL);
+	}
+#endif
+
+	/* reset dsp */
+	vx_reset_dsp(chip);
+	
+	udelay(END_OF_RESET_WAIT_TIME); /* another wait? */
+
+	/* download boot strap */
+	for (i = 0; i < 0x600; i += 3) {
+		if (i >= boot->length) {
+			if (no_fillup)
+				break;
+			if (vx_wait_isr_bit(chip, ISR_TX_EMPTY) < 0) {
+				snd_printk(KERN_ERR "dsp boot failed at %d\n", i);
+				return -EIO;
+			}
+			vx_outb(chip, TXH, 0);
+			vx_outb(chip, TXM, 0);
+			vx_outb(chip, TXL, 0);
+		} else {
+			unsigned char image[3];
+			if (copy_from_user(image, boot->image + i, 3))
+				return -EFAULT;
+			if (vx_wait_isr_bit(chip, ISR_TX_EMPTY) < 0) {
+				snd_printk(KERN_ERR "dsp boot failed at %d\n", i);
+				return -EIO;
+			}
+			vx_outb(chip, TXH, image[0]);
+			vx_outb(chip, TXM, image[1]);
+			vx_outb(chip, TXL, image[2]);
+		}
+	}
+	return 0;
+}
+
+/*
+ * vx_test_irq_src - query the source of interrupts
+ *
+ * called from irq handler only
+ */
+static int vx_test_irq_src(vx_core_t *chip, unsigned int *ret)
+{
+	int err;
+
+	vx_init_rmh(&chip->irq_rmh, CMD_TEST_IT);
+	spin_lock(&chip->lock);
+	err = vx_send_msg_nolock(chip, &chip->irq_rmh);
+	if (err < 0)
+		*ret = 0;
+	else
+		*ret = chip->irq_rmh.Stat[0];
+	spin_unlock(&chip->lock);
+	return err;
+}
+
+
+/*
+ * vx_interrupt - soft irq handler
+ */
+static void vx_interrupt(unsigned long private_data)
+{
+	vx_core_t *chip = snd_magic_cast(vx_core_t, (void*)private_data, return);
+	unsigned int events;
+		
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return;
+
+	if (vx_test_irq_src(chip, &events) < 0)
+		return;
+    
+#if 0
+	if (events & 0x000800)
+		printk(KERN_ERR "DSP Stream underrun ! IRQ events = 0x%x\n", events);
+#endif
+	// printk(KERN_DEBUG "IRQ events = 0x%x\n", events);
+
+	/* We must prevent any application using this DSP
+	 * and block any further request until the application
+	 * either unregisters or reloads the DSP
+	 */
+	if (events & FATAL_DSP_ERROR) {
+		snd_printk(KERN_ERR "vx_core: fatal DSP error!!\n");
+		return;
+	}
+
+	/* The start on time code conditions are filled (ie the time code
+	 * received by the board is equal to one of those given to it).
+	 */
+	if (events & TIME_CODE_EVENT_PENDING)
+		; /* so far, nothing to do yet */
+
+	/* The frequency has changed on the board (UER mode). */
+	if (events & FREQUENCY_CHANGE_EVENT_PENDING)
+		vx_change_frequency(chip);
+
+	/* update the pcm streams */
+	vx_pcm_update_intr(chip, events);
+}
+
+
+/**
+ * snd_vx_irq_handler - interrupt handler
+ */
+irqreturn_t snd_vx_irq_handler(int irq, void *dev, struct pt_regs *regs)
+{
+	vx_core_t *chip = snd_magic_cast(vx_core_t, dev, return IRQ_NONE);
+
+	if (! (chip->chip_status & VX_STAT_CHIP_INIT) ||
+	    (chip->chip_status & VX_STAT_IS_STALE))
+		return IRQ_NONE;
+	if (! vx_test_and_ack(chip))
+		tasklet_hi_schedule(&chip->tq);
+	return IRQ_HANDLED;
+}
+
+
+/*
+ */
+static void vx_reset_board(vx_core_t *chip, int cold_reset)
+{
+	snd_assert(chip->ops->reset_board, return);
+
+	/* current source, later sync'ed with target */
+	chip->audio_source = VX_AUDIO_SRC_LINE;
+	if (cold_reset) {
+		chip->audio_source_target = chip->audio_source;
+		chip->clock_source = INTERNAL_QUARTZ;
+		chip->freq = 48000;
+		chip->uer_detected = VX_UER_MODE_NOT_PRESENT;
+		chip->uer_bits = SNDRV_PCM_DEFAULT_CON_SPDIF;
+	}
+
+	chip->ops->reset_board(chip, cold_reset);
+
+	vx_reset_codec(chip, cold_reset);
+
+	vx_set_internal_clock(chip, chip->freq);
+
+	/* Reset the DSP */
+	vx_reset_dsp(chip);
+
+	if (vx_is_pcmcia(chip)) {
+		/* Acknowledge any pending IRQ and reset the MEMIRQ flag. */
+		vx_test_and_ack(chip);
+		vx_validate_irq(chip, 1);
+	}
+
+	/* init CBits */
+	vx_set_iec958_status(chip, chip->uer_bits);
+}
+
+
+/*
+ * proc interface
+ */
+
+static void vx_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	vx_core_t *chip = snd_magic_cast(vx_core_t, entry->private_data, return);
+	static char *audio_src_vxp[] = { "Line", "Mic", "Digital" };
+	static char *audio_src_vx2[] = { "Analog", "Analog", "Digital" };
+	static char *clock_src[] = { "Internal", "External" };
+	static char *uer_type[] = { "Consumer", "Professional", "Not Present" };
+	
+	snd_iprintf(buffer, "%s\n", chip->card->longname);
+	snd_iprintf(buffer, "DSP audio info:");
+	if (chip->audio_info & VX_AUDIO_INFO_REAL_TIME)
+		snd_iprintf(buffer, " realtime");
+	if (chip->audio_info & VX_AUDIO_INFO_OFFLINE)
+		snd_iprintf(buffer, " offline");
+	if (chip->audio_info & VX_AUDIO_INFO_MPEG1)
+		snd_iprintf(buffer, " mpeg1");
+	if (chip->audio_info & VX_AUDIO_INFO_MPEG2)
+		snd_iprintf(buffer, " mpeg2");
+	if (chip->audio_info & VX_AUDIO_INFO_LINEAR_8)
+		snd_iprintf(buffer, " linear8");
+	if (chip->audio_info & VX_AUDIO_INFO_LINEAR_16)
+		snd_iprintf(buffer, " linear16");
+	if (chip->audio_info & VX_AUDIO_INFO_LINEAR_24)
+		snd_iprintf(buffer, " linear24");
+	snd_iprintf(buffer, "\n");
+	snd_iprintf(buffer, "Input Source: %s\n", vx_is_pcmcia(chip) ?
+		    audio_src_vxp[chip->audio_source] :
+		    audio_src_vx2[chip->audio_source]);
+	snd_iprintf(buffer, "Clock Source: %s\n", clock_src[chip->clock_source]);
+	snd_iprintf(buffer, "Frequency: %d\n", chip->freq);
+	snd_iprintf(buffer, "Detected Frequency: %d\n", chip->freq_detected);
+	snd_iprintf(buffer, "Detected UER type: %s\n", uer_type[chip->uer_detected]);
+}
+
+static void vx_proc_init(vx_core_t *chip)
+{
+	snd_info_entry_t *entry;
+
+	if (! snd_card_proc_new(chip->card, "vx-status", &entry))
+		snd_info_set_text_ops(entry, chip, vx_proc_read);
+}
+
+
+/**
+ * snd_vx_dsp_boot - load the DSP boot
+ */
+int snd_vx_dsp_boot(vx_core_t *chip, const snd_hwdep_dsp_image_t *boot)
+{
+	int err;
+	int cold_reset = !(chip->chip_status & VX_STAT_DEVICE_INIT);
+
+	vx_reset_board(chip, cold_reset);
+	vx_validate_irq(chip, 0);
+
+	if ((err = snd_vx_load_boot_image(chip, boot)) < 0)
+		return err;
+	snd_vx_delay(chip, 10);
+
+	return 0;
+}
+
+/**
+ * snd_vx_dsp_load - load the DSP image
+ */
+int snd_vx_dsp_load(vx_core_t *chip, const snd_hwdep_dsp_image_t *dsp)
+{
+	unsigned int i;
+	int err;
+	unsigned int csum = 0;
+	unsigned char image[3], *cptr;
+
+	snd_assert(dsp->length % 3 == 0, return -EINVAL);
+
+	vx_toggle_dac_mute(chip, 1);
+
+	/* Transfert data buffer from PC to DSP */
+	for (i = 0; i < dsp->length; i += 3) {
+		if (copy_from_user(image, dsp->image + i, 3))
+			return -EFAULT;
+		/* Wait DSP ready for a new read */
+		if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) {
+			printk("dsp loading error at position %d\n", i);
+			return err;
+		}
+		cptr = image;
+		csum ^= *cptr;
+		csum = (csum >> 24) | (csum << 8);
+		vx_outb(chip, TXH, *cptr++);
+		csum ^= *cptr;
+		csum = (csum >> 24) | (csum << 8);
+		vx_outb(chip, TXM, *cptr++);
+		csum ^= *cptr;
+		csum = (csum >> 24) | (csum << 8);
+		vx_outb(chip, TXL, *cptr++);
+	}
+	snd_printdd(KERN_DEBUG "checksum = 0x%08x\n", csum);
+
+	snd_vx_delay(chip, 200);
+
+	if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
+		return err;
+
+	vx_toggle_dac_mute(chip, 0);
+
+	vx_test_and_ack(chip);
+	vx_validate_irq(chip, 1);
+
+	return 0;
+}
+
+/**
+ * snd_vx_create - constructor for vx_core_t
+ * @hw: hardware specific record
+ *
+ * this function allocates the instance and prepare for the hardware
+ * initialization.
+ *
+ * return the instance pointer if successful, NULL in error.
+ */
+vx_core_t *snd_vx_create(snd_card_t *card, struct snd_vx_hardware *hw,
+			 struct snd_vx_ops *ops,
+			 int extra_size)
+{
+	vx_core_t *chip;
+
+	snd_assert(card && hw && ops, return NULL);
+
+	chip = snd_magic_kcalloc(vx_core_t, extra_size, GFP_KERNEL);
+	if (! chip) {
+		snd_printk(KERN_ERR "vx_core: no memory\n");
+		return NULL;
+	}
+	spin_lock_init(&chip->lock);
+	spin_lock_init(&chip->irq_lock);
+	chip->irq = -1;
+	chip->hw = hw;
+	chip->type = hw->type;
+	chip->ops = ops;
+	tasklet_init(&chip->tq, vx_interrupt, (unsigned long)chip);
+	init_MUTEX(&chip->mixer_mutex);
+
+	chip->card = card;
+	card->private_data = chip;
+	strcpy(card->driver, hw->name);
+	sprintf(card->shortname, "Digigram %s", hw->name);
+
+	vx_proc_init(chip);
+
+	return chip;
+}
+
+#ifdef CONFIG_PM
+/*
+ * suspend
+ */
+void snd_vx_suspend(vx_core_t *chip)
+{
+	unsigned int i;
+
+	chip->chip_status |= VX_STAT_IN_SUSPEND;
+	for (i = 0; i < chip->hw->num_codecs; i++)
+		snd_pcm_suspend_all(chip->pcm[i]);
+	if (chip->hwdep)
+		chip->hwdep->dsp_loaded = 0;
+}
+
+/*
+ * resume
+ */
+void snd_vx_resume(vx_core_t *chip)
+{
+	/* clear all stuff... */
+	chip->chip_status &= ~(VX_STAT_IN_SUSPEND|VX_STAT_CHIP_INIT);
+}
+
+#endif
+
+/*
+ * module entries
+ */
+static int __init alsa_vx_core_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_vx_core_exit(void)
+{
+}
+
+module_init(alsa_vx_core_init)
+module_exit(alsa_vx_core_exit)
+
+/*
+ * exports
+ */
+EXPORT_SYMBOL(snd_vx_check_reg_bit);
+EXPORT_SYMBOL(snd_vx_create);
+EXPORT_SYMBOL(snd_vx_hwdep_new);
+EXPORT_SYMBOL(snd_vx_irq_handler);
+EXPORT_SYMBOL(snd_vx_delay);
+EXPORT_SYMBOL(snd_vx_dsp_boot);
+EXPORT_SYMBOL(snd_vx_dsp_load);
+EXPORT_SYMBOL(snd_vx_load_boot_image);
+#ifdef CONFIG_PM
+EXPORT_SYMBOL(snd_vx_suspend);
+EXPORT_SYMBOL(snd_vx_resume);
+#endif
diff -urN linux-2.5.70-bk6/sound/drivers/vx/vx_hwdep.c linux-2.5.70-bk7/sound/drivers/vx/vx_hwdep.c
--- linux-2.5.70-bk6/sound/drivers/vx/vx_hwdep.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/vx/vx_hwdep.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,122 @@
+/*
+ * Driver for Digigram VX soundcards
+ *
+ * hwdep device manager
+ *
+ * Copyright (c) 2002 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 <sound/core.h>
+#include <sound/hwdep.h>
+#include <sound/vx_core.h>
+
+static int vx_hwdep_open(snd_hwdep_t *hw, struct file *file)
+{
+	return 0;
+}
+
+static int vx_hwdep_release(snd_hwdep_t *hw, struct file *file)
+{
+	return 0;
+}
+
+static int vx_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info)
+{
+	static char *type_ids[VX_TYPE_NUMS] = {
+		[VX_TYPE_BOARD] = "vxboard",
+		[VX_TYPE_V2] = "vx222",
+		[VX_TYPE_MIC] = "vx222",
+		[VX_TYPE_VXPOCKET] = "vxpocket",
+		[VX_TYPE_VXP440] = "vxp440",
+	};
+	vx_core_t *vx = snd_magic_cast(vx_core_t, hw->private_data, return -ENXIO);
+
+	snd_assert(type_ids[vx->type], return -EINVAL);
+	strcpy(info->id, type_ids[vx->type]);
+	if (vx_is_pcmcia(vx))
+		info->num_dsps = 4;
+	else
+		info->num_dsps = 3;
+	if (vx->chip_status & VX_STAT_CHIP_INIT)
+		info->chip_ready = 1;
+	info->version = VX_DRIVER_VERSION;
+	return 0;
+}
+
+static int vx_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
+{
+	vx_core_t *vx = snd_magic_cast(vx_core_t, hw->private_data, return -ENXIO);
+	int index, err;
+
+	snd_assert(vx->ops->load_dsp, return -ENXIO);
+	err = vx->ops->load_dsp(vx, dsp);
+	if (err < 0)
+		return err;
+
+	index = dsp->index;
+	if (! vx_is_pcmcia(vx))
+		index++;
+	if (index == 1)
+		vx->chip_status |= VX_STAT_XILINX_LOADED;
+	if (index < 3)
+		return 0;
+
+	/* ok, we reached to the last one */
+	/* create the devices if not built yet */
+	if (! (vx->chip_status & VX_STAT_DEVICE_INIT)) {
+		if ((err = snd_vx_pcm_new(vx)) < 0)
+			return err;
+
+		if ((err = snd_vx_mixer_new(vx)) < 0)
+			return err;
+
+		if (vx->ops->add_controls)
+			if ((err = vx->ops->add_controls(vx)) < 0)
+				return err;
+
+		if ((err = snd_card_register(vx->card)) < 0)
+			return err;
+
+		vx->chip_status |= VX_STAT_DEVICE_INIT;
+	}
+	vx->chip_status |= VX_STAT_CHIP_INIT;
+	return 0;
+}
+
+
+/* exported */
+int snd_vx_hwdep_new(vx_core_t *chip)
+{
+	int err;
+	snd_hwdep_t *hw;
+
+	if ((err = snd_hwdep_new(chip->card, SND_VX_HWDEP_ID, 0, &hw)) < 0)
+		return err;
+
+	hw->iface = SNDRV_HWDEP_IFACE_VX;
+	hw->private_data = chip;
+	hw->ops.open = vx_hwdep_open;
+	hw->ops.release = vx_hwdep_release;
+	hw->ops.dsp_status = vx_hwdep_dsp_status;
+	hw->ops.dsp_load = vx_hwdep_dsp_load;
+	hw->exclusive = 1;
+	sprintf(hw->name, "VX Loader (%s)", chip->card->driver);
+	chip->hwdep = hw;
+
+	return 0;
+}
diff -urN linux-2.5.70-bk6/sound/drivers/vx/vx_mixer.c linux-2.5.70-bk7/sound/drivers/vx/vx_mixer.c
--- linux-2.5.70-bk6/sound/drivers/vx/vx_mixer.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/vx/vx_mixer.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,951 @@
+/*
+ * Driver for Digigram VX soundcards
+ *
+ * Common mixer part
+ *
+ * Copyright (c) 2002 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 <sound/core.h>
+#include <sound/control.h>
+#include <sound/vx_core.h>
+#include "vx_cmd.h"
+
+#define chip_t vx_core_t
+
+
+/*
+ * write a codec data (24bit)
+ */
+static void vx_write_codec_reg(vx_core_t *chip, int codec, unsigned int data)
+{
+	unsigned long flags;
+
+	snd_assert(chip->ops->write_codec, return);
+
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	chip->ops->write_codec(chip, codec, data);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+/*
+ * Data type used to access the Codec
+ */
+typedef union {
+	u32 l;
+#ifdef SNDRV_BIG_ENDIAN
+	struct w {
+		u16 h;
+		u16 l;
+	} w;
+	struct b {
+		u8 hh;
+		u8 mh;
+		u8 ml;
+		u8 ll;
+	} b;
+#else /* LITTLE_ENDIAN */
+	struct w {
+		u16 l;
+		u16 h;
+	} w;
+	struct b {
+		u8 ll;
+		u8 ml;
+		u8 mh;
+		u8 hh;
+	} b;
+#endif
+} vx_codec_data_t;
+
+#define SET_CDC_DATA_SEL(di,s)          ((di).b.mh = (u8) (s))
+#define SET_CDC_DATA_REG(di,r)          ((di).b.ml = (u8) (r))
+#define SET_CDC_DATA_VAL(di,d)          ((di).b.ll = (u8) (d))
+#define SET_CDC_DATA_INIT(di)           ((di).l = 0L, SET_CDC_DATA_SEL(di,XX_CODEC_SELECTOR))
+
+/*
+ * set up codec register and write the value
+ * @codec: the codec id, 0 or 1
+ * @reg: register index
+ * @val: data value
+ */
+static void vx_set_codec_reg(vx_core_t *chip, int codec, int reg, int val)
+{
+	vx_codec_data_t data;
+	/* DAC control register */
+	SET_CDC_DATA_INIT(data);
+	SET_CDC_DATA_REG(data, reg);
+	SET_CDC_DATA_VAL(data, val);
+	vx_write_codec_reg(chip, codec, data.l);
+}
+
+
+/*
+ * vx_set_analog_output_level - set the output attenuation level
+ * @codec: the output codec, 0 or 1.  (1 for VXP440 only)
+ * @left: left output level, 0 = mute
+ * @right: right output level
+ */
+static void vx_set_analog_output_level(vx_core_t *chip, int codec, int left, int right)
+{
+	left  = chip->hw->output_level_max - left;
+	right = chip->hw->output_level_max - right;
+
+	if (chip->ops->akm_write) {
+		chip->ops->akm_write(chip, XX_CODEC_LEVEL_LEFT_REGISTER, left);
+		chip->ops->akm_write(chip, XX_CODEC_LEVEL_RIGHT_REGISTER, right);
+	} else {
+		/* convert to attenuation level: 0 = 0dB (max), 0xe3 = -113.5 dB (min) */
+		vx_set_codec_reg(chip, codec, XX_CODEC_LEVEL_LEFT_REGISTER, left);
+		vx_set_codec_reg(chip, codec, XX_CODEC_LEVEL_RIGHT_REGISTER, right);
+	}
+}
+
+
+/*
+ * vx_toggle_dac_mute -  mute/unmute DAC
+ * @mute: 0 = unmute, 1 = mute
+ */
+
+#define DAC_ATTEN_MIN	0x08
+#define DAC_ATTEN_MAX	0x38
+
+void vx_toggle_dac_mute(vx_core_t *chip, int mute)
+{
+	unsigned int i;
+	for (i = 0; i < chip->hw->num_codecs; i++) {
+		if (chip->ops->akm_write)
+			chip->ops->akm_write(chip, XX_CODEC_DAC_CONTROL_REGISTER, mute); /* XXX */
+		else
+			vx_set_codec_reg(chip, i, XX_CODEC_DAC_CONTROL_REGISTER,
+					 mute ? DAC_ATTEN_MAX : DAC_ATTEN_MIN);
+	}
+}
+
+/*
+ * vx_reset_codec - reset and initialize the codecs
+ */
+void vx_reset_codec(vx_core_t *chip, int cold_reset)
+{
+	unsigned int i;
+	int port = chip->type >= VX_TYPE_VXPOCKET ? 0x75 : 0x65;
+
+	chip->ops->reset_codec(chip);
+
+	/* AKM codecs should be initialized in reset_codec callback */
+	if (! chip->ops->akm_write) {
+		/* initialize old codecs */
+		for (i = 0; i < chip->hw->num_codecs; i++) {
+			/* DAC control register (change level when zero crossing + mute) */
+			vx_set_codec_reg(chip, i, XX_CODEC_DAC_CONTROL_REGISTER, DAC_ATTEN_MAX);
+			/* ADC control register */
+			vx_set_codec_reg(chip, i, XX_CODEC_ADC_CONTROL_REGISTER, 0x00);
+			/* Port mode register */
+			vx_set_codec_reg(chip, i, XX_CODEC_PORT_MODE_REGISTER, port);
+			/* Clock control register */
+			vx_set_codec_reg(chip, i, XX_CODEC_CLOCK_CONTROL_REGISTER, 0x00);
+		}
+	}
+
+	/* mute analog output */
+	for (i = 0; i < chip->hw->num_codecs; i++) {
+		chip->output_level[i][0] = 0;
+		chip->output_level[i][1] = 0;
+		vx_set_analog_output_level(chip, i, 0, 0);
+	}
+}
+
+/*
+ * change the audio input source
+ * @src: the target source (VX_AUDIO_SRC_XXX)
+ */
+static void vx_change_audio_source(vx_core_t *chip, int src)
+{
+	unsigned long flags;
+
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	chip->ops->change_audio_source(chip, src);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+/*
+ * change the audio source if necessary and possible
+ * returns 1 if the source is actually changed.
+ */
+int vx_sync_audio_source(vx_core_t *chip)
+{
+	if (chip->audio_source_target == chip->audio_source ||
+	    chip->pcm_running)
+		return 0;
+	vx_change_audio_source(chip, chip->audio_source_target);
+	chip->audio_source = chip->audio_source_target;
+	return 1;
+}
+
+
+/*
+ * audio level, mute, monitoring
+ */
+struct vx_audio_level {
+	unsigned int has_level: 1;
+	unsigned int has_monitor_level: 1;
+	unsigned int has_mute: 1;
+	unsigned int has_monitor_mute: 1;
+	unsigned int mute;
+	unsigned int monitor_mute;
+	short level;
+	short monitor_level;
+};
+
+static int vx_adjust_audio_level(vx_core_t *chip, int audio, int capture,
+				 struct vx_audio_level *info)
+{
+	struct vx_rmh rmh;
+
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return -EBUSY;
+
+        vx_init_rmh(&rmh, CMD_AUDIO_LEVEL_ADJUST);
+	if (capture)
+		rmh.Cmd[0] |= COMMAND_RECORD_MASK;
+	/* Add Audio IO mask */
+	rmh.Cmd[1] = 1 << audio;
+	rmh.Cmd[2] = 0;
+	if (info->has_level) {
+		rmh.Cmd[0] |=  VALID_AUDIO_IO_DIGITAL_LEVEL;
+		rmh.Cmd[2] |= info->level;
+        }
+	if (info->has_monitor_level) {
+		rmh.Cmd[0] |=  VALID_AUDIO_IO_MONITORING_LEVEL;
+		rmh.Cmd[2] |= ((unsigned int)info->monitor_level << 10);
+        }
+	if (info->has_mute) { 
+		rmh.Cmd[0] |= VALID_AUDIO_IO_MUTE_LEVEL;
+		if (info->mute)
+			rmh.Cmd[2] |= AUDIO_IO_HAS_MUTE_LEVEL;
+	}
+	if (info->has_monitor_mute) {
+		/* validate flag for M2 at least to unmute it */ 
+		rmh.Cmd[0] |=  VALID_AUDIO_IO_MUTE_MONITORING_1 | VALID_AUDIO_IO_MUTE_MONITORING_2;
+		if (info->monitor_mute)
+			rmh.Cmd[2] |= AUDIO_IO_HAS_MUTE_MONITORING_1;
+	}
+
+	return vx_send_msg(chip, &rmh);
+}
+
+    
+#if 0 // not used
+static int vx_read_audio_level(vx_core_t *chip, int audio, int capture,
+			       struct vx_audio_level *info)
+{
+	int err;
+	struct vx_rmh rmh;
+
+	memset(info, 0, sizeof(*info));
+        vx_init_rmh(&rmh, CMD_GET_AUDIO_LEVELS);
+	if (capture)
+		rmh.Cmd[0] |= COMMAND_RECORD_MASK;
+	/* Add Audio IO mask */
+	rmh.Cmd[1] = 1 << audio;
+	err = vx_send_msg(chip, &rmh);
+	if (err < 0)
+		return err;
+	info.level = rmh.Stat[0] & MASK_DSP_WORD_LEVEL;
+	info.monitor_level = (rmh.Stat[0] >> 10) & MASK_DSP_WORD_LEVEL;
+	info.mute = (rmh.Stat[i] & AUDIO_IO_HAS_MUTE_LEVEL) ? 1 : 0;
+	info.monitor_mute = (rmh.Stat[i] & AUDIO_IO_HAS_MUTE_MONITORING_1) ? 1 : 0;
+	return 0;
+}
+#endif // not used
+
+/*
+ * set the monitoring level and mute state of the given audio
+ * no more static, because must be called from vx_pcm to demute monitoring
+ */
+int vx_set_monitor_level(vx_core_t *chip, int audio, int level, int active)
+{
+	struct vx_audio_level info;
+
+	memset(&info, 0, sizeof(info));
+	info.has_monitor_level = 1;
+	info.monitor_level = level;
+	info.has_monitor_mute = 1;
+	info.monitor_mute = !active;
+	chip->audio_monitor[audio] = level;
+	chip->audio_monitor_active[audio] = active;
+	return vx_adjust_audio_level(chip, audio, 0, &info); /* playback only */
+}
+
+
+/*
+ * set the mute status of the given audio
+ */
+static int vx_set_audio_switch(vx_core_t *chip, int audio, int active)
+{
+	struct vx_audio_level info;
+
+	memset(&info, 0, sizeof(info));
+	info.has_mute = 1;
+	info.mute = !active;
+	chip->audio_active[audio] = active;
+	return vx_adjust_audio_level(chip, audio, 0, &info); /* playback only */
+}
+
+/*
+ * set the mute status of the given audio
+ */
+static int vx_set_audio_gain(vx_core_t *chip, int audio, int capture, int level)
+{
+	struct vx_audio_level info;
+
+	memset(&info, 0, sizeof(info));
+	info.has_level = 1;
+	info.level = level;
+	chip->audio_gain[capture][audio] = level;
+	return vx_adjust_audio_level(chip, audio, capture, &info);
+}
+
+/*
+ * reset all audio levels
+ */
+static void vx_reset_audio_levels(vx_core_t *chip)
+{
+	unsigned int i, c;
+	struct vx_audio_level info;
+
+	memset(chip->audio_gain, 0, sizeof(chip->audio_gain));
+	memset(chip->audio_active, 0, sizeof(chip->audio_active));
+	memset(chip->audio_monitor, 0, sizeof(chip->audio_monitor));
+	memset(chip->audio_monitor_active, 0, sizeof(chip->audio_monitor_active));
+
+	for (c = 0; c < 2; c++) {
+		for (i = 0; i < chip->hw->num_ins * 2; i++) {
+			memset(&info, 0, sizeof(info));
+			if (c == 0) {
+				info.has_monitor_level = 1;
+				info.has_mute = 1;
+				info.has_monitor_mute = 1;
+			}
+			info.has_level = 1;
+			info.level = CVAL_0DB; /* default: 0dB */
+			vx_adjust_audio_level(chip, i, c, &info);
+			chip->audio_gain[c][i] = CVAL_0DB;
+			chip->audio_monitor[i] = CVAL_0DB;
+		}
+	}
+}
+
+
+/*
+ * VU, peak meter record
+ */
+
+#define VU_METER_CHANNELS	2
+
+struct vx_vu_meter {
+	int saturated;
+	int vu_level;
+	int peak_level;
+};
+
+/*
+ * get the VU and peak meter values
+ * @audio: the audio index
+ * @capture: 0 = playback, 1 = capture operation
+ * @info: the array of vx_vu_meter records (size = 2).
+ */
+static int vx_get_audio_vu_meter(vx_core_t *chip, int audio, int capture, struct vx_vu_meter *info)
+{
+	struct vx_rmh rmh;
+	int i, err;
+
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return -EBUSY;
+
+	vx_init_rmh(&rmh, CMD_AUDIO_VU_PIC_METER);
+	rmh.LgStat += 2 * VU_METER_CHANNELS;
+	if (capture)
+		rmh.Cmd[0] |= COMMAND_RECORD_MASK;
+    
+        /* Add Audio IO mask */
+	rmh.Cmd[1] = 0;
+	for (i = 0; i < VU_METER_CHANNELS; i++)
+		rmh.Cmd[1] |= 1 << (audio + i);
+	err = vx_send_msg(chip, &rmh);
+	if (err < 0)
+		return err;
+	/* Read response */
+	for (i = 0; i < 2 * VU_METER_CHANNELS; i +=2) {
+		info->saturated = (rmh.Stat[0] & (1 << (audio + i))) ? 1 : 0;
+		info->vu_level = rmh.Stat[i + 1];
+		info->peak_level = rmh.Stat[i + 2];
+		info++;
+	}
+	return 0;
+}
+   
+
+/*
+ * control API entries
+ */
+
+/*
+ * output level control
+ */
+static int vx_output_level_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = chip->hw->output_level_max;
+	return 0;
+}
+
+static int vx_output_level_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	int codec = kcontrol->id.index;
+	down(&chip->mixer_mutex);
+	ucontrol->value.integer.value[0] = chip->output_level[codec][0];
+	ucontrol->value.integer.value[1] = chip->output_level[codec][1];
+	up(&chip->mixer_mutex);
+	return 0;
+}
+
+static int vx_output_level_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	int codec = kcontrol->id.index;
+	down(&chip->mixer_mutex);
+	if (ucontrol->value.integer.value[0] != chip->output_level[codec][0] ||
+	    ucontrol->value.integer.value[1] != chip->output_level[codec][1]) {
+		vx_set_analog_output_level(chip, codec,
+					   ucontrol->value.integer.value[0],
+					   ucontrol->value.integer.value[1]);
+		chip->output_level[codec][0] = ucontrol->value.integer.value[0];
+		chip->output_level[codec][1] = ucontrol->value.integer.value[1];
+		up(&chip->mixer_mutex);
+		return 1;
+	}
+	up(&chip->mixer_mutex);
+	return 0;
+}
+
+static snd_kcontrol_new_t vx_control_output_level = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Master Playback Volume",
+	.info =		vx_output_level_info,
+	.get =		vx_output_level_get,
+	.put =		vx_output_level_put,
+};
+
+/*
+ * audio source select
+ */
+static int vx_audio_src_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	static char *texts_mic[3] = {
+		"Digital", "Line", "Mic"
+	};
+	static char *texts_vx2[2] = {
+		"Digital", "Analog"
+	};
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	if (chip->type >= VX_TYPE_VXPOCKET) {
+		uinfo->value.enumerated.items = 3;
+		if (uinfo->value.enumerated.item > 2)
+			uinfo->value.enumerated.item = 2;
+		strcpy(uinfo->value.enumerated.name,
+		       texts_mic[uinfo->value.enumerated.item]);
+	} else {
+		uinfo->value.enumerated.items = 2;
+		if (uinfo->value.enumerated.item > 1)
+			uinfo->value.enumerated.item = 1;
+		strcpy(uinfo->value.enumerated.name,
+		       texts_vx2[uinfo->value.enumerated.item]);
+	}
+	return 0;
+}
+
+static int vx_audio_src_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.enumerated.item[0] = chip->audio_source_target;
+	return 0;
+}
+
+static int vx_audio_src_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	down(&chip->mixer_mutex);
+	if (chip->audio_source_target != ucontrol->value.enumerated.item[0]) {
+		chip->audio_source_target = ucontrol->value.enumerated.item[0];
+		vx_sync_audio_source(chip);
+		up(&chip->mixer_mutex);
+		return 1;
+	}
+	up(&chip->mixer_mutex);
+	return 0;
+}
+
+static snd_kcontrol_new_t vx_control_audio_src = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Capture Source",
+	.info =		vx_audio_src_info,
+	.get =		vx_audio_src_get,
+	.put =		vx_audio_src_put,
+};
+
+/*
+ * Audio Gain
+ */
+static int vx_audio_gain_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 = CVAL_MAX;
+	return 0;
+}
+
+static int vx_audio_gain_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	int audio = kcontrol->private_value & 0xff;
+	int capture = (kcontrol->private_value >> 8) & 1;
+
+	down(&chip->mixer_mutex);
+	ucontrol->value.integer.value[0] = chip->audio_gain[capture][audio];
+	ucontrol->value.integer.value[1] = chip->audio_gain[capture][audio+1];
+	up(&chip->mixer_mutex);
+	return 0;
+}
+
+static int vx_audio_gain_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	int audio = kcontrol->private_value & 0xff;
+	int capture = (kcontrol->private_value >> 8) & 1;
+
+	down(&chip->mixer_mutex);
+	if (ucontrol->value.integer.value[0] != chip->audio_gain[capture][audio] ||
+	    ucontrol->value.integer.value[1] != chip->audio_gain[capture][audio+1]) {
+		vx_set_audio_gain(chip, audio, capture, ucontrol->value.integer.value[0]);
+		vx_set_audio_gain(chip, audio+1, capture, ucontrol->value.integer.value[1]);
+		up(&chip->mixer_mutex);
+		return 1;
+	}
+	up(&chip->mixer_mutex);
+	return 0;
+}
+
+static int vx_audio_monitor_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	int audio = kcontrol->private_value & 0xff;
+
+	down(&chip->mixer_mutex);
+	ucontrol->value.integer.value[0] = chip->audio_monitor[audio];
+	ucontrol->value.integer.value[1] = chip->audio_monitor[audio+1];
+	up(&chip->mixer_mutex);
+	return 0;
+}
+
+static int vx_audio_monitor_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	int audio = kcontrol->private_value & 0xff;
+
+	down(&chip->mixer_mutex);
+	if (ucontrol->value.integer.value[0] != chip->audio_monitor[audio] ||
+	    ucontrol->value.integer.value[1] != chip->audio_monitor[audio+1]) {
+		vx_set_monitor_level(chip, audio, ucontrol->value.integer.value[0],
+				     chip->audio_monitor_active[audio]);
+		vx_set_monitor_level(chip, audio+1, ucontrol->value.integer.value[1],
+				     chip->audio_monitor_active[audio+1]);
+		up(&chip->mixer_mutex);
+		return 1;
+	}
+	up(&chip->mixer_mutex);
+	return 0;
+}
+
+static int vx_audio_sw_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 vx_audio_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	int audio = kcontrol->private_value & 0xff;
+
+	down(&chip->mixer_mutex);
+	ucontrol->value.integer.value[0] = chip->audio_active[audio];
+	ucontrol->value.integer.value[1] = chip->audio_active[audio+1];
+	up(&chip->mixer_mutex);
+	return 0;
+}
+
+static int vx_audio_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	int audio = kcontrol->private_value & 0xff;
+
+	down(&chip->mixer_mutex);
+	if (ucontrol->value.integer.value[0] != chip->audio_active[audio] ||
+	    ucontrol->value.integer.value[1] != chip->audio_active[audio+1]) {
+		vx_set_audio_switch(chip, audio, ucontrol->value.integer.value[0]);
+		vx_set_audio_switch(chip, audio+1, ucontrol->value.integer.value[1]);
+		up(&chip->mixer_mutex);
+		return 1;
+	}
+	up(&chip->mixer_mutex);
+	return 0;
+}
+
+static int vx_monitor_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	int audio = kcontrol->private_value & 0xff;
+
+	down(&chip->mixer_mutex);
+	ucontrol->value.integer.value[0] = chip->audio_monitor_active[audio];
+	ucontrol->value.integer.value[1] = chip->audio_monitor_active[audio+1];
+	up(&chip->mixer_mutex);
+	return 0;
+}
+
+static int vx_monitor_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	int audio = kcontrol->private_value & 0xff;
+
+	down(&chip->mixer_mutex);
+	if (ucontrol->value.integer.value[0] != chip->audio_monitor_active[audio] ||
+	    ucontrol->value.integer.value[1] != chip->audio_monitor_active[audio+1]) {
+		vx_set_monitor_level(chip, audio, chip->audio_monitor[audio],
+				     ucontrol->value.integer.value[0]);
+		vx_set_monitor_level(chip, audio+1, chip->audio_monitor[audio+1],
+				     ucontrol->value.integer.value[1]);
+		up(&chip->mixer_mutex);
+		return 1;
+	}
+	up(&chip->mixer_mutex);
+	return 0;
+}
+
+static snd_kcontrol_new_t vx_control_audio_gain = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	/* name will be filled later */
+	.info =         vx_audio_gain_info,
+	.get =          vx_audio_gain_get,
+	.put =          vx_audio_gain_put
+};
+static snd_kcontrol_new_t vx_control_output_switch = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "PCM Playback Switch",
+	.info =         vx_audio_sw_info,
+	.get =          vx_audio_sw_get,
+	.put =          vx_audio_sw_put
+};
+static snd_kcontrol_new_t vx_control_monitor_gain = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "Monitoring Volume",
+	.info =         vx_audio_gain_info,	/* shared */
+	.get =          vx_audio_monitor_get,
+	.put =          vx_audio_monitor_put
+};
+static snd_kcontrol_new_t vx_control_monitor_switch = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "Monitoring Switch",
+	.info =         vx_audio_sw_info,	/* shared */
+	.get =          vx_monitor_sw_get,
+	.put =          vx_monitor_sw_put
+};
+
+
+/*
+ * IEC958 status bits
+ */
+static int vx_iec958_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 vx_iec958_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+
+	down(&chip->mixer_mutex);
+	ucontrol->value.iec958.status[0] = (chip->uer_bits >> 0) & 0xff;
+	ucontrol->value.iec958.status[1] = (chip->uer_bits >> 8) & 0xff;
+	ucontrol->value.iec958.status[2] = (chip->uer_bits >> 16) & 0xff;
+	ucontrol->value.iec958.status[3] = (chip->uer_bits >> 24) & 0xff;
+	up(&chip->mixer_mutex);
+        return 0;
+}
+
+static int vx_iec958_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 int vx_iec958_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+
+	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);
+	down(&chip->mixer_mutex);
+	if (chip->uer_bits != val) {
+		chip->uer_bits = val;
+		vx_set_iec958_status(chip, val);
+		up(&chip->mixer_mutex);
+		return 1;
+	}
+	up(&chip->mixer_mutex);
+	return 0;
+}
+
+static snd_kcontrol_new_t vx_control_iec958_mask = {
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+	.info =		vx_iec958_info,	/* shared */
+	.get =		vx_iec958_mask_get,
+};
+
+static snd_kcontrol_new_t vx_control_iec958 = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.info =         vx_iec958_info,
+	.get =          vx_iec958_get,
+	.put =          vx_iec958_put
+};
+
+
+/*
+ * VU meter
+ */
+
+#define METER_MAX	0xff
+#define METER_SHIFT	16
+
+static int vx_vu_meter_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 = METER_MAX;
+	return 0;
+}
+
+static int vx_vu_meter_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	struct vx_vu_meter meter[2];
+	int audio = kcontrol->private_value & 0xff;
+	int capture = (kcontrol->private_value >> 8) & 1;
+
+	vx_get_audio_vu_meter(chip, audio, capture, meter);
+	ucontrol->value.integer.value[0] = meter[0].vu_level >> METER_SHIFT;
+	ucontrol->value.integer.value[1] = meter[1].vu_level >> METER_SHIFT;
+	return 0;
+}
+
+static int vx_peak_meter_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	struct vx_vu_meter meter[2];
+	int audio = kcontrol->private_value & 0xff;
+	int capture = (kcontrol->private_value >> 8) & 1;
+
+	vx_get_audio_vu_meter(chip, audio, capture, meter);
+	ucontrol->value.integer.value[0] = meter[0].peak_level >> METER_SHIFT;
+	ucontrol->value.integer.value[1] = meter[1].peak_level >> METER_SHIFT;
+	return 0;
+}
+
+static int vx_saturation_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 vx_saturation_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *chip = snd_kcontrol_chip(kcontrol);
+	struct vx_vu_meter meter[2];
+	int audio = kcontrol->private_value & 0xff;
+
+	vx_get_audio_vu_meter(chip, audio, 1, meter); /* capture only */
+	ucontrol->value.integer.value[0] = meter[0].saturated;
+	ucontrol->value.integer.value[1] = meter[1].saturated;
+	return 0;
+}
+
+static snd_kcontrol_new_t vx_control_vu_meter = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	/* name will be filled later */
+	.info =		vx_vu_meter_info,
+	.get =		vx_vu_meter_get,
+};
+
+static snd_kcontrol_new_t vx_control_peak_meter = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	/* name will be filled later */
+	.info =		vx_vu_meter_info,	/* shared */
+	.get =		vx_peak_meter_get,
+};
+
+static snd_kcontrol_new_t vx_control_saturation = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Input Saturation",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info =		vx_saturation_info,
+	.get =		vx_saturation_get,
+};
+
+
+
+/*
+ *
+ */
+
+int snd_vx_mixer_new(vx_core_t *chip)
+{
+	unsigned int i, c;
+	int err;
+	snd_kcontrol_new_t temp;
+	snd_card_t *card = chip->card;
+	char name[32];
+
+	strcpy(card->mixername, card->driver);
+
+	/* output level controls */
+	for (i = 0; i < chip->hw->num_outs; i++) {
+		temp = vx_control_output_level;
+		temp.index = i;
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+			return err;
+	}
+
+	/* PCM volumes, switches, monitoring */
+	for (i = 0; i < chip->hw->num_outs; i++) {
+		int val = i * 2;
+		temp = vx_control_audio_gain;
+		temp.index = i;
+		temp.name = "PCM Playback Volume";
+		temp.private_value = val;
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+			return err;
+		temp = vx_control_output_switch;
+		temp.index = i;
+		temp.private_value = val;
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+			return err;
+		temp = vx_control_monitor_gain;
+		temp.index = i;
+		temp.private_value = val;
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+			return err;
+		temp = vx_control_monitor_switch;
+		temp.index = i;
+		temp.private_value = val;
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+			return err;
+	}
+	for (i = 0; i < chip->hw->num_outs; i++) {
+		temp = vx_control_audio_gain;
+		temp.index = i;
+		temp.name = "PCM Capture Volume";
+		temp.private_value = (i * 2) | (1 << 8);
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+			return err;
+	}
+
+	/* Audio source */
+	if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_audio_src, chip))) < 0)
+		return err;
+	/* IEC958 controls */
+	if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958_mask, chip))) < 0)
+		return err;
+	if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip))) < 0)
+		return err;
+	/* VU, peak, saturation meters */
+	for (c = 0; c < 1; c++) {
+		static char *dir[2] = { "Output", "Input" };
+		for (i = 0; i < chip->hw->num_ins; i++) {
+			int val = (i * 2) | (c << 8);
+			if (c == 1) {
+				temp = vx_control_saturation;
+				temp.index = i;
+				temp.private_value = val;
+				if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+					return err;
+			}
+			sprintf(name, "%s VU Meter", dir[c]);
+			temp = vx_control_vu_meter;
+			temp.index = i;
+			temp.name = name;
+			temp.private_value = val;
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+				return err;
+			sprintf(name, "%s Peak Meter", dir[c]);
+			temp = vx_control_peak_meter;
+			temp.index = i;
+			temp.name = name;
+			temp.private_value = val;
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+				return err;
+		}
+	}
+	vx_reset_audio_levels(chip);
+	return 0;
+}
diff -urN linux-2.5.70-bk6/sound/drivers/vx/vx_pcm.c linux-2.5.70-bk7/sound/drivers/vx/vx_pcm.c
--- linux-2.5.70-bk6/sound/drivers/vx/vx_pcm.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/vx/vx_pcm.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,1307 @@
+/*
+ * Driver for Digigram VX soundcards
+ *
+ * PCM part
+ *
+ * Copyright (c) 2002,2003 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
+ *
+ *
+ * STRATEGY
+ *  for playback, we send series of "chunks", which size is equal with the
+ *  IBL size, typically 126 samples.  at each end of chunk, the end-of-buffer
+ *  interrupt is notified, and the interrupt handler will feed the next chunk.
+ *
+ *  the current position is calculated from the sample count RMH.
+ *  pipe->transferred is the counter of data which has been already transferred.
+ *  if this counter reaches to the period size, snd_pcm_period_elapsed() will
+ *  be issued.
+ *
+ *  for capture, the situation is much easier.
+ *  to get a low latency response, we'll check the capture streams at each
+ *  interrupt (capture stream has no EOB notification).  if the pending
+ *  data is accumulated to the period size, snd_pcm_period_elapsed() is
+ *  called and the pointer is updated.
+ *
+ *  the current point of read buffer is kept in pipe->hw_ptr.  note that
+ *  this is in bytes.
+ *
+ *
+ * TODO
+ *  - linked trigger for full-duplex mode.
+ *  - scheduled action on the stream.
+ */
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+#include <sound/asoundef.h>
+#include <sound/pcm.h>
+#include <sound/vx_core.h>
+#include "vx_cmd.h"
+
+#define chip_t	vx_core_t
+
+
+/*
+ * we use a vmalloc'ed (sg-)buffer
+ */
+
+/* get the physical page pointer on the given offset */
+static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, unsigned long offset)
+{
+	void *pageptr = subs->runtime->dma_area + offset;
+	return vmalloc_to_page(pageptr);
+}
+
+/*
+ * allocate a buffer via vmalloc_32().
+ * called from hw_params
+ * NOTE: this may be called not only once per pcm open!
+ */
+static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size)
+{
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	if (runtime->dma_area) {
+		/* already allocated */
+		if (runtime->dma_bytes >= size)
+			return 0; /* already enough large */
+		vfree_nocheck(runtime->dma_area); /* bypass the memory wrapper */
+	}
+	runtime->dma_area = vmalloc_32(size);
+	if (! runtime->dma_area)
+		return -ENOMEM;
+	memset(runtime->dma_area, 0, size);
+	runtime->dma_bytes = size;
+	return 1; /* changed */
+}
+
+/*
+ * free the buffer.
+ * called from hw_free callback
+ * NOTE: this may be called not only once per pcm open!
+ */
+static int snd_pcm_free_vmalloc_buffer(snd_pcm_substream_t *subs)
+{
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	if (runtime->dma_area) {
+		vfree_nocheck(runtime->dma_area); /* bypass the memory wrapper */
+		runtime->dma_area = NULL;
+	}
+	return 0;
+}
+
+
+/*
+ * read three pending pcm bytes via inb()
+ */
+static void vx_pcm_read_per_bytes(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe)
+{
+	int offset = pipe->hw_ptr;
+	unsigned char *buf = (unsigned char *)(runtime->dma_area + offset);
+	*buf++ = vx_inb(chip, RXH);
+	if (++offset >= pipe->buffer_bytes) {
+		offset = 0;
+		buf = (unsigned char *)runtime->dma_area;
+	}
+	*buf++ = vx_inb(chip, RXM);
+	if (++offset >= pipe->buffer_bytes) {
+		offset = 0;
+		buf = (unsigned char *)runtime->dma_area;
+	}
+	*buf++ = vx_inb(chip, RXL);
+	if (++offset >= pipe->buffer_bytes) {
+		offset = 0;
+		buf = (unsigned char *)runtime->dma_area;
+	}
+	pipe->hw_ptr = offset;
+}
+
+/*
+ * vx_set_pcx_time - convert from the PC time to the RMH status time.
+ * @pc_time: the pointer for the PC-time to set
+ * @dsp_time: the pointer for RMH status time array
+ */
+static void vx_set_pcx_time(vx_core_t *chip, pcx_time_t *pc_time, unsigned int *dsp_time)
+{
+	dsp_time[0] = (unsigned int)((*pc_time) >> 24) & PCX_TIME_HI_MASK;
+	dsp_time[1] = (unsigned int)(*pc_time) &  MASK_DSP_WORD;
+}
+
+/*
+ * vx_set_differed_time - set the differed time if specified
+ * @rmh: the rmh record to modify
+ * @pipe: the pipe to be checked
+ *
+ * if the pipe is programmed with the differed time, set the DSP time
+ * on the rmh and changes its command length.
+ *
+ * returns the increase of the command length.
+ */
+static int vx_set_differed_time(vx_core_t *chip, struct vx_rmh *rmh, vx_pipe_t *pipe)
+{
+	/* Update The length added to the RMH command by the timestamp */
+	if (! (pipe->differed_type & DC_DIFFERED_DELAY))
+		return 0;
+		
+	/* Set the T bit */
+	rmh->Cmd[0] |= DSP_DIFFERED_COMMAND_MASK;
+
+	/* Time stamp is the 1st following parameter */
+	vx_set_pcx_time(chip, &pipe->pcx_time, &rmh->Cmd[1]);
+
+	/* Add the flags to a notified differed command */
+	if (pipe->differed_type & DC_NOTIFY_DELAY)
+		rmh->Cmd[1] |= NOTIFY_MASK_TIME_HIGH ;
+
+	/* Add the flags to a multiple differed command */
+	if (pipe->differed_type & DC_MULTIPLE_DELAY)
+		rmh->Cmd[1] |= MULTIPLE_MASK_TIME_HIGH;
+
+	/* Add the flags to a stream-time differed command */
+	if (pipe->differed_type & DC_STREAM_TIME_DELAY)
+		rmh->Cmd[1] |= STREAM_MASK_TIME_HIGH;
+		
+	rmh->LgCmd += 2;
+	return 2;
+}
+
+/*
+ * vx_set_stream_format - send the stream format command
+ * @pipe: the affected pipe
+ * @data: format bitmask
+ */
+static int vx_set_stream_format(vx_core_t *chip, vx_pipe_t *pipe, unsigned int data)
+{
+	struct vx_rmh rmh;
+
+	vx_init_rmh(&rmh, pipe->is_capture ?
+		    CMD_FORMAT_STREAM_IN : CMD_FORMAT_STREAM_OUT);
+	rmh.Cmd[0] |= pipe->number << FIELD_SIZE;
+
+        /* Command might be longer since we may have to add a timestamp */
+	vx_set_differed_time(chip, &rmh, pipe);
+
+	rmh.Cmd[rmh.LgCmd] = (data & 0xFFFFFF00) >> 8;
+	rmh.Cmd[rmh.LgCmd + 1] = (data & 0xFF) << 16 /*| (datal & 0xFFFF00) >> 8*/;
+	rmh.LgCmd += 2;
+    
+	return vx_send_msg(chip, &rmh);
+}
+
+
+/*
+ * vx_set_format - set the format of a pipe
+ * @pipe: the affected pipe
+ * @runtime: pcm runtime instance to be referred
+ *
+ * returns 0 if successful, or a negative error code.
+ */
+static int vx_set_format(vx_core_t *chip, vx_pipe_t *pipe,
+			 snd_pcm_runtime_t *runtime)
+{
+	unsigned int header = HEADER_FMT_BASE;
+
+	if (runtime->channels == 1)
+		header |= HEADER_FMT_MONO;
+	if (snd_pcm_format_little_endian(runtime->format))
+		header |= HEADER_FMT_INTEL;
+	if (runtime->rate < 32000 && runtime->rate > 11025)
+		header |= HEADER_FMT_UPTO32;
+	else if (runtime->rate <= 11025)
+		header |= HEADER_FMT_UPTO11;
+
+	switch (snd_pcm_format_physical_width(runtime->format)) {
+	// case 8: break;
+	case 16: header |= HEADER_FMT_16BITS; break;
+	case 24: header |= HEADER_FMT_24BITS; break;
+	default : 
+		snd_BUG();
+		return -EINVAL;
+        };
+
+	return vx_set_stream_format(chip, pipe, header);
+}
+
+/*
+ * set / query the IBL size
+ */
+static int vx_set_ibl(vx_core_t *chip, struct vx_ibl_info *info)
+{
+	int err;
+	struct vx_rmh rmh;
+
+	vx_init_rmh(&rmh, CMD_IBL);
+	rmh.Cmd[0] |= info->size & 0x03ffff;
+	err = vx_send_msg(chip, &rmh);
+	if (err < 0)
+		return err;
+	info->size = rmh.Stat[0];
+	info->max_size = rmh.Stat[1];
+	info->min_size = rmh.Stat[2];
+	info->granularity = rmh.Stat[3];
+	snd_printdd(KERN_DEBUG "vx_set_ibl: size = %d, max = %d, min = %d, gran = %d\n",
+		   info->size, info->max_size, info->min_size, info->granularity);
+	return 0;
+}
+
+
+/*
+ * vx_get_pipe_state - get the state of a pipe
+ * @pipe: the pipe to be checked
+ * @state: the pointer for the returned state
+ *
+ * checks the state of a given pipe, and stores the state (1 = running,
+ * 0 = paused) on the given pointer.
+ *
+ * called from trigger callback only
+ */
+static int vx_get_pipe_state(vx_core_t *chip, vx_pipe_t *pipe, int *state)
+{
+	int err;
+	struct vx_rmh rmh;
+
+	vx_init_rmh(&rmh, CMD_PIPE_STATE);
+	vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
+	err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ 
+	if (! err)
+		*state = (rmh.Stat[0] & (1 << pipe->number)) ? 1 : 0;
+	return err;
+}
+
+
+/*
+ * vx_query_hbuffer_size - query available h-buffer size in bytes
+ * @pipe: the pipe to be checked
+ *
+ * return the available size on h-buffer in bytes,
+ * or a negative error code.
+ *
+ * NOTE: calling this function always switches to the stream mode.
+ *       you'll need to disconnect the host to get back to the
+ *       normal mode.
+ */
+static int vx_query_hbuffer_size(vx_core_t *chip, vx_pipe_t *pipe)
+{
+	int result;
+	struct vx_rmh rmh;
+
+	vx_init_rmh(&rmh, CMD_SIZE_HBUFFER);
+	vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
+	if (pipe->is_capture)
+		rmh.Cmd[0] |= 0x00000001;
+	result = vx_send_msg(chip, &rmh);
+	if (! result)
+		result = rmh.Stat[0] & 0xffff;
+	return result;
+}
+
+
+/*
+ * vx_pipe_can_start - query whether a pipe is ready for start
+ * @pipe: the pipe to be checked
+ *
+ * return 1 if ready, 0 if not ready, and negative value on error.
+ *
+ * called from trigger callback only
+ */
+static int vx_pipe_can_start(vx_core_t *chip, vx_pipe_t *pipe)
+{
+	int err;
+	struct vx_rmh rmh;
+        
+	vx_init_rmh(&rmh, CMD_CAN_START_PIPE);
+	vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
+	rmh.Cmd[0] |= 1;
+
+	err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ 
+	if (! err) {
+		if (rmh.Stat[0])
+			err = 1;
+	}
+	return err;
+}
+
+/*
+ * vx_conf_pipe - tell the pipe to stand by and wait for IRQA.
+ * @pipe: the pipe to be configured
+ */
+static int vx_conf_pipe(vx_core_t *chip, vx_pipe_t *pipe)
+{
+	struct vx_rmh rmh;
+
+	vx_init_rmh(&rmh, CMD_CONF_PIPE);
+	if (pipe->is_capture)
+		rmh.Cmd[0] |= COMMAND_RECORD_MASK;
+	rmh.Cmd[1] = 1 << pipe->number;
+	return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+}
+
+/*
+ * vx_send_irqa - trigger IRQA
+ */
+static int vx_send_irqa(vx_core_t *chip)
+{
+	struct vx_rmh rmh;
+
+	vx_init_rmh(&rmh, CMD_SEND_IRQA);
+	return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ 
+}
+
+
+#define MAX_WAIT_FOR_DSP        250
+/*
+ * vx boards do not support inter-card sync, besides
+ * only 126 samples require to be prepared before a pipe can start
+ */
+#define CAN_START_DELAY         2	/* wait 2ms only before asking if the pipe is ready*/
+#define WAIT_STATE_DELAY        2	/* wait 2ms after irqA was requested and check if the pipe state toggled*/
+
+/*
+ * vx_toggle_pipe - start / pause a pipe
+ * @pipe: the pipe to be triggered
+ * @state: start = 1, pause = 0
+ *
+ * called from trigger callback only
+ *
+ */
+static int vx_toggle_pipe(vx_core_t *chip, vx_pipe_t *pipe, int state)
+{
+	int err, i, cur_state, delay;
+
+	/* Check the pipe is not already in the requested state */
+	if (vx_get_pipe_state(chip, pipe, &cur_state) < 0)
+		return -EBADFD;
+	if (state == cur_state)
+		return 0;
+
+	/* If a start is requested, ask the DSP to get prepared
+	 * and wait for a positive acknowledge (when there are
+	 * enough sound buffer for this pipe)
+	 */
+	if (state) {
+		int delay = CAN_START_DELAY;
+		for (i = 0 ; i < MAX_WAIT_FOR_DSP; i++) {
+			snd_vx_delay(chip, delay);
+			err = vx_pipe_can_start(chip, pipe);
+			if (err > 0)
+				break;
+			/* Wait for a few, before asking again
+			 * to avoid flooding the DSP with our requests
+			 */
+			if ((i % 4 ) == 0)
+				delay <<= 1;
+		}
+	}
+    
+	if ((err = vx_conf_pipe(chip, pipe)) < 0)
+		return err;
+
+	if ((err = vx_send_irqa(chip)) < 0)
+		return err;
+    
+	/* If it completes successfully, wait for the pipes
+	 * reaching the expected state before returning
+	 * Check one pipe only (since they are synchronous)
+	 */
+	delay = WAIT_STATE_DELAY;
+	for (i = 0; i < MAX_WAIT_FOR_DSP; i++) {
+		snd_vx_delay(chip, delay);
+		err = vx_get_pipe_state(chip, pipe, &cur_state);
+		if (err < 0 || cur_state == state)
+			break;
+		err = -EIO;
+		if ((i % 4 ) == 0)
+			delay <<= 1;
+	}
+	return err < 0 ? -EIO : 0;
+}
+
+    
+/*
+ * vx_stop_pipe - stop a pipe
+ * @pipe: the pipe to be stopped
+ *
+ * called from trigger callback only
+ */
+static int vx_stop_pipe(vx_core_t *chip, vx_pipe_t *pipe)
+{
+	struct vx_rmh rmh;
+	vx_init_rmh(&rmh, CMD_STOP_PIPE);
+	vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
+	return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ 
+}
+
+
+/*
+ * vx_alloc_pipe - allocate a pipe and initialize the pipe instance
+ * @capture: 0 = playback, 1 = capture operation
+ * @audioid: the audio id to be assigned
+ * @num_audio: number of audio channels
+ * @pipep: the returned pipe instance
+ *
+ * return 0 on success, or a negative error code.
+ */
+static int vx_alloc_pipe(vx_core_t *chip, int capture,
+			 int audioid, int num_audio,
+			 vx_pipe_t **pipep)
+{
+	int err;
+	vx_pipe_t *pipe;
+	struct vx_rmh rmh;
+	int data_mode;
+
+	*pipep = 0;
+	vx_init_rmh(&rmh, CMD_RES_PIPE);
+	vx_set_pipe_cmd_params(&rmh, capture, audioid, num_audio);
+#if 0	// NYI
+	if (underrun_skip_sound)
+		rmh.Cmd[0] |= BIT_SKIP_SOUND;
+#endif	// NYI
+	data_mode = (chip->uer_bits & IEC958_AES0_NONAUDIO) != 0;
+	if (! capture && data_mode)
+		rmh.Cmd[0] |= BIT_DATA_MODE;
+	err = vx_send_msg(chip, &rmh);
+	if (err < 0)
+		return err;
+
+	/* initialize the pipe record */
+	pipe = snd_magic_kcalloc(vx_pipe_t, 0, GFP_KERNEL);
+	if (! pipe) {
+		/* release the pipe */
+		vx_init_rmh(&rmh, CMD_FREE_PIPE);
+		vx_set_pipe_cmd_params(&rmh, capture, audioid, 0);
+		vx_send_msg(chip, &rmh);
+		return -ENOMEM;
+	}
+
+	/* the pipe index should be identical with the audio index */
+	pipe->number = audioid;
+	pipe->is_capture = capture;
+	pipe->channels = num_audio;
+	pipe->differed_type = 0;
+	pipe->pcx_time = 0;
+	pipe->data_mode = data_mode;
+	*pipep = pipe;
+
+	return 0;
+}
+
+
+/*
+ * vx_free_pipe - release a pipe
+ * @pipe: pipe to be released
+ */
+static int vx_free_pipe(vx_core_t *chip, vx_pipe_t *pipe)
+{
+	struct vx_rmh rmh;
+
+	vx_init_rmh(&rmh, CMD_FREE_PIPE);
+	vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
+	vx_send_msg(chip, &rmh);
+
+	snd_magic_kfree(pipe);
+	return 0;
+}
+
+
+/*
+ * vx_start_stream - start the stream
+ *
+ * called from trigger callback only
+ */
+static int vx_start_stream(vx_core_t *chip, vx_pipe_t *pipe)
+{
+	struct vx_rmh rmh;
+
+	vx_init_rmh(&rmh, CMD_START_ONE_STREAM);
+	vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number);
+	vx_set_differed_time(chip, &rmh, pipe);
+	return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ 
+}
+
+
+/*
+ * vx_stop_stream - stop the stream
+ *
+ * called from trigger callback only
+ */
+static int vx_stop_stream(vx_core_t *chip, vx_pipe_t *pipe)
+{
+	struct vx_rmh rmh;
+
+	vx_init_rmh(&rmh, CMD_STOP_STREAM);
+	vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number);
+	return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */ 
+}
+
+
+/*
+ * playback hw information
+ */
+
+static snd_pcm_hardware_t vx_pcm_playback_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		/*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5000,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	126,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		VX_MAX_PERIODS,
+	.fifo_size =		126,
+};
+
+
+static void vx_pcm_delayed_start(unsigned long arg);
+
+/*
+ * vx_pcm_playback_open - open callback for playback
+ */
+static int vx_pcm_playback_open(snd_pcm_substream_t *subs)
+{
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	vx_core_t *chip = snd_pcm_substream_chip(subs);
+	vx_pipe_t *pipe = 0;
+	unsigned int audio;
+	int err;
+
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return -EBUSY;
+
+	audio = subs->pcm->device * 2;
+	snd_assert(audio < chip->audio_outs, return -EINVAL);
+	
+	/* playback pipe may have been already allocated for monitoring */
+	pipe = chip->playback_pipes[audio];
+	if (! pipe) {
+		/* not allocated yet */
+		err = vx_alloc_pipe(chip, 0, audio, 2, &pipe); /* stereo playback */
+		if (err < 0)
+			return err;
+		chip->playback_pipes[audio] = pipe;
+	}
+	/* open for playback */
+	pipe->references++;
+
+	pipe->substream = subs;
+	tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs);
+	chip->playback_pipes[audio] = pipe;
+
+	runtime->hw = vx_pcm_playback_hw;
+	runtime->hw.period_bytes_min = chip->ibl.size;
+	runtime->private_data = pipe;
+
+	return 0;
+}
+
+/*
+ * vx_pcm_playback_close - close callback for playback
+ */
+static int vx_pcm_playback_close(snd_pcm_substream_t *subs)
+{
+	vx_core_t *chip = snd_pcm_substream_chip(subs);
+	vx_pipe_t *pipe;
+
+	if (! subs->runtime->private_data)
+		return -EINVAL;
+
+	pipe = snd_magic_cast(vx_pipe_t, subs->runtime->private_data, return -EINVAL);
+
+	if (--pipe->references == 0) {
+		chip->playback_pipes[pipe->number] = 0;
+		vx_free_pipe(chip, pipe);
+	}
+
+	return 0;
+
+}
+
+
+/*
+ * vx_notify_end_of_buffer - send "end-of-buffer" notifier at the given pipe
+ * @pipe: the pipe to notify
+ *
+ * NB: call with a certain lock.
+ */
+static int vx_notify_end_of_buffer(vx_core_t *chip, vx_pipe_t *pipe)
+{
+	int err;
+	struct vx_rmh rmh;  /* use a temporary rmh here */
+
+	/* Toggle Dsp Host Interface into Message mode */
+	vx_send_rih_nolock(chip, IRQ_PAUSE_START_CONNECT);
+	vx_init_rmh(&rmh, CMD_NOTIFY_END_OF_BUFFER);
+	vx_set_stream_cmd_params(&rmh, 0, pipe->number);
+	err = vx_send_msg_nolock(chip, &rmh);
+	if (err < 0)
+		return err;
+	/* Toggle Dsp Host Interface back to sound transfer mode */
+	vx_send_rih_nolock(chip, IRQ_PAUSE_START_CONNECT);
+	return 0;
+}
+
+/*
+ * vx_pcm_playback_transfer_chunk - transfer a single chunk
+ * @subs: substream
+ * @pipe: the pipe to transfer
+ * @size: chunk size in bytes
+ *
+ * transfer a single buffer chunk.  EOB notificaton is added after that.
+ * called from the interrupt handler, too.
+ *
+ * return 0 if ok.
+ */
+static int vx_pcm_playback_transfer_chunk(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe, int size)
+{
+	int space, err = 0;
+
+	space = vx_query_hbuffer_size(chip, pipe);
+	if (space < 0) {
+		/* disconnect the host, SIZE_HBUF command always switches to the stream mode */
+		vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT);
+		snd_printd("error hbuffer\n");
+		return space;
+	}
+	if (space < size) {
+		vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT);
+		snd_printd("no enough hbuffer space %d\n", space);
+		return -EIO; /* XRUN */
+	}
+		
+	/* we don't need irqsave here, because this function
+	 * is called from either trigger callback or irq handler
+	 */
+	spin_lock(&chip->lock); 
+	vx_pseudo_dma_write(chip, runtime, pipe, size);
+	err = vx_notify_end_of_buffer(chip, pipe);
+	/* disconnect the host, SIZE_HBUF command always switches to the stream mode */
+	vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT);
+	spin_unlock(&chip->lock);
+	return err;
+}
+
+/*
+ * update the position of the given pipe.
+ * pipe->position is updated and wrapped within the buffer size.
+ * pipe->transferred is updated, too, but the size is not wrapped,
+ * so that the caller can check the total transferred size later
+ * (to call snd_pcm_period_elapsed).
+ */
+static int vx_update_pipe_position(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe)
+{
+	struct vx_rmh rmh;
+	int err, update;
+	u64 count;
+
+	vx_init_rmh(&rmh, CMD_STREAM_SAMPLE_COUNT);
+	vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
+	err = vx_send_msg(chip, &rmh);
+	if (err < 0)
+		return err;
+
+	count = ((u64)(rmh.Stat[0] & 0xfffff) << 24) | (u64)rmh.Stat[1];
+	update = (int)(count - pipe->cur_count);
+	pipe->cur_count = count;
+	pipe->position += update;
+	if (pipe->position >= (int)runtime->buffer_size)
+		pipe->position %= runtime->buffer_size;
+	pipe->transferred += update;
+	return 0;
+}
+
+/*
+ * transfer the pending playback buffer data to DSP
+ * called from interrupt handler
+ */
+static void vx_pcm_playback_transfer(vx_core_t *chip, snd_pcm_substream_t *subs, vx_pipe_t *pipe, int nchunks)
+{
+	int i, err;
+	snd_pcm_runtime_t *runtime = subs->runtime;
+
+	if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE))
+		return;
+	for (i = 0; i < nchunks; i++) {
+		if ((err = vx_pcm_playback_transfer_chunk(chip, runtime, pipe,
+							  chip->ibl.size)) < 0)
+			return;
+	}
+}
+
+/*
+ * update the playback position and call snd_pcm_period_elapsed() if necessary
+ * called from interrupt handler
+ */
+static void vx_pcm_playback_update(vx_core_t *chip, snd_pcm_substream_t *subs, vx_pipe_t *pipe)
+{
+	int err;
+	snd_pcm_runtime_t *runtime = subs->runtime;
+
+	if (pipe->running && ! (chip->chip_status & VX_STAT_IS_STALE)) {
+		if ((err = vx_update_pipe_position(chip, runtime, pipe)) < 0)
+			return;
+		if (pipe->transferred >= (int)runtime->period_size) {
+			pipe->transferred %= runtime->period_size;
+			snd_pcm_period_elapsed(subs);
+		}
+	}
+}
+
+/*
+ * start the stream and pipe.
+ * this function is called from tasklet, which is invoked by the trigger
+ * START callback.
+ */
+static void vx_pcm_delayed_start(unsigned long arg)
+{
+	snd_pcm_substream_t *subs = (snd_pcm_substream_t *)arg;
+	vx_core_t *chip = snd_magic_cast(vx_core_t, subs->pcm->private_data, return);
+	vx_pipe_t *pipe = snd_magic_cast(vx_pipe_t, subs->runtime->private_data, return);
+	int err;
+
+	/*  printk( KERN_DEBUG "DDDD tasklet delayed start jiffies = %ld\n", jiffies);*/
+
+	if ((err = vx_start_stream(chip, pipe)) < 0) {
+		snd_printk(KERN_ERR "vx: cannot start stream\n");
+		return;
+	}
+	if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0) {
+		snd_printk(KERN_ERR "vx: cannot start pipe\n");
+		return;
+	}
+	/*   printk( KERN_DEBUG "dddd tasklet delayed start jiffies = %ld \n", jiffies);*/
+}
+
+/*
+ * vx_pcm_playback_trigger - trigger callback for playback
+ */
+static int vx_pcm_trigger(snd_pcm_substream_t *subs, int cmd)
+{
+	vx_core_t *chip = snd_pcm_substream_chip(subs);
+	vx_pipe_t *pipe = snd_magic_cast(vx_pipe_t, subs->runtime->private_data, return -EINVAL);
+	int err;
+
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return -EBUSY;
+		
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (! pipe->is_capture)
+			vx_pcm_playback_transfer(chip, subs, pipe, 2);
+		/* FIXME:
+		 * we trigger the pipe using tasklet, so that the interrupts are
+		 * issued surely after the trigger is completed.
+		 */ 
+		tasklet_hi_schedule(&pipe->start_tq);
+		chip->pcm_running++;
+		pipe->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		vx_toggle_pipe(chip, pipe, 0);
+		vx_stop_pipe(chip, pipe);
+		vx_stop_stream(chip, pipe);
+		chip->pcm_running--;
+		pipe->running = 0;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if ((err = vx_toggle_pipe(chip, pipe, 0)) < 0)
+			return err;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0)
+			return err;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ * vx_pcm_playback_pointer - pointer callback for playback
+ */
+static snd_pcm_uframes_t vx_pcm_playback_pointer(snd_pcm_substream_t *subs)
+{
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	vx_pipe_t *pipe = snd_magic_cast(vx_pipe_t, runtime->private_data, return -EINVAL);
+	return pipe->position;
+}
+
+/*
+ * vx_pcm_hw_params - hw_params callback for playback and capture
+ */
+static int vx_pcm_hw_params(snd_pcm_substream_t *subs,
+				     snd_pcm_hw_params_t *hw_params)
+{
+	return snd_pcm_alloc_vmalloc_buffer(subs, params_buffer_bytes(hw_params));
+}
+
+/*
+ * vx_pcm_hw_free - hw_free callback for playback and capture
+ */
+static int vx_pcm_hw_free(snd_pcm_substream_t *subs)
+{
+	return snd_pcm_free_vmalloc_buffer(subs);
+}
+
+/*
+ * vx_pcm_prepare - prepare callback for playback and capture
+ */
+static int vx_pcm_prepare(snd_pcm_substream_t *subs)
+{
+	vx_core_t *chip = snd_pcm_substream_chip(subs);
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	vx_pipe_t *pipe = snd_magic_cast(vx_pipe_t, runtime->private_data, return -EINVAL);
+	int err, data_mode;
+	// int max_size, nchunks;
+
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return -EBUSY;
+
+	data_mode = (chip->uer_bits & IEC958_AES0_NONAUDIO) != 0;
+	if (data_mode != pipe->data_mode && ! pipe->is_capture) {
+		/* IEC958 status (raw-mode) was changed */
+		/* we reopen the pipe */
+		struct vx_rmh rmh;
+		snd_printdd(KERN_DEBUG "reopen the pipe with data_mode = %d\n", data_mode);
+		vx_init_rmh(&rmh, CMD_FREE_PIPE);
+		vx_set_pipe_cmd_params(&rmh, 0, pipe->number, 0);
+		if ((err = vx_send_msg(chip, &rmh)) < 0)
+			return err;
+		vx_init_rmh(&rmh, CMD_RES_PIPE);
+		vx_set_pipe_cmd_params(&rmh, 0, pipe->number, pipe->channels);
+		if (data_mode)
+			rmh.Cmd[0] |= BIT_DATA_MODE;
+		if ((err = vx_send_msg(chip, &rmh)) < 0)
+			return err;
+		pipe->data_mode = data_mode;
+	}
+
+	if (chip->pcm_running && chip->freq != runtime->rate) {
+		snd_printk(KERN_ERR "vx: cannot set different clock %d from the current %d\n", runtime->rate, chip->freq);
+		return -EINVAL;
+	}
+	vx_set_clock(chip, runtime->rate);
+
+	if ((err = vx_set_format(chip, pipe, runtime)) < 0)
+		return err;
+
+	if (vx_is_pcmcia(chip)) {
+		pipe->align = 2; /* 16bit word */
+	} else {
+		pipe->align = 4; /* 32bit word */
+	}
+
+	pipe->buffer_bytes = frames_to_bytes(runtime, runtime->buffer_size);
+	pipe->period_bytes = frames_to_bytes(runtime, runtime->period_size);
+	pipe->hw_ptr = 0;
+
+	/* set the timestamp */
+	vx_update_pipe_position(chip, runtime, pipe);
+	/* clear again */
+	pipe->transferred = 0;
+	pipe->position = 0;
+
+	pipe->prepared = 1;
+
+	return 0;
+}
+
+
+/*
+ * operators for PCM playback
+ */
+static snd_pcm_ops_t vx_pcm_playback_ops = {
+	.open =		vx_pcm_playback_open,
+	.close =	vx_pcm_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	vx_pcm_hw_params,
+	.hw_free =	vx_pcm_hw_free,
+	.prepare =	vx_pcm_prepare,
+	.trigger =	vx_pcm_trigger,
+	.pointer =	vx_pcm_playback_pointer,
+	.page =		snd_pcm_get_vmalloc_page,
+};
+
+
+/*
+ * playback hw information
+ */
+
+static snd_pcm_hardware_t vx_pcm_capture_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		/*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5000,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	126,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		VX_MAX_PERIODS,
+	.fifo_size =		126,
+};
+
+
+/*
+ * vx_pcm_capture_open - open callback for capture
+ */
+static int vx_pcm_capture_open(snd_pcm_substream_t *subs)
+{
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	vx_core_t *chip = snd_pcm_substream_chip(subs);
+	vx_pipe_t *pipe;
+	vx_pipe_t *pipe_out_monitoring = NULL;
+	unsigned int audio;
+	int err;
+
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return -EBUSY;
+
+	audio = subs->pcm->device * 2;
+	snd_assert(audio < chip->audio_ins, return -EINVAL);
+	err = vx_alloc_pipe(chip, 1, audio, 2, &pipe);
+	if (err < 0)
+		return err;
+	pipe->substream = subs;
+	tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs);
+	chip->capture_pipes[audio] = pipe;
+
+	/* check if monitoring is needed */
+	if (chip->audio_monitor_active[audio]) {
+		pipe_out_monitoring = chip->playback_pipes[audio];
+		if (! pipe_out_monitoring) {
+			/* allocate a pipe */
+			err = vx_alloc_pipe(chip, 0, audio, 2, &pipe_out_monitoring);
+			if (err < 0)
+				return err;
+			chip->playback_pipes[audio] = pipe_out_monitoring;
+		}
+		pipe_out_monitoring->references++;
+		/* 
+		   if an output pipe is available, it's audios still may need to be 
+		   unmuted. hence we'll have to call a mixer entry point.
+		*/
+		vx_set_monitor_level(chip, audio, chip->audio_monitor[audio], chip->audio_monitor_active[audio]);
+		/* assuming stereo */
+		vx_set_monitor_level(chip, audio+1, chip->audio_monitor[audio+1], chip->audio_monitor_active[audio+1]); 
+	}
+
+	pipe->monitoring_pipe = pipe_out_monitoring; /* default value NULL */
+
+	runtime->hw = vx_pcm_capture_hw;
+	runtime->hw.period_bytes_min = chip->ibl.size;
+	runtime->private_data = pipe;
+
+	return 0;
+}
+
+/*
+ * vx_pcm_capture_close - close callback for capture
+ */
+static int vx_pcm_capture_close(snd_pcm_substream_t *subs)
+{
+	vx_core_t *chip = snd_pcm_substream_chip(subs);
+	vx_pipe_t *pipe;
+	vx_pipe_t *pipe_out_monitoring;
+	
+	if (! subs->runtime->private_data)
+		return -EINVAL;
+	pipe = snd_magic_cast(vx_pipe_t, subs->runtime->private_data, return -EINVAL);
+	chip->capture_pipes[pipe->number] = 0;
+
+	pipe_out_monitoring = pipe->monitoring_pipe;
+
+	/*
+	  if an output pipe is attached to this input, 
+	  check if it needs to be released.
+	*/
+	if (pipe_out_monitoring) {
+		if (--pipe_out_monitoring->references == 0) {
+			vx_free_pipe(chip, pipe_out_monitoring);
+			chip->playback_pipes[pipe->number] = 0;
+			pipe->monitoring_pipe = 0;
+		}
+	}
+	
+	vx_free_pipe(chip, pipe);
+	return 0;
+}
+
+
+
+#define DMA_READ_ALIGN	6	/* hardware alignment for read */
+
+/*
+ * vx_pcm_capture_update - update the capture buffer
+ */
+static void vx_pcm_capture_update(vx_core_t *chip, snd_pcm_substream_t *subs, vx_pipe_t *pipe)
+{
+	int size, space, count;
+	snd_pcm_runtime_t *runtime = subs->runtime;
+
+	if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE))
+		return;
+
+	size = runtime->buffer_size - snd_pcm_capture_avail(runtime);
+	if (! size)
+		return;
+	size = frames_to_bytes(runtime, size);
+	space = vx_query_hbuffer_size(chip, pipe);
+	if (space < 0)
+		goto _error;
+	if (size > space)
+		size = space;
+	size = (size / 3) * 3; /* align to 3 bytes */
+	if (size < DMA_READ_ALIGN)
+		goto _error;
+
+	/* keep the last 6 bytes, they will be read after disconnection */
+	count = size - DMA_READ_ALIGN;
+	/* read bytes until the current pointer reaches to the aligned position
+	 * for word-transfer
+	 */
+	while (count > 0) {
+		if ((pipe->hw_ptr % pipe->align) == 0)
+			break;
+		if (vx_wait_for_rx_full(chip) < 0)
+			goto _error;
+		vx_pcm_read_per_bytes(chip, runtime, pipe);
+		count -= 3;
+	}
+	if (count > 0) {
+		/* ok, let's accelerate! */
+		int align = pipe->align * 3;
+		space = (count / align) * align;
+		vx_pseudo_dma_read(chip, runtime, pipe, space);
+		count -= space;
+	}
+	/* read the rest of bytes */
+	while (count > 0) {
+		if (vx_wait_for_rx_full(chip) < 0)
+			goto _error;
+		vx_pcm_read_per_bytes(chip, runtime, pipe);
+		count -= 3;
+	}
+	/* disconnect the host, SIZE_HBUF command always switches to the stream mode */
+	vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT);
+	/* read the last pending 6 bytes */
+	count = DMA_READ_ALIGN;
+	while (count > 0) {
+		vx_pcm_read_per_bytes(chip, runtime, pipe);
+		count -= 3;
+	}
+	/* update the position */
+	pipe->transferred += size;
+	if (pipe->transferred >= pipe->period_bytes) {
+		pipe->transferred %= pipe->period_bytes;
+		snd_pcm_period_elapsed(subs);
+	}
+	return;
+
+ _error:
+	/* disconnect the host, SIZE_HBUF command always switches to the stream mode */
+	vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT);
+	return;
+}
+
+/*
+ * vx_pcm_capture_pointer - pointer callback for capture
+ */
+static snd_pcm_uframes_t vx_pcm_capture_pointer(snd_pcm_substream_t *subs)
+{
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	vx_pipe_t *pipe = snd_magic_cast(vx_pipe_t, runtime->private_data, return -EINVAL);
+	return bytes_to_frames(runtime, pipe->hw_ptr);
+}
+
+/*
+ * operators for PCM capture
+ */
+static snd_pcm_ops_t vx_pcm_capture_ops = {
+	.open =		vx_pcm_capture_open,
+	.close =	vx_pcm_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	vx_pcm_hw_params,
+	.hw_free =	vx_pcm_hw_free,
+	.prepare =	vx_pcm_prepare,
+	.trigger =	vx_pcm_trigger,
+	.pointer =	vx_pcm_capture_pointer,
+	.page =		snd_pcm_get_vmalloc_page,
+};
+
+
+/*
+ * interrupt handler for pcm streams
+ */
+void vx_pcm_update_intr(vx_core_t *chip, unsigned int events)
+{
+	unsigned int i;
+	vx_pipe_t *pipe;
+
+#define EVENT_MASK	(END_OF_BUFFER_EVENTS_PENDING|ASYNC_EVENTS_PENDING)
+
+	if (events & EVENT_MASK) {
+		vx_init_rmh(&chip->irq_rmh, CMD_ASYNC);
+		if (events & ASYNC_EVENTS_PENDING)
+			chip->irq_rmh.Cmd[0] |= 0x00000001;	/* SEL_ASYNC_EVENTS */
+		if (events & END_OF_BUFFER_EVENTS_PENDING)
+			chip->irq_rmh.Cmd[0] |= 0x00000002;	/* SEL_END_OF_BUF_EVENTS */
+
+		if (vx_send_msg(chip, &chip->irq_rmh) < 0) {
+			snd_printdd(KERN_ERR "msg send error!!\n");
+			return;
+		}
+
+		i = 1;
+		while (i < chip->irq_rmh.LgStat) {
+			int p, buf, capture, eob;
+			p = chip->irq_rmh.Stat[i] & MASK_FIRST_FIELD;
+			capture = (chip->irq_rmh.Stat[i] & 0x400000) ? 1 : 0;
+			eob = (chip->irq_rmh.Stat[i] & 0x800000) ? 1 : 0;
+			i++;
+			if (events & ASYNC_EVENTS_PENDING)
+				i++;
+			buf = 1; /* force to transfer */
+			if (events & END_OF_BUFFER_EVENTS_PENDING) {
+				if (eob)
+					buf = chip->irq_rmh.Stat[i];
+				i++;
+			}
+			if (capture)
+				continue;
+			snd_assert(p >= 0 && (unsigned int)p < chip->audio_outs,);
+			pipe = chip->playback_pipes[p];
+			if (pipe && pipe->substream) {
+				vx_pcm_playback_update(chip, pipe->substream, pipe);
+				vx_pcm_playback_transfer(chip, pipe->substream, pipe, buf);
+			}
+		}
+	}
+
+	/* update the capture pcm pointers as frequently as possible */
+	for (i = 0; i < chip->audio_ins; i++) {
+		pipe = chip->capture_pipes[i];
+		if (pipe && pipe->substream)
+			vx_pcm_capture_update(chip, pipe->substream, pipe);
+	}
+}
+
+
+/*
+ * vx_init_audio_io - check the availabe audio i/o and allocate pipe arrays
+ */
+static int vx_init_audio_io(vx_core_t *chip)
+{
+	struct vx_rmh rmh;
+	int preferred;
+
+	vx_init_rmh(&rmh, CMD_SUPPORTED);
+	if (vx_send_msg(chip, &rmh) < 0) {
+		snd_printk(KERN_ERR "vx: cannot get the supported audio data\n");
+		return -ENXIO;
+	}
+
+	chip->audio_outs = rmh.Stat[0] & MASK_FIRST_FIELD;
+	chip->audio_ins = (rmh.Stat[0] >> (FIELD_SIZE*2)) & MASK_FIRST_FIELD;
+	chip->audio_info = rmh.Stat[1];
+
+	/* allocate pipes */
+	chip->playback_pipes = kmalloc(sizeof(vx_pipe_t *) * chip->audio_outs, GFP_KERNEL);
+	chip->capture_pipes = kmalloc(sizeof(vx_pipe_t *) * chip->audio_ins, GFP_KERNEL);
+	if (! chip->playback_pipes || ! chip->capture_pipes)
+		return -ENOMEM;
+
+	memset(chip->playback_pipes, 0, sizeof(vx_pipe_t *) * chip->audio_outs);
+	memset(chip->capture_pipes, 0, sizeof(vx_pipe_t *) * chip->audio_ins);
+
+	preferred = chip->ibl.size;
+	chip->ibl.size = 0;
+	vx_set_ibl(chip, &chip->ibl); /* query the info */
+	if (preferred > 0) {
+		chip->ibl.size = ((preferred + chip->ibl.granularity - 1) / chip->ibl.granularity) * chip->ibl.granularity;
+		if (chip->ibl.size > chip->ibl.max_size)
+			chip->ibl.size = chip->ibl.max_size;
+	} else
+		chip->ibl.size = chip->ibl.min_size; /* set to the minimum */
+	vx_set_ibl(chip, &chip->ibl);
+
+	return 0;
+}
+
+
+/*
+ * free callback for pcm
+ */
+static void snd_vx_pcm_free(snd_pcm_t *pcm)
+{
+	vx_core_t *chip = snd_magic_cast(vx_core_t, pcm->private_data, return);
+	chip->pcm[pcm->device] = NULL;
+	if (chip->playback_pipes) {
+		kfree(chip->playback_pipes);
+		chip->playback_pipes = 0;
+	}
+	if (chip->capture_pipes) {
+		kfree(chip->capture_pipes);
+		chip->capture_pipes = 0;
+	}
+}
+
+/*
+ * snd_vx_pcm_new - create and initialize a pcm
+ */
+int snd_vx_pcm_new(vx_core_t *chip)
+{
+	snd_pcm_t *pcm;
+	unsigned int i;
+	int err;
+
+	if ((err = vx_init_audio_io(chip)) < 0)
+		return err;
+
+	for (i = 0; i < chip->hw->num_codecs; i++) {
+		unsigned int outs, ins;
+		outs = chip->audio_outs > i * 2 ? 1 : 0;
+		ins = chip->audio_ins > i * 2 ? 1 : 0;
+		if (! outs && ! ins)
+			break;
+		err = snd_pcm_new(chip->card, "VX PCM", i,
+				  outs, ins, &pcm);
+		if (err < 0)
+			return err;
+		if (outs)
+			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &vx_pcm_playback_ops);
+		if (ins)
+			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &vx_pcm_capture_ops);
+
+		pcm->private_data = chip;
+		pcm->private_free = snd_vx_pcm_free;
+		pcm->info_flags = 0;
+		strcpy(pcm->name, chip->card->shortname);
+		chip->pcm[i] = pcm;
+	}
+
+	return 0;
+}
diff -urN linux-2.5.70-bk6/sound/drivers/vx/vx_uer.c linux-2.5.70-bk7/sound/drivers/vx/vx_uer.c
--- linux-2.5.70-bk6/sound/drivers/vx/vx_uer.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/drivers/vx/vx_uer.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,320 @@
+/*
+ * Driver for Digigram VX soundcards
+ *
+ * IEC958 stuff
+ *
+ * Copyright (c) 2002 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/delay.h>
+#include <sound/core.h>
+#include <sound/vx_core.h>
+#include "vx_cmd.h"
+
+
+/*
+ * vx_modify_board_clock - tell the board that its clock has been modified
+ * @sync: DSP needs to resynchronize its FIFO
+ */
+static int vx_modify_board_clock(vx_core_t *chip, int sync)
+{
+	struct vx_rmh rmh;
+
+	vx_init_rmh(&rmh, CMD_MODIFY_CLOCK);
+	/* Ask the DSP to resynchronize its FIFO. */
+	if (sync)
+		rmh.Cmd[0] |= CMD_MODIFY_CLOCK_S_BIT;
+	return vx_send_msg(chip, &rmh);
+}
+
+/*
+ * vx_modify_board_inputs - resync audio inputs
+ */
+static int vx_modify_board_inputs(vx_core_t *chip)
+{
+	struct vx_rmh rmh;
+
+	vx_init_rmh(&rmh, CMD_RESYNC_AUDIO_INPUTS);
+        rmh.Cmd[0] |= 1 << 0; /* reference: AUDIO 0 */
+	return vx_send_msg(chip, &rmh);
+}
+
+/*
+ * vx_read_one_cbit - read one bit from UER config
+ * @index: the bit index
+ * returns 0 or 1.
+ */
+static int vx_read_one_cbit(vx_core_t *chip, int index)
+{
+	unsigned long flags;
+	int val;
+	spin_lock_irqsave(&chip->lock, flags);
+	if (chip->type >= VX_TYPE_VXPOCKET) {
+		vx_outb(chip, CSUER, 1); /* read */
+		vx_outb(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK);
+		val = (vx_inb(chip, RUER) >> 7) & 0x01;
+	} else {
+		vx_outl(chip, CSUER, 1); /* read */
+		vx_outl(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK);
+		val = (vx_inl(chip, RUER) >> 7) & 0x01;
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return val;
+}
+
+/*
+ * vx_write_one_cbit - write one bit to UER config
+ * @index: the bit index
+ * @val: bit value, 0 or 1
+ */
+static void vx_write_one_cbit(vx_core_t *chip, int index, int val)
+{
+	unsigned long flags;
+	val = !!val;	/* 0 or 1 */
+	spin_lock_irqsave(&chip->lock, flags);
+	if (vx_is_pcmcia(chip)) {
+		vx_outb(chip, CSUER, 0); /* write */
+		vx_outb(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK));
+	} else {
+		vx_outl(chip, CSUER, 0); /* write */
+		vx_outl(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK));
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+/*
+ * vx_read_uer_status - read the current UER status
+ * @mode: pointer to store the UER mode, VX_UER_MODE_XXX
+ *
+ * returns the frequency of UER, or 0 if not sync,
+ * or a negative error code.
+ */
+static int vx_read_uer_status(vx_core_t *chip, int *mode)
+{
+	int val, freq;
+
+	/* Default values */
+	freq = 0;
+
+	/* Read UER status */
+	if (vx_is_pcmcia(chip))
+	    val = vx_inb(chip, CSUER);
+	else
+	    val = vx_inl(chip, CSUER);
+	if (val < 0)
+		return val;
+	/* If clock is present, read frequency */
+	if (val & VX_SUER_CLOCK_PRESENT_MASK) {
+		switch (val & VX_SUER_FREQ_MASK) {
+		case VX_SUER_FREQ_32KHz_MASK:
+			freq = 32000;
+			break;
+		case VX_SUER_FREQ_44KHz_MASK:
+			freq = 44100;
+			break;
+		case VX_SUER_FREQ_48KHz_MASK:
+			freq = 48000;
+			break;
+		}
+        }
+	if (val & VX_SUER_DATA_PRESENT_MASK)
+		/* bit 0 corresponds to consumer/professional bit */
+		*mode = vx_read_one_cbit(chip, 0) ?
+			VX_UER_MODE_PROFESSIONAL : VX_UER_MODE_CONSUMER;
+	else
+		*mode = VX_UER_MODE_NOT_PRESENT;
+
+	return freq;
+}
+
+
+/*
+ * compute the sample clock value from frequency
+ *
+ * The formula is as follows:
+ *
+ *    HexFreq = (dword) ((double) ((double) 28224000 / (double) Frequency))
+ *    switch ( HexFreq & 0x00000F00 )
+ *    case 0x00000100: ;
+ *    case 0x00000200:
+ *    case 0x00000300: HexFreq -= 0x00000201 ;
+ *    case 0x00000400:
+ *    case 0x00000500:
+ *    case 0x00000600:
+ *    case 0x00000700: HexFreq = (dword) (((double) 28224000 / (double) (Frequency*2)) - 1)
+ *    default        : HexFreq = (dword) ((double) 28224000 / (double) (Frequency*4)) - 0x000001FF
+ */
+
+static int vx_calc_clock_from_freq(vx_core_t *chip, int freq)
+{
+#define XX_FECH48000                    0x0000004B
+#define XX_FECH32000                    0x00000171
+#define XX_FECH24000                    0x0000024B
+#define XX_FECH16000                    0x00000371
+#define XX_FECH12000                    0x0000044B
+#define XX_FECH8000                     0x00000571
+#define XX_FECH44100                    0x0000007F
+#define XX_FECH29400                    0x0000016F
+#define XX_FECH22050                    0x0000027F
+#define XX_FECH14000                    0x000003EF
+#define XX_FECH11025                    0x0000047F
+#define XX_FECH7350                     0x000005BF
+
+	switch (freq) {
+	case 48000:     return XX_FECH48000;
+	case 44100:     return XX_FECH44100;
+	case 32000:     return XX_FECH32000;
+	case 29400:     return XX_FECH29400;
+	case 24000:     return XX_FECH24000;
+	case 22050:     return XX_FECH22050;
+	case 16000:     return XX_FECH16000;
+	case 14000:     return XX_FECH14000;
+	case 12000:     return XX_FECH12000;
+	case 11025:     return XX_FECH11025;
+	case 8000:      return XX_FECH8000;
+	case 7350:      return XX_FECH7350;
+	default:        return freq;   /* The value is already correct */
+	}
+}
+
+
+/*
+ * vx_change_clock_source - change the clock source
+ * @source: the new source
+ */
+void vx_change_clock_source(vx_core_t *chip, int source)
+{
+	unsigned long flags;
+
+	/* we mute DAC to prevent clicks */
+	vx_toggle_dac_mute(chip, 1);
+	spin_lock_irqsave(&chip->lock, flags);
+	chip->ops->set_clock_source(chip, source);
+	chip->clock_source = source;
+	spin_unlock_irqrestore(&chip->lock, flags);
+	/* unmute */
+	vx_toggle_dac_mute(chip, 0);
+}
+
+
+/*
+ * set the internal clock
+ */
+void vx_set_internal_clock(vx_core_t *chip, unsigned int freq)
+{
+	int clock;
+	unsigned long flags;
+	/* Get real clock value */
+	clock = vx_calc_clock_from_freq(chip, freq);
+	snd_printdd(KERN_DEBUG "set internal clock to 0x%x from freq %d\n", clock, freq);
+	spin_lock_irqsave(&chip->lock, flags);
+	if (vx_is_pcmcia(chip)) {
+		vx_outb(chip, HIFREQ, (clock >> 8) & 0x0f);
+		vx_outb(chip, LOFREQ, clock & 0xff);
+	} else {
+		vx_outl(chip, HIFREQ, (clock >> 8) & 0x0f);
+		vx_outl(chip, LOFREQ, clock & 0xff);
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+/*
+ * set the iec958 status bits
+ * @bits: 32-bit status bits
+ */
+void vx_set_iec958_status(vx_core_t *chip, unsigned int bits)
+{
+	int i;
+
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return;
+
+	for (i = 0; i < 32; i++)
+		vx_write_one_cbit(chip, i, bits & (1 << i));
+}
+
+
+/*
+ * vx_set_clock - change the clock and audio source if necessary
+ */
+int vx_set_clock(vx_core_t *chip, unsigned int freq)
+{
+	int src_changed = 0;
+
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return 0;
+
+	/* change the audio source if possible */
+	vx_sync_audio_source(chip);
+
+	switch (chip->audio_source) {
+	case VX_AUDIO_SRC_DIGITAL:
+		if (chip->clock_source != UER_SYNC) {
+			vx_change_clock_source(chip, UER_SYNC);
+			mdelay(6);
+			src_changed = 1;
+		}
+		if (chip->freq == freq)
+			return 0;
+		break;
+	default:
+		if (chip->clock_source != INTERNAL_QUARTZ) {
+			vx_change_clock_source(chip, INTERNAL_QUARTZ);
+			src_changed = 1;
+		}
+		if (chip->freq == freq)
+			return 0;
+		vx_set_internal_clock(chip, freq);
+		if (src_changed)
+			vx_modify_board_inputs(chip);
+		break;
+	}
+	chip->freq = freq;
+	vx_modify_board_clock(chip, 1);
+	return 0;
+}
+
+
+/*
+ * vx_change_frequency - called from interrupt handler
+ */
+int vx_change_frequency(vx_core_t *chip)
+{
+	int freq;
+
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return 0;
+
+	if (chip->clock_source == INTERNAL_QUARTZ)
+		return 0;
+	/*
+	 * Read the real UER board frequency
+	 */
+	freq = vx_read_uer_status(chip, &chip->uer_detected);
+	if (freq < 0)
+		return freq;
+	/*
+	 * The frequency computed by the DSP is good and
+	 * is different from the previous computed.
+	 */
+	if (freq == 48000 || freq == 44100 || freq == 32000)
+		chip->freq_detected = freq;
+
+	return 0;
+}
diff -urN linux-2.5.70-bk6/sound/i2c/Makefile linux-2.5.70-bk7/sound/i2c/Makefile
--- linux-2.5.70-bk6/sound/i2c/Makefile	2003-05-26 18:00:41.000000000 -0700
+++ linux-2.5.70-bk7/sound/i2c/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -11,6 +11,8 @@
   obj-$(CONFIG_L3) += l3/
 endif
 
+obj-$(CONFIG_SND) += other/
+
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_INTERWAVE_STB) += snd-tea6330t.o snd-i2c.o
 obj-$(CONFIG_SND_ICE1712) += snd-cs8427.o snd-i2c.o
diff -urN linux-2.5.70-bk6/sound/i2c/cs8427.c linux-2.5.70-bk7/sound/i2c/cs8427.c
--- linux-2.5.70-bk6/sound/i2c/cs8427.c	2003-05-26 18:00:20.000000000 -0700
+++ linux-2.5.70-bk7/sound/i2c/cs8427.c	2003-06-02 04:39:12.000000000 -0700
@@ -22,6 +22,7 @@
 
 #include <sound/driver.h>
 #include <linux/slab.h>
+#include <linux/delay.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/pcm.h>
@@ -47,6 +48,7 @@
 
 typedef struct {
 	unsigned char regmap[0x14];	/* map of first 1 + 13 registers */
+	unsigned int rate;
 	cs8427_stream_t playback;
 	cs8427_stream_t capture;
 } cs8427_t;
@@ -56,8 +58,8 @@
 	int bit;
 	unsigned char res = 0;
 	for (bit = 0; bit < 8; bit++) {
-		res |= val & 1;
 		res <<= 1;
+		res |= val & 1;
 		val >>= 1;
 	}
 	return res;
@@ -133,10 +135,10 @@
 		return 0;
 	if ((err = snd_cs8427_select_corudata(device, udata)) < 0)
 		return err;
-	memcpy(hw_data, data, count);
+	memcpy(hw_data, ndata, count);
 	if (udata) {
 		memset(data, 0, sizeof(data));
-		if (memcmp(hw_data, data, 32) == 0) {
+		if (memcmp(hw_data, data, count) == 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)
@@ -147,7 +149,7 @@
 	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)
+	if (snd_i2c_sendbytes(device, data, count + 1) != count + 1)
 		return -EIO;
 	return 1;
 }
@@ -164,15 +166,15 @@
 {
 	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_CONTROL1: RMCK to OMCK, valid PCM audio, disable mutes, TCBL=output */
+	  CS8427_SWCLK | CS8427_TCBLDIR,
 	  /* 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,
+	     recovered input clock, recovered input clock source is ILRCK changed to AES3INPUT (workaround, see snd_cs8427_reset) */
+	  CS8427_RXDILRCK,
 	  /* 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 */
@@ -181,9 +183,8 @@
 	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,
-	  Why setting CS8427_V causes clicks and glitches? */
-	  CS8427_UNLOCK | CS8427_CONF | CS8427_BIP | CS8427_PAR,
+	  /* CS8427_UNLOCK | CS8427_V | CS8427_CONF | CS8427_BIP | CS8427_PAR, */
+	  0xff, /* set everything */
 	  /* CS8427_REG_CSDATABUF:
 	     Registers 32-55 window to CS buffer
 	     Inhibit D->E transfers from overwriting first 5 bytes of CS data.
@@ -203,7 +204,7 @@
 	int err;
 	cs8427_t *chip;
 	snd_i2c_device_t *device;
-	unsigned char buf[32 + 1];
+	unsigned char buf[24];
 
 	if ((err = snd_i2c_device_create(bus, "CS8427", CS8427_ADDR | (addr & 7), &device)) < 0)
 		return err;
@@ -242,31 +243,32 @@
 		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)
+	buf[0] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 0));
+	buf[1] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 8));
+	buf[2] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 16));
+	buf[3] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 24));
+	memset(buf + 4, 0, 24 - 4);
+	if (snd_cs8427_send_corudata(device, 0, buf, 24) < 0)
 		goto __fail;
+	memcpy(chip->playback.def_status, buf, 24);
+	memcpy(chip->playback.pcm_status, buf, 24);
+	snd_i2c_unlock(bus);
+
 	/* 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;
+	snd_cs8427_reset(device);
 
 #if 0	// it's nice for read tests
 	{
 	char buf[128];
+	int xx;
 	buf[0] = 0x81;
 	snd_i2c_sendbytes(device, buf, 1);
 	snd_i2c_readbytes(device, buf, 127);
+	for (xx = 0; xx < 127; xx++)
+		printk("reg[0x%x] = 0x%x\n", xx+1, buf[xx]);
 	}
 #endif
 	
-	snd_i2c_unlock(bus);
 	if (r_cs8427)
 		*r_cs8427 = device;
 	return 0;
@@ -277,6 +279,44 @@
       	return err < 0 ? err : -EIO;
 }
 
+/*
+ * Reset the chip using run bit, also lock PLL using ILRCK and
+ * put back AES3INPUT. This workaround is described in latest
+ * CS8427 datasheet, otherwise TXDSERIAL will not work.
+ */
+void snd_cs8427_reset(snd_i2c_device_t *cs8427)
+{
+	cs8427_t *chip;
+	unsigned long end_time;
+	int data;
+
+	snd_assert(cs8427, return);
+	chip = snd_magic_cast(cs8427_t, cs8427->private_data, return);
+	snd_i2c_lock(cs8427->bus);
+	chip->regmap[CS8427_REG_CLOCKSOURCE] &= ~(CS8427_RUN | CS8427_RXDMASK);
+	snd_cs8427_reg_write(cs8427, CS8427_REG_CLOCKSOURCE, chip->regmap[CS8427_REG_CLOCKSOURCE]);
+	udelay(200);
+	chip->regmap[CS8427_REG_CLOCKSOURCE] |= CS8427_RUN | CS8427_RXDILRCK;
+	snd_cs8427_reg_write(cs8427, CS8427_REG_CLOCKSOURCE, chip->regmap[CS8427_REG_CLOCKSOURCE]);
+	udelay(200);
+	snd_i2c_unlock(cs8427->bus);
+	end_time = jiffies + HZ / 2;
+	while (time_after_eq(end_time, jiffies)) {
+		snd_i2c_lock(cs8427->bus);
+		data = snd_cs8427_reg_read(cs8427, CS8427_REG_RECVERRORS);
+		snd_i2c_unlock(cs8427->bus);
+		if (!(data & CS8427_UNLOCK))
+			break;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ/100);
+	}
+	snd_i2c_lock(cs8427->bus);
+	chip->regmap[CS8427_REG_CLOCKSOURCE] &= ~CS8427_RXDMASK;
+	chip->regmap[CS8427_REG_CLOCKSOURCE] |= CS8427_RXDAES3INPUT;
+	snd_cs8427_reg_write(cs8427, CS8427_REG_CLOCKSOURCE, chip->regmap[CS8427_REG_CLOCKSOURCE]);
+	snd_i2c_unlock(cs8427->bus);
+}
+
 static int snd_cs8427_in_status_info(snd_kcontrol_t *kcontrol,
 				     snd_ctl_elem_info_t *uinfo)
 {
@@ -294,7 +334,7 @@
 	int data;
 
 	snd_i2c_lock(device->bus);
-	data = snd_cs8427_reg_read(device, 15);
+	data = snd_cs8427_reg_read(device, kcontrol->private_value);
 	snd_i2c_unlock(device->bus);
 	if (data < 0)
 		return data;
@@ -302,6 +342,36 @@
 	return 0;
 }
 
+static int snd_cs8427_qsubcode_info(snd_kcontrol_t *kcontrol,
+				    snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = 10;
+	return 0;
+}
+
+static int snd_cs8427_qsubcode_get(snd_kcontrol_t *kcontrol,
+				   snd_ctl_elem_value_t *ucontrol)
+{
+	snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol);
+	unsigned char reg = CS8427_REG_QSUBCODE;
+	int err;
+
+	snd_i2c_lock(device->bus);
+	if ((err = snd_i2c_sendbytes(device, &reg, 1)) != 1) {
+		snd_printk("unable to send register 0x%x byte to CS8427\n", reg);
+		snd_i2c_unlock(device->bus);
+		return err < 0 ? err : -EIO;
+	}
+	if ((err = snd_i2c_readbytes(device, ucontrol->value.bytes.data, 10)) != 10) {
+		snd_printk("unable to read Q-subcode bytes from CS8427\n");
+		snd_i2c_unlock(device->bus);
+		return err < 0 ? err : -EIO;
+	}
+	snd_i2c_unlock(device->bus);
+	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;
@@ -316,7 +386,7 @@
 	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);
+	memcpy(ucontrol->value.iec958.status, chip->playback.def_status, 24);
 	snd_i2c_unlock(device->bus);
 	return 0;
 }
@@ -327,13 +397,14 @@
 	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;
+	snd_pcm_runtime_t *runtime = chip->playback.substream ? chip->playback.substream->runtime : NULL;
 	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);
+	change = memcmp(ucontrol->value.iec958.status, status, 24) != 0;
+	memcpy(status, ucontrol->value.iec958.status, 24);
+	if (change && (kcontrol->private_value ? runtime != NULL : runtime == NULL)) {
+		err = snd_cs8427_send_corudata(device, 0, status, 24);
 		if (err < 0)
 			change = err;
 	}
@@ -351,7 +422,7 @@
 static int snd_cs8427_spdif_mask_get(snd_kcontrol_t * kcontrol,
 				      snd_ctl_elem_value_t * ucontrol)
 {
-	memset(ucontrol->value.iec958.status, 0xff, 23);
+	memset(ucontrol->value.iec958.status, 0xff, 24);
 	return 0;
 }
 
@@ -364,6 +435,15 @@
 	.name =		"IEC958 CS8427 Input Status",
 	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
 	.get =		snd_cs8427_in_status_get,
+	.private_value = 15,
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.info =		snd_cs8427_in_status_info,
+	.name =		"IEC958 CS8427 Error Status",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.get =		snd_cs8427_in_status_get,
+	.private_value = 16,
 },
 {
 	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
@@ -388,6 +468,13 @@
 	.get =		snd_cs8427_spdif_get,
 	.put =		snd_cs8427_spdif_put,
 	.private_value = 1
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.info =		snd_cs8427_qsubcode_info,
+	.name =		"IEC958 Q-subcode Capture Default",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.get =		snd_cs8427_qsubcode_get
 }};
 
 int snd_cs8427_iec958_build(snd_i2c_device_t *cs8427,
@@ -413,6 +500,8 @@
 			chip->playback.pcm_ctl = kctl;
 	}
 
+	chip->playback.substream = play_substream;
+	chip->capture.substream = cap_substream;
 	snd_assert(chip->playback.pcm_ctl, return -EIO);
 	return 0;
 }
@@ -435,7 +524,7 @@
 {
 	cs8427_t *chip;
 	char *status;
-	int err;
+	int err, reset;
 
 	snd_assert(cs8427, return -ENXIO);
 	chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO);
@@ -457,17 +546,21 @@
 		case 48000: status[3] |= IEC958_AES3_CON_FS_48000; break;
 		}
 	}
-	err = snd_cs8427_send_corudata(cs8427, 0, status, 23);
+	err = snd_cs8427_send_corudata(cs8427, 0, status, 24);
 	if (err > 0)
 		snd_ctl_notify(cs8427->bus->card,
 			       SNDRV_CTL_EVENT_MASK_VALUE,
 			       &chip->playback.pcm_ctl->id);
+	reset = chip->rate != rate;
 	snd_i2c_unlock(cs8427->bus);
+	if (reset)
+		snd_cs8427_reset(cs8427);
 	return err < 0 ? err : 0;
 }
 
 EXPORT_SYMBOL(snd_cs8427_detect);
 EXPORT_SYMBOL(snd_cs8427_create);
+EXPORT_SYMBOL(snd_cs8427_reset);
 EXPORT_SYMBOL(snd_cs8427_iec958_build);
 EXPORT_SYMBOL(snd_cs8427_iec958_active);
 EXPORT_SYMBOL(snd_cs8427_iec958_pcm);
diff -urN linux-2.5.70-bk6/sound/i2c/l3/uda1341.c linux-2.5.70-bk7/sound/i2c/l3/uda1341.c
--- linux-2.5.70-bk6/sound/i2c/l3/uda1341.c	2003-05-26 18:00:40.000000000 -0700
+++ linux-2.5.70-bk7/sound/i2c/l3/uda1341.c	2003-06-02 04:39:12.000000000 -0700
@@ -17,7 +17,7 @@
  * 2002-05-12   Tomas Kasparek  another code cleanup
  */
 
-/* $Id: uda1341.c,v 1.8 2003/03/20 16:45:59 perex Exp $ */
+/* $Id: uda1341.c,v 1.9 2003/04/19 13:34:33 perex Exp $ */
 
 #include <sound/driver.h>
 #include <linux/module.h>
@@ -36,8 +36,6 @@
 
 #include <linux/l3/l3.h>
 
-#undef DEBUG_MODE
-#undef DEBUG_FUNCTION_NAMES
 #include <sound/uda1341.h>
 
 /* {{{ HW regs definition */
@@ -93,13 +91,13 @@
 
 const int uda1341_enum_items[] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-		2, //peak - before/after
-		4, //deemp - none/32/44.1/48
-		0,
-		4, //filter - flat/min/min/max
-		0, 0, 0,
-		4, //mixer - differ/line/mic/mixer
-		0, 0, 0, 0, 0,
+	2, //peak - before/after
+	4, //deemp - none/32/44.1/48
+	0,
+	4, //filter - flat/min/min/max
+	0, 0, 0,
+	4, //mixer - differ/line/mic/mixer
+	0, 0, 0, 0, 0,
 };
 
 const char ** uda1341_enum_names[] = {
@@ -117,26 +115,24 @@
 
 typedef struct uda1341 uda1341_t;
 
-struct uda1341{
-	void (*write) (struct l3_client *uda1341, unsigned short reg, unsigned short val);
-	unsigned char (*read) (struct l3_client *uda1341, unsigned short reg);
-        
-	unsigned char   regs[uda1341_reg_last];
-	int		active;
-
+struct uda1341 {
+	int (*write) (struct l3_client *uda1341, unsigned short reg, unsigned short val);
+	int (*read) (struct l3_client *uda1341, unsigned short reg);        
+	unsigned char regs[uda1341_reg_last];
+	int active;
 	spinlock_t reg_lock;
-
 	snd_card_t *card;
-
 	uda1341_cfg cfg;
+#ifdef CONFIG_PM
+	unsigned char suspend_regs[uda1341_reg_last];
+	uda1341_cfg suspend_cfg;
+#endif
 };
 
 //hack for ALSA magic casting
 typedef struct l3_client l3_client_t;
 #define chip_t l3_client_t      
 
-static struct l3_client *uda1341=NULL;
-
 /* transfer 8bit integer into string with binary representation */
 void int2str_bin8(uint8_t val, char *buf){
 	const int size = sizeof(val) * 8;
@@ -151,66 +147,45 @@
 
 /* {{{ HW manipulation routines */
 
-void snd_uda1341_codec_write(struct l3_client *clnt, unsigned short reg, unsigned short val)
+int snd_uda1341_codec_write(struct l3_client *clnt, unsigned short reg, unsigned short val)
 {
 	struct uda1341 *uda = clnt->driver_data;
-
-	int err=0;
 	unsigned char buf[2] = { 0xc0, 0xe0 }; // for EXT addressing
+	int err = 0;
 
-	DEBUG_NAME(KERN_DEBUG "codec_write: reg: %s val: %d ", uda1341_reg_names[reg], val);
-        
 	uda->regs[reg] = val;
 
 	if (uda->active) {
-		DEBUG("O");
 		if (IS_DATA0(reg)) {
-			DEBUG(" D0 ");
 			err = l3_write(clnt, UDA1341_DATA0, (const unsigned char *)&val, 1);
 		} else if (IS_DATA1(reg)) {
-			DEBUG(" D1 ");                        
 			err = l3_write(clnt, UDA1341_DATA1, (const unsigned char *)&val, 1);
 		} else if (IS_STATUS(reg)) {
-			DEBUG(" S ");                        
 			err = l3_write(clnt, UDA1341_STATUS, (const unsigned char *)&val, 1);
 		} else if (IS_EXTEND(reg)) {
-			DEBUG(" E ");
 			buf[0] |= (reg - ext0) & 0x7;   //EXT address
 			buf[1] |= val;                  //EXT data
-			DEBUG("%x %x ", buf[0], buf[1]);
 			err = l3_write(clnt, UDA1341_DATA0, (const unsigned char *)buf, 2);
 		}
-                
-		if (err == 1 || err == 2)
-			DEBUG("K\n");
-		else
-			DEBUG(" Error: %d\n", err);
 	} else
 		printk(KERN_ERR "UDA1341 codec not active!\n");
+	return err;
 }
 
-unsigned char snd_uda1341_codec_read(struct l3_client *clnt, unsigned short reg)
+int snd_uda1341_codec_read(struct l3_client *clnt, unsigned short reg)
 {
-	int err=0;
 	unsigned char val;
-
-	DEBUG_NAME(KERN_DEBUG "codec_read: reg: %d ", reg);
-	
-	DEBUG("O");
+	int err;
 
 	err = l3_read(clnt, reg, &val, 1);
-	
 	if (err == 1)
-		DEBUG("K\n");
-	else
-		DEBUG(" Error: %d\n", err);
-
-	return val & 63; //use just 6bits - the rest is address of the reg
+		// use just 6bits - the rest is address of the reg
+		return val & 63;
+	return err < 0 ? err : -EIO;
 }
 
-static int snd_uda1341_valid_reg(struct l3_client *clnt, unsigned short reg)
+static inline int snd_uda1341_valid_reg(struct l3_client *clnt, unsigned short reg)
 {
-	DEBUG_NAME(KERN_DEBUG "valid_reg\n");
 	return reg < uda1341_reg_last;
 }
 
@@ -221,8 +196,10 @@
 	unsigned short old, new;
 	struct uda1341 *uda = clnt->driver_data;
 
-	DEBUG(KERN_DEBUG "update_bits: reg: %s mask: %d shift: %d val: %d\n",
-	      uda1341_reg_names[reg], mask, shift, value);
+#if 0
+	printk(KERN_DEBUG "update_bits: reg: %s mask: %d shift: %d val: %d\n",
+	       uda1341_reg_names[reg], mask, shift, value);
+#endif
         
 	if (!snd_uda1341_valid_reg(clnt, reg))
 		return -EINVAL;
@@ -243,70 +220,111 @@
 {
 	struct uda1341 *uda = clnt->driver_data;
 	int ret = 0;
+#ifdef CONFIG_PM
+	int reg;
+#endif
 
-	DEBUG_NAME(KERN_DEBUG "cfg_write what: %d value: %d\n", what, value);
+#if 0
+	printk(KERN_DEBUG "cfg_write what: %d value: %d\n", what, value);
+#endif
 
 	uda->cfg[what] = value;
         
 	switch(what) {
-	case CMD_RESET: ret = snd_uda1341_update_bits(clnt, data0_2, 1, 2, 1, flush);//MUTE
-		ret = snd_uda1341_update_bits(clnt, stat0, 1, 6, 1, flush);//RESET
-		ret = snd_uda1341_update_bits(clnt, stat0, 1, 6, 0, flush);// RESTORE
+	case CMD_RESET:
+		ret = snd_uda1341_update_bits(clnt, data0_2, 1, 2, 1, flush);	// MUTE
+		ret = snd_uda1341_update_bits(clnt, stat0, 1, 6, 1, flush);	// RESET
+		ret = snd_uda1341_update_bits(clnt, stat0, 1, 6, 0, flush);	// RESTORE
 		uda->cfg[CMD_RESET]=0;
 		break;
-	case CMD_FS: ret = snd_uda1341_update_bits(clnt, stat0, 3, 4, value, flush);
+	case CMD_FS:
+		ret = snd_uda1341_update_bits(clnt, stat0, 3, 4, value, flush);
 		break;
-	case CMD_FORMAT: ret = snd_uda1341_update_bits(clnt, stat0, 7, 1, value, flush);
+	case CMD_FORMAT:
+		ret = snd_uda1341_update_bits(clnt, stat0, 7, 1, value, flush);
 		break;
-	case CMD_OGAIN: ret = snd_uda1341_update_bits(clnt, stat1, 1, 6, value, flush);
+	case CMD_OGAIN:
+		ret = snd_uda1341_update_bits(clnt, stat1, 1, 6, value, flush);
 		break;
-	case CMD_IGAIN: ret = snd_uda1341_update_bits(clnt, stat1, 1, 5, value, flush);
+	case CMD_IGAIN:
+		ret = snd_uda1341_update_bits(clnt, stat1, 1, 5, value, flush);
 		break;
-	case CMD_DAC: ret = snd_uda1341_update_bits(clnt, stat1, 1, 0, value, flush);
+	case CMD_DAC:
+		ret = snd_uda1341_update_bits(clnt, stat1, 1, 0, value, flush);
 		break;
-	case CMD_ADC: ret = snd_uda1341_update_bits(clnt, stat1, 1, 1, value, flush);
+	case CMD_ADC:
+		ret = snd_uda1341_update_bits(clnt, stat1, 1, 1, value, flush);
 		break;
-	case CMD_VOLUME: ret = snd_uda1341_update_bits(clnt, data0_0, 63, 0, value, flush);
+	case CMD_VOLUME:
+		ret = snd_uda1341_update_bits(clnt, data0_0, 63, 0, value, flush);
 		break;
-	case CMD_BASS: ret = snd_uda1341_update_bits(clnt, data0_1, 15, 2, value, flush);
+	case CMD_BASS:
+		ret = snd_uda1341_update_bits(clnt, data0_1, 15, 2, value, flush);
 		break;
-	case CMD_TREBBLE: ret = snd_uda1341_update_bits(clnt, data0_1, 3, 0, value, flush);
+	case CMD_TREBBLE:
+		ret = snd_uda1341_update_bits(clnt, data0_1, 3, 0, value, flush);
 		break;
-	case CMD_PEAK: ret = snd_uda1341_update_bits(clnt, data0_2, 1, 5, value, flush);
+	case CMD_PEAK:
+		ret = snd_uda1341_update_bits(clnt, data0_2, 1, 5, value, flush);
 		break;
-	case CMD_DEEMP: ret = snd_uda1341_update_bits(clnt, data0_2, 3, 3, value, flush);
+	case CMD_DEEMP:
+		ret = snd_uda1341_update_bits(clnt, data0_2, 3, 3, value, flush);
 		break;
-	case CMD_MUTE: ret = snd_uda1341_update_bits(clnt, data0_2, 1, 2, value, flush);
+	case CMD_MUTE:
+		ret = snd_uda1341_update_bits(clnt, data0_2, 1, 2, value, flush);
 		break;
-	case CMD_FILTER: ret = snd_uda1341_update_bits(clnt, data0_2, 3, 0, value, flush);
+	case CMD_FILTER:
+		ret = snd_uda1341_update_bits(clnt, data0_2, 3, 0, value, flush);
 		break;
-	case CMD_CH1: ret = snd_uda1341_update_bits(clnt, ext0, 31, 0, value, flush);
+	case CMD_CH1:
+		ret = snd_uda1341_update_bits(clnt, ext0, 31, 0, value, flush);
 		break;
-	case CMD_CH2: ret = snd_uda1341_update_bits(clnt, ext1, 31, 0, value, flush);
+	case CMD_CH2:
+		ret = snd_uda1341_update_bits(clnt, ext1, 31, 0, value, flush);
 		break;
-	case CMD_MIC: ret = snd_uda1341_update_bits(clnt, ext2, 7, 2, value, flush);
+	case CMD_MIC:
+		ret = snd_uda1341_update_bits(clnt, ext2, 7, 2, value, flush);
 		break;
-	case CMD_MIXER: ret = snd_uda1341_update_bits(clnt, ext2, 3, 0, value, flush);
+	case CMD_MIXER:
+		ret = snd_uda1341_update_bits(clnt, ext2, 3, 0, value, flush);
 		break;
-	case CMD_AGC: ret = snd_uda1341_update_bits(clnt, ext4, 1, 4, value, flush);
+	case CMD_AGC:
+		ret = snd_uda1341_update_bits(clnt, ext4, 1, 4, value, flush);
 		break;
-	case CMD_IG: ret = snd_uda1341_update_bits(clnt, ext4, 3, 0, value & 0x3, flush);
+	case CMD_IG:
+		ret = snd_uda1341_update_bits(clnt, ext4, 3, 0, value & 0x3, flush);
 		ret = snd_uda1341_update_bits(clnt, ext5, 31, 0, value >> 2, flush);
 		break;
-	case CMD_AGC_TIME: ret = snd_uda1341_update_bits(clnt, ext6, 7, 2, value, flush);
+	case CMD_AGC_TIME:
+		ret = snd_uda1341_update_bits(clnt, ext6, 7, 2, value, flush);
 		break;
-	case CMD_AGC_LEVEL: ret = snd_uda1341_update_bits(clnt, ext6, 3, 0, value, flush);
+	case CMD_AGC_LEVEL:
+		ret = snd_uda1341_update_bits(clnt, ext6, 3, 0, value, flush);
+		break;
+#ifdef CONFIG_PM		
+	case CMD_SUSPEND:
+		for (reg = stat0; reg < uda1341_reg_last; reg++)
+			uda->suspend_regs[reg] = uda->regs[reg];
+		for (reg = 0; reg < CMD_LAST; reg++)
+			uda->suspend_cfg[reg] = uda->cfg[reg];
+		break;
+	case CMD_RESUME:
+		for (reg = stat0; reg < uda1341_reg_last; reg++)
+			snd_uda1341_codec_write(clnt, reg, uda->suspend_regs[reg]);
+		for (reg = 0; reg < CMD_LAST; reg++)
+			uda->cfg[reg] = uda->suspend_cfg[reg];
+		break;
+#endif
+	default:
+		ret = -EINVAL;
 		break;
-	default: ret = -EINVAL;	break;                                
 	}
                 
-	if (!uda->active) {
+	if (!uda->active)
 		printk(KERN_ERR "UDA1341 codec not active!\n");                
-	}
 	return ret;
 }
 
-
 /* }}} */
 
 /* {{{ Proc interface */
@@ -319,9 +337,9 @@
 	int peak;
 
 	peak = snd_uda1341_codec_read(clnt, UDA1341_DATA1);
+	if (peak < 0)
+		peak = 0;
 	
-	DEBUG_NAME(KERN_DEBUG "proc_read\n");
-
 	snd_iprintf(buffer, "%s\n\n", uda->card->longname);
 
 	// for information about computed values see UDA1341TS datasheet pages 15 - 21
@@ -343,7 +361,7 @@
 
 	snd_iprintf(buffer, "Mute                : %s\n", uda->cfg[CMD_MUTE] ? "on" : "off");
 
-	if(uda->cfg[CMD_VOLUME] == 0)
+	if (uda->cfg[CMD_VOLUME] == 0)
 		snd_iprintf(buffer, "Volume              : 0 dB\n");
 	else if (uda->cfg[CMD_VOLUME] < 62)
 		snd_iprintf(buffer, "Volume              : %d dB\n", -1*uda->cfg[CMD_VOLUME] +1);
@@ -384,8 +402,6 @@
 	int reg;
 	char buf[12];
 
-	DEBUG_NAME(KERN_DEBUG "proc_regs_read\n");
-        
 	spin_lock(&uda->reg_lock);
 	for (reg = 0; reg < uda1341_reg_last; reg ++) {
 		if (reg == empty)
@@ -403,13 +419,10 @@
 static void __devinit snd_uda1341_proc_init(snd_card_t *card, struct l3_client *clnt)
 {
 	snd_info_entry_t *entry;
-	struct uda1341 *uda = clnt->driver_data;
 
-	DEBUG_NAME(KERN_DEBUG "proc_init\n");
-        
 	if (! snd_card_proc_new(card, "uda1341", &entry))
 		snd_info_set_text_ops(entry, clnt, snd_uda1341_proc_read);
-	if (! snd_card_proc_new(card, "uda1341-regs", &entry)) {
+	if (! snd_card_proc_new(card, "uda1341-regs", &entry))
 		snd_info_set_text_ops(entry, clnt, snd_uda1341_proc_regs_read);
 }
 
@@ -429,8 +442,6 @@
 {
 	int mask = (kcontrol->private_value >> 12) & 63;
 
-	DEBUG_NAME(KERN_DEBUG "info_single where: %ld\n", kcontrol->private_value & 31);
-
 	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 1;
 	uinfo->value.integer.min = 0;
@@ -446,8 +457,6 @@
 	int mask = (kcontrol->private_value >> 12) & 63;
 	int invert = (kcontrol->private_value >> 18) & 1;
         
-	DEBUG_NAME(KERN_DEBUG "get_single where: %d (val: %d)\n", where, uda->cfg[where]);
-        
 	ucontrol->value.integer.value[0] = uda->cfg[where];
 	if (invert)
 		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
@@ -470,9 +479,6 @@
 	if (invert)
 		val = mask - val;
 
-	DEBUG(KERN_DEBUG "put_single where: %d reg: %d mask: %d shift: %d inv: %d val: %d\n",
-	      where, reg, mask, shift, invert, val);
-        
 	uda->cfg[where] = val;
 	return snd_uda1341_update_bits(clnt, reg, mask, shift, val, FLUSH);
 }
@@ -492,8 +498,6 @@
 	int where = kcontrol->private_value & 31;
 	const char **texts;
 	
-	DEBUG_NAME(KERN_DEBUG "info_enum where: %d\n", where);
-
 	// this register we don't handle this way
 	if (!uda1341_enum_items[where])
 		return -EINVAL;
@@ -516,8 +520,6 @@
 	uda1341_t *uda = clnt->driver_data;
 	int where = kcontrol->private_value & 31;        
         
-	DEBUG_NAME(KERN_DEBUG "get_enum where: %d (val: %d)\n", where, uda->cfg[where]);
-        
 	ucontrol->value.enumerated.item[0] = uda->cfg[where];	
 	return 0;
 }
@@ -533,9 +535,6 @@
 
 	uda->cfg[where] = (ucontrol->value.enumerated.item[0] & mask);
 	
-	DEBUG(KERN_DEBUG "put_enum where: %d reg: %d mask: %d shift: %d val: %d\n",
-	      where, reg, mask, shift, uda->cfg[where]);
-        
 	return snd_uda1341_update_bits(clnt, reg, mask, shift, uda->cfg[where], FLUSH);
 }
 
@@ -557,8 +556,6 @@
 	int mask_2 = (kcontrol->private_value >> 25) & 63;
 	int mask;
         
-	DEBUG_NAME(KERN_DEBUG "info_2regs where: %ld\n", kcontrol->private_value & 31);
-
 	mask = (mask_2 + 1) * (mask_1 + 1) - 1;
 	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 1;
@@ -577,8 +574,6 @@
 	int invert = (kcontrol->private_value >> 31) & 1;
 	int mask;
 
-	DEBUG_NAME(KERN_DEBUG "get_2regs where: %d (val: %d)\n", where, uda->cfg[where]);
-
 	mask = (mask_2 + 1) * (mask_1 + 1) - 1;
 
 	ucontrol->value.integer.value[0] = uda->cfg[where];
@@ -604,10 +599,6 @@
 
 	val = ucontrol->value.integer.value[0];
          
-	DEBUG_NAME(KERN_DEBUG "put_2regs where: %d reg1: %d reg2: %d mask1: %d mask2: %d "
-		   "shift1: %d shift2: %d inv: %d val: %d\n", where, reg_1, reg_2, mask_1, mask_2,
-		   shift_1, shift_2, invert, val);       
-
 	mask = (mask_2 + 1) * (mask_1 + 1) - 1;
 
 	val1 = val & mask_1;
@@ -659,20 +650,43 @@
 	UDA1341_2REGS("Gain Input Amplifier Gain (channel 2)", CMD_IG, ext4, ext5, 0, 0, 3, 31, 0),
 };
 
+static void uda1341_free(struct l3_client *uda1341)
+{
+	l3_detach_client(uda1341); // calls kfree for driver_data (uda1341_t)
+	snd_magic_kfree(uda1341);
+}
+
+static int uda1341_dev_free(snd_device_t *device)
+{
+	struct l3_client *clnt = snd_magic_cast(l3_client_t, device->device_data, return);
+	uda1341_free(clnt);
+	return 0;
+}
+
 int __init snd_chip_uda1341_mixer_new(snd_card_t *card, struct l3_client **clnt)
 {
+	static snd_device_ops_t ops = {
+		.dev_free =     uda1341_dev_free,
+	};
+	struct l3_client *uda1341;
 	int idx, err;
 
-	DEBUG_NAME(KERN_DEBUG "uda1341 mixer_new\n");
-        
 	snd_assert(card != NULL, return -EINVAL);
 
 	uda1341 = snd_magic_kcalloc(l3_client_t, 0, GFP_KERNEL);
 	if (uda1341 == NULL)
 		return -ENOMEM;
          
-	if ((err = l3_attach_client(uda1341, "l3-bit-sa1100-gpio", "snd-uda1341")))
-		return -ENODEV;
+	if ((err = l3_attach_client(uda1341, "l3-bit-sa1100-gpio", "snd-uda1341"))) {
+		kfree(uda1341);
+		return err;
+	}
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, uda1341, &ops)) < 0) {
+		l3_detach_client(uda1341);
+		kfree(uda1341);
+		return err;
+	}
 
 	for (idx = 0; idx < UDA1341_CONTROLS; idx++) {
 		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_uda1341_controls[idx], uda1341))) < 0)
@@ -688,16 +702,6 @@
 	return 0;
 }
 
-void __init snd_chip_uda1341_mixer_del(snd_card_t *card)
-{
-	DEBUG_NAME(KERN_DEBUG "uda1341 mixer_del\n");
-        
-	l3_detach_client(uda1341);
-
-	snd_magic_kfree(uda1341);
-	uda1341 = NULL;
-}
-
 /* }}} */
 
 /* {{{ L3 operations */
@@ -706,14 +710,10 @@
 {
 	struct uda1341 *uda;
 
-	DEBUG_NAME(KERN_DEBUG "uda1341 attach\n");
-        
 	uda = snd_magic_kcalloc(uda1341_t, 0, GFP_KERNEL);
 	if (!uda)
 		return -ENOMEM;
 
-	memset(uda, 0, sizeof(*uda));
-
 	/* init fixed parts of my copy of registers */
 	uda->regs[stat0]   = STAT0;
 	uda->regs[stat1]   = STAT1;
@@ -728,22 +728,18 @@
 	spin_lock_init(&uda->reg_lock);
         
 	clnt->driver_data = uda;
-
-	//l3_open(clnt);
 	return 0;
 }
 
 static void uda1341_detach(struct l3_client *clnt)
 {
-	DEBUG_NAME(KERN_DEBUG "uda1341 detach\n");        
-	snd_magic_kfree(clnt->driver_data);
+	if (clnt->driver_data)
+		snd_magic_kfree(clnt->driver_data);
 }
 
 static int
 uda1341_command(struct l3_client *clnt, int cmd, void *arg)
 {
-	DEBUG_NAME(KERN_DEBUG "l3_command\n");
-
 	if (cmd != CMD_READ_REG)
 		return snd_uda1341_cfg_write(clnt, cmd, (int) arg, FLUSH);
 
@@ -754,8 +750,6 @@
 {
 	struct uda1341 *uda = clnt->driver_data;
 
-	DEBUG_NAME(KERN_DEBUG "uda1341 open\n");
-        
 	uda->active = 1;
 
 	/* init default configuration */
@@ -764,8 +758,8 @@
 	snd_uda1341_cfg_write(clnt, CMD_FORMAT, LSB16, FLUSH);  // unknown state after reset
 	snd_uda1341_cfg_write(clnt, CMD_OGAIN, ON, FLUSH);      // default off after reset
 	snd_uda1341_cfg_write(clnt, CMD_IGAIN, ON, FLUSH);      // default off after reset
-	snd_uda1341_cfg_write(clnt, CMD_DAC, ON, FLUSH);    // ??? default value after reset
-	snd_uda1341_cfg_write(clnt, CMD_ADC, ON, FLUSH);    // ??? default value after reset
+	snd_uda1341_cfg_write(clnt, CMD_DAC, ON, FLUSH);	// ??? default value after reset
+	snd_uda1341_cfg_write(clnt, CMD_ADC, ON, FLUSH);	// ??? default value after reset
 	snd_uda1341_cfg_write(clnt, CMD_VOLUME, 20, FLUSH);     // default 0dB after reset
 	snd_uda1341_cfg_write(clnt, CMD_BASS, 0, REGS_ONLY);    // default value after reset
 	snd_uda1341_cfg_write(clnt, CMD_TREBBLE, 0, REGS_ONLY); // default value after reset
@@ -790,7 +784,6 @@
 {
 	struct uda1341 *uda = clnt->driver_data;
 
-	DEBUG_NAME(KERN_DEBUG "uda1341 close\n");
 	uda->active = 0;
 }
 
@@ -832,7 +825,6 @@
 MODULE_DEVICES("{{UDA1341,UDA1341TS}}");
 
 EXPORT_SYMBOL(snd_chip_uda1341_mixer_new);
-EXPORT_SYMBOL(snd_chip_uda1341_mixer_del);
 
 /* }}} */
 
diff -urN linux-2.5.70-bk6/sound/i2c/other/Makefile linux-2.5.70-bk7/sound/i2c/other/Makefile
--- linux-2.5.70-bk6/sound/i2c/other/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/i2c/other/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,10 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-ak4xxx-adda-objs := ak4xxx-adda.o
+
+# Module Dependency
+obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
+obj-$(CONFIG_SND_ICE1724) += snd-ak4xxx-adda.o
diff -urN linux-2.5.70-bk6/sound/i2c/other/ak4xxx-adda.c linux-2.5.70-bk7/sound/i2c/other/ak4xxx-adda.c
--- linux-2.5.70-bk6/sound/i2c/other/ak4xxx-adda.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/i2c/other/ak4xxx-adda.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,445 @@
+/*
+ *   ALSA driver for AK4524 / AK4528 / AK4529 / AK4355 / AK4381
+ *   AD and DA converters
+ *
+ *	Copyright (c) 2000-2003 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
+ *
+ */      
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/ak4xxx-adda.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx  AD/DA converters");
+MODULE_LICENSE("GPL");
+
+void snd_akm4xxx_write(akm4xxx_t *ak, int chip, unsigned char reg, unsigned char val)
+{
+	ak->ops.lock(ak, chip);
+	ak->ops.write(ak, chip, reg, val);
+
+	/* save the data */
+	if (ak->type == SND_AK4524 || ak->type == SND_AK4528) {
+		if ((reg != 0x04 && reg != 0x05) || (reg & 0x80) == 0)
+			ak->images[chip][reg] = val;
+		else
+			ak->ipga_gain[chip][reg-4] = val;
+	} else {
+		/* AK4529, or else */
+		ak->images[chip][reg] = val;
+	}
+	ak->ops.unlock(ak, chip);
+}
+
+/*
+ * reset the AKM codecs
+ * @state: 1 = reset codec, 0 = restore the registers
+ *
+ * assert the reset operation and restores the register values to the chips.
+ */
+void snd_akm4xxx_reset(akm4xxx_t *ak, int state)
+{
+	unsigned int chip;
+	unsigned char reg;
+	
+	switch (ak->type) {
+	case SND_AK4524:
+	case SND_AK4528:
+		for (chip = 0; chip < ak->num_dacs/2; chip++) {
+			snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
+			if (state)
+				continue;
+			/* DAC volumes */
+			for (reg = 0x04; reg < (ak->type == SND_AK4528 ? 0x06 : 0x08); reg++)
+				snd_akm4xxx_write(ak, chip, reg, ak->images[chip][reg]);
+			if (ak->type == SND_AK4528)
+				continue;
+			/* IPGA */
+			for (reg = 0x04; reg < 0x06; reg++)
+				snd_akm4xxx_write(ak, chip, reg, ak->ipga_gain[chip][reg-4]);
+		}
+		break;
+	case SND_AK4529:
+		/* FIXME: needed for ak4529? */
+		break;
+	case SND_AK4355:
+		snd_akm4xxx_write(ak, 0, 0x01, state ? 0x02 : 0x01);
+		if (state)
+			return;
+		for (reg = 0x00; reg < 0x0a; reg++)
+			if (reg != 0x01)
+				snd_akm4xxx_write(ak, 0, reg, ak->images[0][reg]);
+		break;
+	case SND_AK4381:
+		for (chip = 0; chip < ak->num_dacs/2; chip++) {
+			snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
+			if (state)
+				continue;
+			for (reg = 0x01; reg < 0x05; reg++)
+				snd_akm4xxx_write(ak, chip, reg, ak->images[chip][reg]);
+		}
+		break;
+	}
+}
+
+/*
+ * initialize all the ak4xxx chips
+ */
+void snd_akm4xxx_init(akm4xxx_t *ak)
+{
+	static unsigned char inits_ak4524[] = {
+		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
+	};
+	static unsigned char inits_ak4528[] = {
+		0x00, 0x07, /* 0: all power up */
+		0x01, 0x00, /* 1: ADC/DAC reset */
+		0x02, 0x60, /* 2: 24bit I2S */
+		0x03, 0x0d, /* 3: deemphasis off, turn LR highpass filters on */
+		0x01, 0x03, /* 1: ADC/DAC enable */
+		0x04, 0x00, /* 4: ADC left muted */
+		0x05, 0x00, /* 5: ADC right muted */
+		0xff, 0xff
+	};
+	static unsigned char inits_ak4529[] = {
+		0x09, 0x01, /* 9: ATS=0, RSTN=1 */
+		0x0a, 0x3f, /* A: all power up, no zero/overflow detection */
+		0x00, 0x0c, /* 0: TDM=0, 24bit I2S, SMUTE=0 */
+		0x01, 0x00, /* 1: ACKS=0, ADC, loop off */
+		0x02, 0xff, /* 2: LOUT1 muted */
+		0x03, 0xff, /* 3: ROUT1 muted */
+		0x04, 0xff, /* 4: LOUT2 muted */
+		0x05, 0xff, /* 5: ROUT2 muted */
+		0x06, 0xff, /* 6: LOUT3 muted */
+		0x07, 0xff, /* 7: ROUT3 muted */
+		0x0b, 0xff, /* B: LOUT4 muted */
+		0x0c, 0xff, /* C: ROUT4 muted */
+		0x08, 0x55, /* 8: deemphasis all off */
+		0xff, 0xff
+	};
+	static unsigned char inits_ak4355[] = {
+		0x01, 0x02, /* 1: reset and soft-mute */
+		0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, disable DZF, sharp roll-off, RSTN#=0 */
+		// 0x02, 0x0e, /* 2: DA's power up, normal speed, RSTN#=0 */
+		0x02, 0x2e,
+		0x03, 0x01, /* 3: de-emphasis off */
+		0x04, 0x00, /* 4: LOUT1 volume muted */
+		0x05, 0x00, /* 5: ROUT1 volume muted */
+		0x06, 0x00, /* 6: LOUT2 volume muted */
+		0x07, 0x00, /* 7: ROUT2 volume muted */
+		0x08, 0x00, /* 8: LOUT3 volume muted */
+		0x09, 0x00, /* 9: ROUT3 volume muted */
+		0x0a, 0x00, /* a: DATT speed=0, ignore DZF */
+		0x01, 0x01, /* 1: un-reset, unmute */
+		0xff, 0xff
+	};
+	static unsigned char inits_ak4381[] = {
+		0x00, 0x0c, /* 0: mode3(i2s), disable auto-clock detect */
+		// 0x01, 0x02, /* 1: de-emphasis off, normal speed, sharp roll-off, DZF off */
+		0x01, 0x12,
+		0x02, 0x00, /* 2: DZF disabled */
+		0x03, 0x00, /* 3: LATT 0 */
+		0x04, 0x00, /* 4: RATT 0 */
+		0x00, 0x0f, /* 0: power-up, un-reset */
+		0xff, 0xff
+	};
+
+	int chip, num_chips;
+	unsigned char *ptr, reg, data, *inits;
+
+	switch (ak->type) {
+	case SND_AK4524:
+		inits = inits_ak4524;
+		num_chips = ak->num_dacs / 2;
+		break;
+	case SND_AK4528:
+		inits = inits_ak4528;
+		num_chips = ak->num_dacs / 2;
+		break;
+	case SND_AK4529:
+		inits = inits_ak4529;
+		num_chips = 1;
+		break;
+	case SND_AK4355:
+		inits = inits_ak4355;
+		num_chips = 1;
+		break;
+	case SND_AK4381:
+		inits = inits_ak4381;
+		num_chips = ak->num_dacs / 2;
+		break;
+	default:
+		snd_BUG();
+		return;
+	}
+
+	for (chip = 0; chip < num_chips; chip++) {
+		ptr = inits;
+		while (*ptr != 0xff) {
+			reg = *ptr++;
+			data = *ptr++;
+			snd_akm4xxx_write(ak, chip, reg, data);
+		}
+	}
+}
+
+#define AK_GET_CHIP(val)		(((val) >> 8) & 0xff)
+#define AK_GET_ADDR(val)		((val) & 0xff)
+#define AK_GET_SHIFT(val)		(((val) >> 16) & 0x7f)
+#define AK_GET_INVERT(val)		(((val) >> 23) & 1)
+#define AK_GET_MASK(val)		(((val) >> 24) & 0xff)
+#define AK_COMPOSE(chip,addr,shift,mask) (((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24))
+#define AK_INVERT 			(1<<23)
+
+static int snd_akm4xxx_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_akm4xxx_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol);
+	int chip = AK_GET_CHIP(kcontrol->private_value);
+	int addr = AK_GET_ADDR(kcontrol->private_value);
+	int invert = AK_GET_INVERT(kcontrol->private_value);
+	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+	unsigned char val = ak->images[chip][addr];
+	
+	ucontrol->value.integer.value[0] = invert ? mask - val : val;
+	return 0;
+}
+
+static int snd_akm4xxx_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol);
+	int chip = AK_GET_CHIP(kcontrol->private_value);
+	int addr = AK_GET_ADDR(kcontrol->private_value);
+	int invert = AK_GET_INVERT(kcontrol->private_value);
+	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+	unsigned char nval = ucontrol->value.integer.value[0] % (mask+1);
+	int change;
+
+	if (invert)
+		nval = mask - nval;
+	change = ak->images[chip][addr] != nval;
+	if (change)
+		snd_akm4xxx_write(ak, chip, addr, nval);
+	return change;
+}
+
+static int snd_akm4xxx_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_akm4xxx_ipga_gain_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol);
+	int chip = AK_GET_CHIP(kcontrol->private_value);
+	int addr = AK_GET_ADDR(kcontrol->private_value);
+	ucontrol->value.integer.value[0] = ak->ipga_gain[chip][addr-4] & 0x7f;
+	return 0;
+}
+
+static int snd_akm4xxx_ipga_gain_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol);
+	int chip = AK_GET_CHIP(kcontrol->private_value);
+	int addr = AK_GET_ADDR(kcontrol->private_value);
+	unsigned char nval = (ucontrol->value.integer.value[0] % 37) | 0x80;
+	int change = ak->ipga_gain[chip][addr] != nval;
+	if (change)
+		snd_akm4xxx_write(ak, chip, addr, nval);
+	return change;
+}
+
+static int snd_akm4xxx_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_akm4xxx_deemphasis_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol);
+	int chip = AK_GET_CHIP(kcontrol->private_value);
+	int addr = AK_GET_ADDR(kcontrol->private_value);
+	int shift = AK_GET_SHIFT(kcontrol->private_value);
+	ucontrol->value.enumerated.item[0] = (ak->images[chip][addr] >> shift) & 3;
+	return 0;
+}
+
+static int snd_akm4xxx_deemphasis_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol);
+	int chip = AK_GET_CHIP(kcontrol->private_value);
+	int addr = AK_GET_ADDR(kcontrol->private_value);
+	int shift = AK_GET_SHIFT(kcontrol->private_value);
+	unsigned char nval = ucontrol->value.enumerated.item[0] & 3;
+	int change;
+	
+	nval = (nval << shift) | (ak->images[chip][addr] & ~(3 << shift));
+	change = ak->images[chip][addr] != nval;
+	if (change)
+		snd_akm4xxx_write(ak, chip, addr, nval);
+	return change;
+}
+
+/*
+ * build AK4xxx controls
+ */
+
+int snd_akm4xxx_build_controls(akm4xxx_t *ak)
+{
+	unsigned int idx;
+	int err;
+
+	for (idx = 0; idx < ak->num_dacs; ++idx) {
+		snd_kcontrol_t ctl;
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "DAC Volume");
+		ctl.id.index = idx + ak->idx_offset * 2;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.count = 1;
+		ctl.info = snd_akm4xxx_volume_info;
+		ctl.get = snd_akm4xxx_volume_get;
+		ctl.put = snd_akm4xxx_volume_put;
+		switch (ak->type) {
+		case SND_AK4524:
+			ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127); /* register 6 & 7 */
+			break;
+		case SND_AK4528:
+			ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); /* register 4 & 5 */
+			break;
+		case SND_AK4529: {
+			int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb; /* registers 2-7 and b,c */
+			ctl.private_value = AK_COMPOSE(0, val, 0, 255) | AK_INVERT;
+			break;
+		}
+		case SND_AK4355:
+			ctl.private_value = AK_COMPOSE(0, idx + 4, 0, 255); /* register 4-9, chip #0 only */
+			break;
+		case SND_AK4381:
+			ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255); /* register 3 & 4 */
+			break;
+		default:
+			return -EINVAL;
+			}
+		ctl.private_data = ak;
+		if ((err = snd_ctl_add(ak->card, snd_ctl_new(&ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
+			return err;
+	}
+	for (idx = 0; idx < ak->num_adcs && ak->type == SND_AK4524; ++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.count = 1;
+		ctl.info = snd_akm4xxx_volume_info;
+		ctl.get = snd_akm4xxx_volume_get;
+		ctl.put = snd_akm4xxx_volume_put;
+		ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); /* register 4 & 5 */
+		ctl.private_data = ak;
+		if ((err = snd_ctl_add(ak->card, snd_ctl_new(&ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 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.count = 1;
+		ctl.info = snd_akm4xxx_ipga_gain_info;
+		ctl.get = snd_akm4xxx_ipga_gain_get;
+		ctl.put = snd_akm4xxx_ipga_gain_put;
+		ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 0); /* register 4 & 5 */
+		ctl.private_data = ak;
+		if ((err = snd_ctl_add(ak->card, snd_ctl_new(&ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
+			return err;
+	}
+	for (idx = 0; idx < ak->num_dacs/2; idx++) {
+		snd_kcontrol_t ctl;
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "Deemphasis");
+		ctl.id.index = idx + ak->idx_offset;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.count = 1;
+		ctl.info = snd_akm4xxx_deemphasis_info;
+		ctl.get = snd_akm4xxx_deemphasis_get;
+		ctl.put = snd_akm4xxx_deemphasis_put;
+		switch (ak->type) {
+		case SND_AK4524:
+		case SND_AK4528:
+			ctl.private_value = AK_COMPOSE(idx, 3, 0, 0); /* register 3 */
+			break;
+		case SND_AK4529: {
+			int shift = idx == 3 ? 6 : (2 - idx) * 2;
+			ctl.private_value = AK_COMPOSE(0, 8, shift, 0); /* register 8 with shift */
+			break;
+		}
+		case SND_AK4355:
+			ctl.private_value = AK_COMPOSE(idx, 3, 0, 0);
+			break;
+		case SND_AK4381:
+			ctl.private_value = AK_COMPOSE(idx, 1, 1, 0);
+			break;
+		}
+		ctl.private_data = ak;
+		if ((err = snd_ctl_add(ak->card, snd_ctl_new(&ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_akm4xxx_write);
+EXPORT_SYMBOL(snd_akm4xxx_reset);
+EXPORT_SYMBOL(snd_akm4xxx_init);
+EXPORT_SYMBOL(snd_akm4xxx_build_controls);
diff -urN linux-2.5.70-bk6/sound/isa/Kconfig linux-2.5.70-bk7/sound/isa/Kconfig
--- linux-2.5.70-bk6/sound/isa/Kconfig	2003-05-26 18:00:56.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/Kconfig	2003-06-02 04:39:12.000000000 -0700
@@ -41,7 +41,7 @@
 
 config SND_PC98_CS4232
 	tristate "NEC PC9800 CS4232 driver"
-	depends on SND && PC9800
+	depends on SND && X86_PC9800
 	help
 	  Say 'Y' or 'M' to include support for NEC PC-9801/PC-9821 on-board
 	  soundchip based on CS4232.
@@ -191,5 +191,11 @@
 	help
 	  Say 'Y' or 'M' to include support for Aztech Sound Galaxy.
 
-endmenu
+config SND_SSCAPE
+	tristate "Ensoniq SoundScape PnP driver"
+	depends on SND
+	help
+	  Say 'Y' or 'M' to include support for Ensoniq SoundScape PnP
+	  soundcard.
 
+endmenu
diff -urN linux-2.5.70-bk6/sound/isa/Makefile linux-2.5.70-bk7/sound/isa/Makefile
--- linux-2.5.70-bk6/sound/isa/Makefile	2003-05-26 18:00:26.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -10,6 +10,7 @@
 snd-es18xx-objs := es18xx.o
 snd-opl3sa2-objs := opl3sa2.o
 snd-sgalaxy-objs := sgalaxy.o
+snd-sscape-objs := sscape.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_ALS100) += snd-als100.o
@@ -19,6 +20,7 @@
 obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o
 obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o
 obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o
+obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o
 
 obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ opti9xx/ \
 		     sb/ wavefront/
diff -urN linux-2.5.70-bk6/sound/isa/ad1816a/ad1816a.c linux-2.5.70-bk7/sound/isa/ad1816a/ad1816a.c
--- linux-2.5.70-bk6/sound/isa/ad1816a/ad1816a.c	2003-05-26 18:00:26.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/ad1816a/ad1816a.c	2003-06-02 04:39:12.000000000 -0700
@@ -307,8 +307,10 @@
 
 	cards += pnp_register_card_driver(&ad1816a_pnpc_driver);
 #ifdef MODULE
-	if (!cards)
+	if (!cards) {
+		pnp_unregister_card_driver(&ad1816a_pnpc_driver);
 		printk(KERN_ERR "no AD1816A based soundcards found.\n");
+	}
 #endif	/* MODULE */
 	return cards ? 0 : -ENODEV;
 }
diff -urN linux-2.5.70-bk6/sound/isa/als100.c linux-2.5.70-bk7/sound/isa/als100.c
--- linux-2.5.70-bk6/sound/isa/als100.c	2003-05-26 18:00:23.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/als100.c	2003-06-02 04:39:12.000000000 -0700
@@ -328,8 +328,10 @@
 
 	cards += pnp_register_card_driver(&als100_pnpc_driver);
 #ifdef MODULE
-	if (!cards)
+	if (!cards) {
+		pnp_unregister_card_driver(&als100_pnpc_driver);
 		snd_printk(KERN_ERR "no ALS100 based soundcards found\n");
+	}
 #endif
 	return cards ? 0 : -ENODEV;
 }
diff -urN linux-2.5.70-bk6/sound/isa/azt2320.c linux-2.5.70-bk7/sound/isa/azt2320.c
--- linux-2.5.70-bk6/sound/isa/azt2320.c	2003-05-26 18:00:58.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/azt2320.c	2003-06-02 04:39:12.000000000 -0700
@@ -1,4 +1,3 @@
-
 /*
     card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards.
     Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
@@ -105,11 +104,9 @@
 MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC);
 
 struct snd_card_azt2320 {
-#ifdef CONFIG_PNP
 	int dev_no;
 	struct pnp_dev *dev;
 	struct pnp_dev *devmpu;
-#endif	/* CONFIG_PNP */
 };
 
 static struct pnp_card_device_id snd_azt2320_pnpids[] __devinitdata = {
@@ -365,8 +362,10 @@
 
 	cards += pnp_register_card_driver(&azt2320_pnpc_driver);
 #ifdef MODULE
-	if (!cards)
+	if (!cards) {
+		pnp_unregister_card_driver(&azt2320_pnpc_driver);
 		snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
+	}
 #endif
 	return cards ? 0 : -ENODEV;
 }
diff -urN linux-2.5.70-bk6/sound/isa/cmi8330.c linux-2.5.70-bk7/sound/isa/cmi8330.c
--- linux-2.5.70-bk6/sound/isa/cmi8330.c	2003-05-26 18:00:24.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/cmi8330.c	2003-06-02 04:39:12.000000000 -0700
@@ -613,6 +613,9 @@
 #endif
 
 	if (!cards) {
+#ifdef CONFIG_PNP
+		pnp_unregister_card_driver(&cmi8330_pnpc_driver);
+#endif
 #ifdef MODULE
 		snd_printk(KERN_ERR "CMI8330 not found or device busy\n");
 #endif
diff -urN linux-2.5.70-bk6/sound/isa/cs423x/Makefile linux-2.5.70-bk7/sound/isa/cs423x/Makefile
--- linux-2.5.70-bk6/sound/isa/cs423x/Makefile	2003-05-26 18:00:56.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/cs423x/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -21,6 +21,7 @@
 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
+obj-$(CONFIG_SND_SSCAPE) += snd-cs4231-lib.o
 obj-$(CONFIG_SND_PC98_CS4232) += snd-pc98-cs4232.o snd-cs4231-lib.o
 
 obj-m := $(sort $(obj-m))
diff -urN linux-2.5.70-bk6/sound/isa/cs423x/cs4231_lib.c linux-2.5.70-bk7/sound/isa/cs423x/cs4231_lib.c
--- linux-2.5.70-bk6/sound/isa/cs423x/cs4231_lib.c	2003-05-26 18:00:25.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/cs423x/cs4231_lib.c	2003-06-02 04:39:12.000000000 -0700
@@ -448,6 +448,7 @@
 	cs4231_t *chip = snd_pcm_substream_chip(substream);
 	int result = 0;
 	unsigned int what;
+	struct list_head *pos;
 	snd_pcm_substream_t *s;
 	int do_start;
 
@@ -467,8 +468,8 @@
 	}
 
 	what = 0;
-	s = substream;
-	do {
+	snd_pcm_group_for_each(pos, substream) {
+		s = snd_pcm_group_substream_entry(pos);
 		if (s == chip->playback_substream) {
 			what |= CS4231_PLAYBACK_ENABLE;
 			snd_pcm_trigger_done(s, substream);
@@ -476,8 +477,7 @@
 			what |= CS4231_RECORD_ENABLE;
 			snd_pcm_trigger_done(s, substream);
 		}
-		s = s->link_next;
-	} while (s != substream);
+	}
 	spin_lock(&chip->reg_lock);
 	if (do_start) {
 		chip->image[CS4231_IFACE_CTRL] |= what;
diff -urN linux-2.5.70-bk6/sound/isa/cs423x/cs4236.c linux-2.5.70-bk7/sound/isa/cs423x/cs4236.c
--- linux-2.5.70-bk6/sound/isa/cs423x/cs4236.c	2003-05-26 18:00:24.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/cs423x/cs4236.c	2003-06-02 04:39:12.000000000 -0700
@@ -365,7 +365,6 @@
 			snd_printk(KERN_ERR IDENT " MPU401 PnP manual resources are invalid, using auto config\n");
 		err = pnp_activate_dev(pdev);
 		if (err < 0) {
-			kfree(cfg);
 			printk(KERN_ERR IDENT " MPU401 PnP configure failed for WSS (out of resources?)\n");
 			mpu_port[dev] = SNDRV_AUTO_PORT;
 			mpu_irq[dev] = SNDRV_AUTO_IRQ;
@@ -382,7 +381,7 @@
 	kfree(cfg);
 	return 0;
 }
-#endif
+#endif /* CONFIG_PNP */
 
 static void snd_card_cs4236_free(snd_card_t *card)
 {
@@ -590,6 +589,9 @@
 	cards += pnp_register_card_driver(&cs423x_pnpc_driver);
 #endif
 	if (!cards) {
+#ifdef CONFIG_PNP
+		pnp_unregister_card_driver(&cs423x_pnpc_driver);
+#endif
 #ifdef MODULE
 		printk(KERN_ERR IDENT " soundcard not found or device busy\n");
 #endif
diff -urN linux-2.5.70-bk6/sound/isa/dt019x.c linux-2.5.70-bk7/sound/isa/dt019x.c
--- linux-2.5.70-bk6/sound/isa/dt019x.c	2003-05-26 18:00:38.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/dt019x.c	2003-06-02 04:39:12.000000000 -0700
@@ -127,8 +127,7 @@
 	if (port[dev] != SNDRV_AUTO_PORT)
 		pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
 	if (dma8[dev] != SNDRV_AUTO_DMA)
-		pnp_resource_change(&cfg->dma_resource[0], dma8[dev],
-			1);
+		pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1);
 	if (irq[dev] != SNDRV_AUTO_IRQ)
 		pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
 
@@ -158,18 +157,17 @@
 		if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
 			snd_printk(KERN_ERR PFX "DT-019X MPU401 the requested resources are invalid, using auto config\n");
 		err = pnp_activate_dev(pdev);
-		if (err < 0)
+		if (err < 0) {
+			pnp_release_card_device(pdev);
+			snd_printk(KERN_ERR PFX "DT-019X MPU401 pnp configure failure, skipping\n");
 			goto __mpu_error;
+		}
 		mpu_port[dev] = pnp_port_start(pdev, 0);
 		mpu_irq[dev] = pnp_irq(pdev, 0);
 		snd_printdd("dt019x: found MPU-401: port=0x%lx, irq=0x%lx\n",
 			 	mpu_port[dev],mpu_irq[dev]);
 	} else {
-		__mpu_error:
-		if (pdev) {
-			pnp_release_card_device(pdev);
-			snd_printk(KERN_ERR PFX "DT-019X MPU401 pnp configure failure, skipping\n");
-		}
+	__mpu_error:
 		acard->devmpu = NULL;
 		mpu_port[dev] = -1;
 	}
@@ -182,16 +180,15 @@
 		if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
 			snd_printk(KERN_ERR PFX "DT-019X OPL3 the requested resources are invalid, using auto config\n");
 		err = pnp_activate_dev(pdev);
-		if (err < 0)
+		if (err < 0) {
+			pnp_release_card_device(pdev);
+			snd_printk(KERN_ERR PFX "DT-019X OPL3 pnp configure failure, skipping\n");
 			goto __fm_error;
+		}
 		fm_port[dev] = pnp_port_start(pdev, 0);
 		snd_printdd("dt019x: found OPL3 synth: port=0x%lx\n",fm_port[dev]);
 	} else {
-		__fm_error:
-		if (pdev) {
-			pnp_release_card_device(pdev);
-			snd_printk(KERN_ERR PFX "DT-019X OPL3 pnp configure failure, skipping\n");
-		}
+	__fm_error:
 		acard->devopl = NULL;
 		fm_port[dev] = -1;
 	}
diff -urN linux-2.5.70-bk6/sound/isa/es18xx.c linux-2.5.70-bk7/sound/isa/es18xx.c
--- linux-2.5.70-bk6/sound/isa/es18xx.c	2003-05-26 18:00:39.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/es18xx.c	2003-06-02 04:39:12.000000000 -0700
@@ -2242,6 +2242,9 @@
 	cards += pnp_register_card_driver(&es18xx_pnpc_driver);
 #endif
 	if(!cards) {
+#ifdef CONFIG_PNP
+		pnp_unregister_card_driver(&es18xx_pnpc_driver);
+#endif
 #ifdef MODULE
 		snd_printk(KERN_ERR "ESS AudioDrive ES18xx soundcard not found or device busy\n");
 #endif
diff -urN linux-2.5.70-bk6/sound/isa/gus/gus_irq.c linux-2.5.70-bk7/sound/isa/gus/gus_irq.c
--- linux-2.5.70-bk6/sound/isa/gus/gus_irq.c	2003-05-26 18:00:57.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/gus/gus_irq.c	2003-06-02 04:39:12.000000000 -0700
@@ -32,8 +32,7 @@
 
 irqreturn_t 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 IRQ_NONE);
+	snd_gus_card_t * gus = snd_magic_cast(snd_gus_card_t, dev_id, return IRQ_NONE);
 	unsigned char status;
 	int loop = 100;
 	int handled = 0;
diff -urN linux-2.5.70-bk6/sound/isa/gus/gus_synth.c linux-2.5.70-bk7/sound/isa/gus/gus_synth.c
--- linux-2.5.70-bk6/sound/isa/gus/gus_synth.c	2003-05-26 18:00:40.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/gus/gus_synth.c	2003-06-02 04:39:12.000000000 -0700
@@ -192,9 +192,6 @@
 	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,
 						   16, 0,
diff -urN linux-2.5.70-bk6/sound/isa/gus/interwave.c linux-2.5.70-bk7/sound/isa/gus/interwave.c
--- linux-2.5.70-bk6/sound/isa/gus/interwave.c	2003-05-26 18:00:40.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/gus/interwave.c	2003-06-02 04:39:12.000000000 -0700
@@ -949,6 +949,9 @@
 #endif
 
 	if (!cards) {
+#ifdef CONFIG_PNP
+		pnp_unregister_card_driver(&interwave_pnpc_driver);
+#endif
 #ifdef MODULE
 		printk(KERN_ERR "InterWave soundcard not found or device busy\n");
 #endif
diff -urN linux-2.5.70-bk6/sound/isa/opl3sa2.c linux-2.5.70-bk7/sound/isa/opl3sa2.c
--- linux-2.5.70-bk6/sound/isa/opl3sa2.c	2003-05-26 18:01:00.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/opl3sa2.c	2003-06-02 04:39:12.000000000 -0700
@@ -645,6 +645,7 @@
 	pnp_init_resource_table(cfg);
 	if (sb_port[dev] != SNDRV_AUTO_PORT)
 		pnp_resource_change(&cfg->port_resource[0], sb_port[dev], 16);
+	if (wss_port[dev] != SNDRV_AUTO_PORT)
 		pnp_resource_change(&cfg->port_resource[1], wss_port[dev], 8);
 	if (fm_port[dev] != SNDRV_AUTO_PORT)
 		pnp_resource_change(&cfg->port_resource[2], fm_port[dev], 4);
@@ -664,7 +665,7 @@
 	err = pnp_activate_dev(pdev);
 	if (err < 0) {
 		kfree(cfg);
-		snd_printk(KERN_ERR "PnP configure failure (out of resources?)\n");
+		snd_printk(KERN_ERR "PnP configure failure (out of resources?) err = %d\n", err);
 		return -EBUSY;
 	}
 	sb_port[dev] = pnp_port_start(pdev, 0);
@@ -903,6 +904,9 @@
 #ifdef MODULE
 		snd_printk(KERN_ERR "Yamaha OPL3-SA soundcard not found or device busy\n");
 #endif
+#ifdef CONFIG_PNP
+		pnp_unregister_card_driver(&opl3sa2_pnpc_driver);
+#endif
 		return -ENODEV;
 	}
 	return 0;
diff -urN linux-2.5.70-bk6/sound/isa/opti9xx/opti92x-ad1848.c linux-2.5.70-bk7/sound/isa/opti9xx/opti92x-ad1848.c
--- linux-2.5.70-bk6/sound/isa/opti9xx/opti92x-ad1848.c	2003-05-26 18:00:24.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/opti9xx/opti92x-ad1848.c	2003-06-02 04:39:12.000000000 -0700
@@ -43,10 +43,8 @@
 #endif	/* CS4231 */
 #include <sound/mpu401.h>
 #include <sound/opl3.h>
-#ifdef USE_OPL4
 #ifndef OPTi93X
-#include "opl4.h" /* <sound/opl4.h> */
-#endif
+#include <sound/opl4.h>
 #endif
 #define SNDRV_LEGACY_FIND_FREE_IRQ
 #define SNDRV_LEGACY_FIND_FREE_DMA
@@ -279,13 +277,6 @@
 
 #ifdef CONFIG_PNP
 
-#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 pnp_card_device_id snd_opti9xx_pnpids[] = {
 #ifndef OPTi93X
 	/* OPTi 82C924 */
@@ -969,8 +960,10 @@
 	case SNDRV_PCM_TRIGGER_STOP:
 	{
 		unsigned int what = 0;
-		snd_pcm_substream_t *s = substream;
-		do {
+		struct list_head *pos;
+		snd_pcm_substream_t *s;
+		snd_pcm_group_for_each(pos, substream) {
+			s = snd_pcm_group_substream_entry(pos);
 			if (s == chip->playback_substream) {
 				what |= OPTi93X_PLAYBACK_ENABLE;
 				snd_pcm_trigger_done(s, substream);
@@ -978,8 +971,7 @@
 				what |= OPTi93X_CAPTURE_ENABLE;
 				snd_pcm_trigger_done(s, substream);
 			}
-			s = s->link_next;
-		} while (s != substream);
+		}
 		spin_lock(&chip->lock);
 		if (cmd == SNDRV_PCM_TRIGGER_START) {
 			snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, what);
@@ -1946,7 +1938,7 @@
 	chip = (opti9xx_t *)card->private_data;
 
 #ifdef CONFIG_PNP
-	if (isapnp && (hw = snd_card_opti9xx_pnp(chip, pcard, pid)) > 0) {
+	if (isapnp && pcard && (hw = snd_card_opti9xx_pnp(chip, pcard, pid)) > 0) {
 		switch (hw) {
 		case 0x0924:
 			hw = OPTi9XX_HW_82C924;
@@ -2111,7 +2103,6 @@
 
 	if (chip->fm_port > 0) {
 		opl3_t *opl3 = NULL;
-#ifdef USE_OPL4
 #ifndef OPTi93X
 		if (chip->hardware == OPTi9XX_HW_82C928 ||
 		    chip->hardware == OPTi9XX_HW_82C929 ||
@@ -2130,7 +2121,6 @@
 			}
 		}
 #endif	/* !OPTi93X */
-#endif
 		if (!opl3 && snd_opl3_create(card,
 					     chip->fm_port,
 					     chip->fm_port + 2,
@@ -2201,8 +2191,15 @@
 {
 	int cards, error;
 
+#ifdef CONFIG_PNP
 	cards = pnp_register_card_driver(&opti9xx_pnpc_driver);
+#else
+	cards = 0;
+#endif
 	if (cards == 0 && (error = snd_card_opti9xx_probe(NULL, NULL)) < 0) {
+#ifdef CONFIG_PNP
+		pnp_unregister_card_driver(&opti9xx_pnpc_driver);
+#endif
 #ifdef MODULE
 #ifdef OPTi93X
 		printk(KERN_ERR "no OPTi 82C93x soundcard found\n");
diff -urN linux-2.5.70-bk6/sound/isa/sb/emu8000.c linux-2.5.70-bk7/sound/isa/sb/emu8000.c
--- linux-2.5.70-bk6/sound/isa/sb/emu8000.c	2003-05-26 18:00:37.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/sb/emu8000.c	2003-06-02 04:39:12.000000000 -0700
@@ -1144,6 +1144,8 @@
 		strcpy(awe->name, "EMU-8000");
 		*(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw;
 	}
+#else
+	awe = NULL;
 #endif
 	if (awe_ret)
 		*awe_ret = awe;
diff -urN linux-2.5.70-bk6/sound/isa/sb/es968.c linux-2.5.70-bk7/sound/isa/sb/es968.c
--- linux-2.5.70-bk6/sound/isa/sb/es968.c	2003-05-26 18:00:40.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/sb/es968.c	2003-06-02 04:39:12.000000000 -0700
@@ -79,16 +79,15 @@
 #define	DRIVER_NAME	"snd-card-es968"
 
 static irqreturn_t snd_card_es968_interrupt(int irq, void *dev_id,
-				     struct pt_regs *regs)
+					    struct pt_regs *regs)
 {
 	sb_t *chip = snd_magic_cast(sb_t, dev_id, return IRQ_NONE);
 
 	if (chip->open & SB_OPEN_PCM) {
-		snd_sb8dsp_interrupt(chip);
+		return snd_sb8dsp_interrupt(chip);
 	} else {
-		snd_sb8dsp_midi_interrupt(chip);
+		return snd_sb8dsp_midi_interrupt(chip);
 	}
-	return IRQ_HANDLED;
 }
 
 static int __devinit snd_card_es968_pnp(int dev, struct snd_card_es968 *acard,
@@ -122,6 +121,7 @@
 	err = pnp_activate_dev(pdev);
 	if (err < 0) {
 		snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
+		kfree(cfg);
 		return err;
 	}
 	port[dev] = pnp_port_start(pdev, 0);
@@ -225,10 +225,13 @@
 static int __init alsa_card_es968_init(void)
 {
 	int res = pnp_register_card_driver(&es968_pnpc_driver);
-#ifdef MODULE
 	if (res == 0)
+	{
+		pnp_unregister_card_driver(&es968_pnpc_driver);
+#ifdef MODULE
 		snd_printk(KERN_ERR "no ES968 based soundcards found\n");
 #endif
+	}
 	return res < 0 ? res : 0;
 }
 
diff -urN linux-2.5.70-bk6/sound/isa/sb/sb16.c linux-2.5.70-bk7/sound/isa/sb/sb16.c
--- linux-2.5.70-bk6/sound/isa/sb/sb16.c	2003-05-26 18:00:46.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/sb/sb16.c	2003-06-02 04:39:12.000000000 -0700
@@ -256,6 +256,8 @@
 #define DRIVER_NAME	"snd-card-sb16"
 #endif
 
+#ifdef CONFIG_PNP
+
 static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
 				       struct pnp_card_link *card,
 				       const struct pnp_card_device_id *id)
@@ -346,6 +348,8 @@
 	return 0;
 }
 
+#endif /* CONFIG_PNP */
+
 static int __init snd_sb16_probe(int dev,
 				 struct pnp_card_link *pcard,
 				 const struct pnp_card_device_id *pid)
@@ -370,12 +374,14 @@
 	if (card == NULL)
 		return -ENOMEM;
 	acard = (struct snd_card_sb16 *) card->private_data;
+#ifdef CONFIG_PNP
 	if (isapnp[dev]) {
 		if ((err = snd_card_sb16_pnp(dev, acard, pcard, pid))) {
 			snd_card_free(card);
 			return err;
 		}
 	}
+#endif
 
 	xirq = irq[dev];
 	xdma8 = dma8[dev];
@@ -625,6 +631,9 @@
 #endif
 
 	if (!cards) {
+#ifdef CONFIG_PNP
+		pnp_unregister_card_driver(&sb16_pnpc_driver);
+#endif
 #ifdef MODULE
 		snd_printk(KERN_ERR "Sound Blaster 16 soundcard not found or device busy\n");
 #ifdef SNDRV_SBAWE_EMU8000
diff -urN linux-2.5.70-bk6/sound/isa/sb/sb8.c linux-2.5.70-bk7/sound/isa/sb/sb8.c
--- linux-2.5.70-bk6/sound/isa/sb/sb8.c	2003-05-26 18:00:42.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/sb/sb8.c	2003-06-02 04:39:12.000000000 -0700
@@ -75,11 +75,10 @@
 	sb_t *chip = snd_magic_cast(sb_t, dev_id, return IRQ_NONE);
 
 	if (chip->open & SB_OPEN_PCM) {
-		snd_sb8dsp_interrupt(chip);
+		return snd_sb8dsp_interrupt(chip);
 	} else {
-		snd_sb8dsp_midi_interrupt(chip);
+		return snd_sb8dsp_midi_interrupt(chip);
 	}
-	return IRQ_HANDLED;
 }
 
 static void snd_sb8_free(snd_card_t *card)
diff -urN linux-2.5.70-bk6/sound/isa/sb/sb8_main.c linux-2.5.70-bk7/sound/isa/sb/sb8_main.c
--- linux-2.5.70-bk6/sound/isa/sb/sb8_main.c	2003-05-26 18:00:39.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/sb/sb8_main.c	2003-06-02 04:39:12.000000000 -0700
@@ -331,7 +331,7 @@
 	return 0;
 }
 
-void snd_sb8dsp_interrupt(sb_t *chip)
+irqreturn_t snd_sb8dsp_interrupt(sb_t *chip)
 {
 	snd_pcm_substream_t *substream;
 	snd_pcm_runtime_t *runtime;
@@ -356,6 +356,7 @@
 		snd_pcm_period_elapsed(substream);
 		break;
 	}
+	return IRQ_HANDLED;
 }
 
 static snd_pcm_uframes_t snd_sb8_playback_pointer(snd_pcm_substream_t * substream)
diff -urN linux-2.5.70-bk6/sound/isa/sb/sb8_midi.c linux-2.5.70-bk7/sound/isa/sb/sb8_midi.c
--- linux-2.5.70-bk6/sound/isa/sb/sb8_midi.c	2003-05-26 18:00:21.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/sb/sb8_midi.c	2003-06-02 04:39:12.000000000 -0700
@@ -21,6 +21,9 @@
  * 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.
+ *
+ * Sun May 11 12:34:56 UTC 2003 Clemens Ladisch <clemens@ladisch.de>
+ *   Added full duplex UART mode for DSP version 2.0 and later.
  */
 
 #include <sound/driver.h>
@@ -33,26 +36,31 @@
 
  */
 
-void snd_sb8dsp_midi_interrupt(sb_t * chip)
+irqreturn_t 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;
+		inb(SBP(chip, DATA_AVAIL));	/* ack interrupt */
+		return IRQ_NONE;
 	}
 	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);
+			if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
+				spin_unlock(&chip->midi_input_lock);
+				snd_rawmidi_receive(chip->midi_substream_input, &byte, 1);
+			} else {
+				spin_unlock(&chip->midi_input_lock);
+			}
 		} else {
 			spin_unlock(&chip->midi_input_lock);
 		}
 	}
+	return IRQ_HANDLED;
 }
 
 /*
@@ -63,10 +71,13 @@
 {
 	unsigned long flags;
 	sb_t *chip;
+	unsigned int valid_open_flags;
 
 	chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO);
+	valid_open_flags = chip->hardware >= SB_HW_20
+		? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0;
 	spin_lock_irqsave(&chip->open_lock, flags);
-	if (chip->open) {
+	if (chip->open & ~valid_open_flags) {
 		spin_unlock_irqrestore(&chip->open_lock, flags);
 		return -EAGAIN;
 	}
@@ -75,6 +86,8 @@
 	if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
 		spin_unlock_irqrestore(&chip->open_lock, flags);
 		snd_sbdsp_reset(chip);		/* reset DSP */
+		if (chip->hardware >= SB_HW_20)
+			snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
 	} else {
 		spin_unlock_irqrestore(&chip->open_lock, flags);
 	}
@@ -85,10 +98,13 @@
 {
 	unsigned long flags;
 	sb_t *chip;
+	unsigned int valid_open_flags;
 
 	chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO);
+	valid_open_flags = chip->hardware >= SB_HW_20
+		? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0;
 	spin_lock_irqsave(&chip->open_lock, flags);
-	if (chip->open) {
+	if (chip->open & ~valid_open_flags) {
 		spin_unlock_irqrestore(&chip->open_lock, flags);
 		return -EAGAIN;
 	}
@@ -97,6 +113,8 @@
 	if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
 		spin_unlock_irqrestore(&chip->open_lock, flags);
 		snd_sbdsp_reset(chip);		/* reset DSP */
+		if (chip->hardware >= SB_HW_20)
+			snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
 	} else {
 		spin_unlock_irqrestore(&chip->open_lock, flags);
 	}
@@ -110,7 +128,7 @@
 
 	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->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER);
 	chip->midi_substream_input = NULL;
 	if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
 		spin_unlock_irqrestore(&chip->open_lock, flags);
@@ -128,7 +146,7 @@
 
 	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->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER);
 	chip->midi_substream_output = NULL;
 	if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
 		spin_unlock_irqrestore(&chip->open_lock, flags);
@@ -147,14 +165,16 @@
 	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;
+		if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) {
+			if (chip->hardware < SB_HW_20)
+				snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
+			chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER;
 		}
 	} else {
-		if (chip->open & SB_OPEN_MIDI_TRIGGER) {
-			snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
-			chip->open &= ~SB_OPEN_MIDI_TRIGGER;
+		if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
+			if (chip->hardware < SB_HW_20)
+				snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
+			chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER;
 		}
 	}
 	spin_unlock_irqrestore(&chip->open_lock, flags);
@@ -171,14 +191,27 @@
 	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;
+		if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) {
+			chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
 			del_timer(&chip->midi_timer);
 			spin_unlock_irqrestore(&chip->open_lock, flags);
-			return;
+			break;
+		}
+		if (chip->hardware >= SB_HW_20) {
+			int timeout = 8;
+			while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0)
+				;
+			if (timeout == 0) {
+				/* Tx FIFO full - try again later */
+				spin_unlock_irqrestore(&chip->open_lock, flags);
+				break;
+			}
+			outb(byte, SBP(chip, WRITE));
+		} else {
+			snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT);
+			snd_sbdsp_command(chip, byte);
 		}
-		snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT);
-		snd_sbdsp_command(chip, byte);
+		snd_rawmidi_transmit_ack(substream, 1);
 		spin_unlock_irqrestore(&chip->open_lock, flags);
 	}
 }
@@ -204,17 +237,17 @@
 	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)) {
+		if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) {
 			init_timer(&chip->midi_timer);
 			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;
+			chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER;
 		}
 	} else {
-		if (chip->open & SB_OPEN_MIDI_TRIGGER) {
-			chip->open &= ~SB_OPEN_MIDI_TRIGGER;
+		if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) {
+			chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
 		}
 	}
 	spin_unlock_irqrestore(&chip->open_lock, flags);
@@ -253,7 +286,9 @@
 	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->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT;
+	if (chip->hardware >= SB_HW_20)
+		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
 	rmidi->private_data = chip;
 	chip->rmidi = rmidi;
 	if (rrawmidi)
diff -urN linux-2.5.70-bk6/sound/isa/sscape.c linux-2.5.70-bk7/sound/isa/sscape.c
--- linux-2.5.70-bk6/sound/isa/sscape.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/isa/sscape.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,1558 @@
+/*
+ *   Low-level ALSA driver for the ENSONIQ SoundScape PnP
+ *   Copyright (c) by Chris Rankin
+ *
+ *   This driver was written in part using information obtained from
+ *   the OSS/Free SoundScape driver, written by Hannu Savolainen.
+ *
+ *   FIXME (deadlock for alsa-kernel):
+ *     - use ISA PnP scheme used by all ALSA ISA drivers
+ *     - add non-MODULE build option
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public 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/delay.h>
+#include <linux/pnp.h>
+#include <linux/spinlock.h>
+#include <asm/dma.h>
+#include <sound/core.h>
+#include <sound/hwdep.h>
+#include <sound/cs4231.h>
+#include <sound/mpu401.h>
+#include <sound/initval.h>
+
+#include <sound/sscape_ioctl.h>
+
+#define chip_t cs4231_t
+
+
+MODULE_AUTHOR("Chris Rankin");
+MODULE_DESCRIPTION("ENSONIQ SoundScape PnP driver");
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IDX;
+static char* id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_STR;
+static long port[SNDRV_CARDS] __devinitdata = { [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_PORT };
+static int irq[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IRQ;
+static int mpu_irq[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IRQ;
+static int dma[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_DMA;
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index number for SoundScape soundcard");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "Description for SoundScape card");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+
+MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(port, "Port # for SoundScape driver.");
+MODULE_PARM_SYNTAX(port, SNDRV_ENABLED);
+
+MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(irq, "IRQ # for SoundScape driver.");
+MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC);
+
+MODULE_PARM(mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(mpu_irq, "MPU401 IRQ # for SoundScape driver.");
+MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC);
+
+MODULE_PARM(dma, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(dma, "DMA # for SoundScape driver.");
+MODULE_PARM_SYNTAX(dma, SNDRV_DMA8_DESC);
+  
+#ifdef CONFIG_PNP
+static struct pnp_card_device_id sscape_pnpids[] __devinitdata = {
+	{ .id = "ENS3081", .devs = { { "ENS0000" } } },
+	{ .id = "" }	/* end */
+};
+
+MODULE_DEVICE_TABLE(pnp_card, sscape_pnpids);
+#endif
+
+static snd_card_t *sscape_card[SNDRV_CARDS];
+
+
+#define MPU401_IO(i)     ((i) + 0)
+#define MIDI_DATA_IO(i)  ((i) + 0)
+#define MIDI_CTRL_IO(i)  ((i) + 1)
+#define HOST_CTRL_IO(i)  ((i) + 2)
+#define HOST_DATA_IO(i)  ((i) + 3)
+#define ODIE_ADDR_IO(i)  ((i) + 4)
+#define ODIE_DATA_IO(i)  ((i) + 5)
+#define CODEC_IO(i)      ((i) + 8)
+
+#define IC_ODIE  1
+#define IC_OPUS  2
+
+#define RX_READY 0x01
+#define TX_READY 0x02
+
+#define CMD_ACK           0x80
+#define CMD_SET_MIDI_VOL  0x84
+#define CMD_GET_MIDI_VOL  0x85
+#define CMD_XXX_MIDI_VOL  0x86
+#define CMD_SET_EXTMIDI   0x8a
+#define CMD_GET_EXTMIDI   0x8b
+#define CMD_SET_MT32      0x8c
+#define CMD_GET_MT32      0x8d
+
+enum GA_REG {
+	GA_INTSTAT_REG = 0,
+	GA_INTENA_REG,
+	GA_DMAA_REG,
+	GA_DMAB_REG,
+	GA_INTCFG_REG,
+	GA_DMACFG_REG,
+	GA_CDCFG_REG,
+	GA_SMCFGA_REG,
+	GA_SMCFGB_REG,
+	GA_HMCTL_REG
+};
+
+#define DMA_8BIT  0x80
+
+
+#define AD1845_FREQ_SEL_MSB    0x16
+#define AD1845_FREQ_SEL_LSB    0x17
+
+struct soundscape {
+	spinlock_t lock;
+	unsigned io_base;
+	int codec_type;
+	int ic_type;
+	struct resource *io_res;
+	cs4231_t *chip;
+	mpu401_t *mpu;
+	snd_hwdep_t *hw;
+
+	/*
+	 * The MIDI device won't work until we've loaded
+	 * its firmware via a hardware-dependent device IOCTL
+	 */
+	spinlock_t fwlock;
+	int hw_in_use;
+	unsigned long midi_usage;
+	unsigned char midi_vol;
+};
+
+#define INVALID_IRQ  ((unsigned)-1)
+
+
+static inline struct soundscape *get_card_soundscape(snd_card_t * c)
+{
+	return (struct soundscape *) (c->private_data);
+}
+
+static inline struct soundscape *get_mpu401_soundscape(mpu401_t * mpu)
+{
+	return (struct soundscape *) (mpu->private_data);
+}
+
+static inline struct soundscape *get_hwdep_soundscape(snd_hwdep_t * hw)
+{
+	return (struct soundscape *) (hw->private_data);
+}
+
+
+struct dmabuf {
+	size_t size;
+	unsigned char *data;
+	dma_addr_t addr;
+};
+
+/*
+ * Allocates some kernel memory that we can use for DMA.
+ * I think this means that the memory has to map to
+ * contiguous pages of physical memory.
+ */
+static struct dmabuf *get_dmabuf(struct dmabuf *buf, unsigned long s)
+{
+	if (buf) {
+		buf->data = snd_malloc_isa_pages_fallback(s, &buf->addr, &buf->size);
+		if (!buf->data) {
+			snd_printk(KERN_ERR "sscape: Failed to allocate %lu bytes for DMA\n", s);
+			return NULL;
+		}
+	}
+
+	return buf;
+}
+
+/*
+ * Release the DMA-able kernel memory ...
+ */
+static void free_dmabuf(struct dmabuf *buf)
+{
+	if (buf && buf->data)
+		snd_free_isa_pages(buf->size, buf->data, buf->addr);
+}
+
+
+/*
+ * This function writes to the SoundScape's control registers,
+ * but doesn't do any locking. It's up to the caller to do that.
+ * This is why this function is "unsafe" ...
+ */
+static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg, unsigned char val)
+{
+	outb(reg, ODIE_ADDR_IO(io_base));
+	outb(val, ODIE_DATA_IO(io_base));
+}
+
+/*
+ * Write to the SoundScape's control registers, and do the
+ * necessary locking ...
+ */
+static void sscape_write(struct soundscape *s, enum GA_REG reg, unsigned char val)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	sscape_write_unsafe(s->io_base, reg, val);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+/*
+ * Read from the SoundScape's control registers, but leave any
+ * locking to the caller. This is why the function is "unsafe" ...
+ */
+static inline unsigned char sscape_read_unsafe(unsigned io_base, enum GA_REG reg)
+{
+	outb(reg, ODIE_ADDR_IO(io_base));
+	return inb(ODIE_DATA_IO(io_base));
+}
+
+/*
+ * Puts the SoundScape into "host" mode, as compared to "MIDI" mode
+ */
+static inline void set_host_mode_unsafe(unsigned io_base)
+{
+	outb(0x0, HOST_CTRL_IO(io_base));
+}
+
+/*
+ * Puts the SoundScape into "MIDI" mode, as compared to "host" mode
+ */
+static inline void set_midi_mode_unsafe(unsigned io_base)
+{
+	outb(0x3, HOST_CTRL_IO(io_base));
+}
+
+/*
+ * Read the SoundScape's host-mode control register, but leave
+ * any locking issues to the caller ...
+ */
+static inline int host_read_unsafe(unsigned io_base)
+{
+	int data = -1;
+	if ((inb(HOST_CTRL_IO(io_base)) & RX_READY) != 0) {
+		data = inb(HOST_DATA_IO(io_base));
+	}
+
+	return data;
+}
+
+/*
+ * Read the SoundScape's host-mode control register, performing
+ * a limited amount of busy-waiting if the register isn't ready.
+ * Also leaves all locking-issues to the caller ...
+ */
+static int host_read_ctrl_unsafe(unsigned io_base, unsigned timeout)
+{
+	int data;
+
+	while (((data = host_read_unsafe(io_base)) < 0) && (timeout != 0)) {
+		udelay(100);
+		--timeout;
+	} /* while */
+
+	return data;
+}
+
+/*
+ * Write to the SoundScape's host-mode control registers, but
+ * leave any locking issues to the caller ...
+ */
+static inline int host_write_unsafe(unsigned io_base, unsigned char data)
+{
+	if ((inb(HOST_CTRL_IO(io_base)) & TX_READY) != 0) {
+		outb(data, HOST_DATA_IO(io_base));
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Write to the SoundScape's host-mode control registers, performing
+ * a limited amount of busy-waiting if the register isn't ready.
+ * Also leaves all locking-issues to the caller ...
+ */
+static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data,
+                                  unsigned timeout)
+{
+	int err;
+
+	while (!(err = host_write_unsafe(io_base, data)) && (timeout != 0)) {
+		udelay(100);
+		--timeout;
+	} /* while */
+
+	return err;
+}
+
+
+/*
+ * Check that the MIDI subsystem is operational. If it isn't,
+ * then we will hang the computer if we try to use it ...
+ *
+ * NOTE: This check is based upon observation, not documentation.
+ */
+static inline int verify_mpu401(const mpu401_t * mpu)
+{
+	return ((inb(MIDI_CTRL_IO(mpu->port)) & 0xc0) == 0x80);
+}
+
+/*
+ * This is apparently the standard way to initailise an MPU-401
+ */
+static inline void initialise_mpu401(const mpu401_t * mpu)
+{
+	outb(0, MIDI_DATA_IO(mpu->port));
+}
+
+/*
+ * Tell the SoundScape to activate the AD1845 chip (I think).
+ * The AD1845 detection fails if we *don't* do this, so I
+ * think that this is a good idea ...
+ */
+static inline void activate_ad1845_unsafe(unsigned io_base)
+{
+	sscape_write_unsafe(io_base, GA_HMCTL_REG, (sscape_read_unsafe(io_base, GA_HMCTL_REG) & 0xcf) | 0x10);
+	sscape_write_unsafe(io_base, GA_CDCFG_REG, 0x80);
+}
+
+/*
+ * Do the necessary ALSA-level cleanup to deallocate our driver ...
+ */
+static void soundscape_free(snd_card_t * c)
+{
+	register struct soundscape *sscape = get_card_soundscape(c);
+	release_resource(sscape->io_res);
+	kfree_nocheck(sscape->io_res);
+	free_dma(sscape->chip->dma1);
+}
+
+/*
+ * Put this process into an idle wait-state for a certain number
+ * of "jiffies". The process can almost certainly be rescheduled
+ * while we're waiting, and so we must NOT be holding any spinlocks
+ * when we call this function. If we are then we risk DEADLOCK in
+ * SMP (Ha!) or pre-emptible kernels.
+ */
+static inline void sleep(long jiffs, int state)
+{
+	set_current_state(state);
+	schedule_timeout(jiffs);
+}
+
+/*
+ * Tell the SoundScape to begin a DMA tranfer using the given channel.
+ * All locking issues are left to the caller.
+ */
+static inline void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg)
+{
+	sscape_write_unsafe(io_base, reg, sscape_read_unsafe(io_base, reg) | 0x01);
+	sscape_write_unsafe(io_base, reg, sscape_read_unsafe(io_base, reg) & 0xfe);
+}
+
+/*
+ * Wait for a DMA transfer to complete. This is a "limited busy-wait",
+ * and all locking issues are left to the caller.
+ */
+static int sscape_wait_dma_unsafe(unsigned io_base, enum GA_REG reg, unsigned timeout)
+{
+	while (!(sscape_read_unsafe(io_base, reg) & 0x01) && (timeout != 0)) {
+		udelay(100);
+		--timeout;
+	} /* while */
+
+	return (sscape_read_unsafe(io_base, reg) & 0x01);
+}
+
+/*
+ * Wait for the On-Board Processor to return its start-up
+ * acknowledgement sequence. This wait is too long for
+ * us to perform "busy-waiting", and so we must sleep.
+ * This in turn means that we must not be holding any
+ * spinlocks when we call this function.
+ */
+static int obp_startup_ack(struct soundscape *s, unsigned timeout)
+{
+	while (timeout != 0) {
+		unsigned long flags;
+		unsigned char x;
+
+		sleep(1, TASK_INTERRUPTIBLE);
+
+		spin_lock_irqsave(&s->lock, flags);
+		x = inb(HOST_DATA_IO(s->io_base));
+		spin_unlock_irqrestore(&s->lock, flags);
+		if ((x & 0xfe) == 0xfe)
+			return 1;
+
+		--timeout;
+	} /* while */
+
+	return 0;
+}
+
+/*
+ * Wait for the host to return its start-up acknowledgement
+ * sequence. This wait is too long for us to perform
+ * "busy-waiting", and so we must sleep. This in turn means
+ * that we must not be holding any spinlocks when we call
+ * this function.
+ */
+static int host_startup_ack(struct soundscape *s, unsigned timeout)
+{
+	while (timeout != 0) {
+		unsigned long flags;
+		unsigned char x;
+
+		sleep(1, TASK_INTERRUPTIBLE);
+
+		spin_lock_irqsave(&s->lock, flags);
+		x = inb(HOST_DATA_IO(s->io_base));
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (x == 0xfe)
+			return 1;
+
+		--timeout;
+	} /* while */
+
+	return 0;
+}
+
+/*
+ * Upload a byte-stream into the SoundScape using DMA channel A.
+ */
+static int upload_dma_data(struct soundscape *s,
+                           const unsigned char *data, size_t size,
+                           size_t dmasize)
+{
+	unsigned long flags;
+	struct dmabuf dma;
+	int ret;
+
+	if (!get_dmabuf(&dma, dmasize))
+		return -ENOMEM;
+
+	spin_lock_irqsave(&s->lock, flags);
+
+	/*
+	 * Reset the board ...
+	 */
+	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f);
+
+	/*
+	 * Enable the DMA channels and configure them ...
+	 */
+	sscape_write_unsafe(s->io_base, GA_DMACFG_REG, 0x50);
+	sscape_write_unsafe(s->io_base, GA_DMAA_REG, (s->chip->dma1 << 4) | DMA_8BIT);
+	sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20);
+
+	/*
+	 * Take the board out of reset ...
+	 */
+	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) | 0x80);
+
+	/*
+	 * Upload the user's data (firmware?) to the SoundScape
+	 * board through the DMA channel ...
+	 */
+	while (size != 0) {
+		unsigned long len;
+
+		/*
+		 * Apparently, copying to/from userspace can sleep.
+		 * We are therefore forbidden from holding any
+		 * spinlocks while we copy ...
+		 */
+		spin_unlock_irqrestore(&s->lock, flags);
+
+		/*
+		 * Remember that the data that we want to DMA
+		 * comes from USERSPACE. We have already verified
+		 * the userspace pointer ...
+		 */
+		len = min(size, dma.size);
+		__copy_from_user(dma.data, data, len);
+		data += len;
+		size -= len;
+
+		/*
+		 * Grab that spinlock again, now that we've
+		 * finished copying!
+		 */
+		spin_lock_irqsave(&s->lock, flags);
+
+		snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE);
+		sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG);
+		if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) {
+			/*
+			 * Don't forget to release this spinlock we're holding ...
+			 */
+			spin_unlock_irqrestore(&s->lock, flags);
+
+			snd_printk(KERN_ERR "sscape: DMA upload has timed out\n");
+			ret = -EAGAIN;
+			goto _release_dma;
+		}
+	} /* while */
+
+	set_host_mode_unsafe(s->io_base);
+
+	/*
+	 * Boot the board ... (I think)
+	 */
+	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) | 0x40);
+	spin_unlock_irqrestore(&s->lock, flags);
+
+	/*
+	 * If all has gone well, then the board should acknowledge
+	 * the new upload and tell us that it has rebooted OK. We
+	 * give it 5 seconds (max) ...
+	 */
+	ret = 0;
+	if (!obp_startup_ack(s, 5)) {
+		snd_printk(KERN_ERR "sscape: No response from on-board processor after upload\n");
+		ret = -EAGAIN;
+	} else if (!host_startup_ack(s, 5)) {
+		snd_printk(KERN_ERR "sscape: SoundScape failed to initialise\n");
+		ret = -EAGAIN;
+	}
+
+	_release_dma:
+	/*
+	 * NOTE!!! We are NOT holding any spinlocks at this point !!!
+	 */
+	sscape_write(s, GA_DMAA_REG, (s->ic_type == IC_ODIE ? 0x70 : 0x40));
+	free_dmabuf(&dma);
+
+	return ret;
+}
+
+/*
+ * Upload the bootblock(?) into the SoundScape. The only
+ * purpose of this block of code seems to be to tell
+ * us which version of the microcode we should be using.
+ *
+ * NOTE: The boot-block data resides in USER-SPACE!!!
+ *       However, we have already verified its memory
+ *       addresses by the time we get here.
+ */
+static int sscape_upload_bootblock(struct soundscape *sscape, struct sscape_bootblock *bb)
+{
+	unsigned long flags;
+	int data = 0;
+	int ret;
+
+	ret = upload_dma_data(sscape, bb->code, sizeof(bb->code), PAGE_SIZE);
+
+	spin_lock_irqsave(&sscape->lock, flags);
+	if (ret == 0) {
+		data = host_read_ctrl_unsafe(sscape->io_base, 100);
+	}
+	set_midi_mode_unsafe(sscape->io_base);
+	spin_unlock_irqrestore(&sscape->lock, flags);
+
+	if (ret == 0) {
+		if (data < 0) {
+			snd_printk(KERN_ERR "sscape: timeout reading firmware version\n");
+			ret = -EAGAIN;
+		} else {
+			__copy_to_user(&bb->version, &data, sizeof(bb->version));
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * Upload the microcode into the SoundScape. The
+ * microcode is 64K of data, and if we try to copy
+ * it into a local variable then we will SMASH THE
+ * KERNEL'S STACK! We therefore leave it in USER
+ * SPACE, and save ourselves from copying it at all.
+ *
+ * We assume that the caller has already verified the
+ * userspace memory addresses.
+ */
+static int sscape_upload_microcode(struct soundscape *sscape,
+                                   const struct sscape_microcode *mc)
+{
+	unsigned long flags;
+	int ret;
+
+	if ((ret = upload_dma_data(sscape, mc->code, sizeof(mc->code), PAGE_SIZE * 16)) == 0) {
+		snd_printk(KERN_INFO "sscape: MIDI firmware loaded\n");
+	}
+
+	spin_lock_irqsave(&sscape->lock, flags);
+	set_midi_mode_unsafe(sscape->io_base);
+	spin_unlock_irqrestore(&sscape->lock, flags);
+
+	initialise_mpu401(sscape->mpu);
+
+	return ret;
+}
+
+/*
+ * Hardware-specific device functions, to implement special
+ * IOCTLs for the SoundScape card. This is how we upload
+ * the microcode into the card, for example, and so we
+ * must ensure that no two processes can open this device
+ * simultaneously, and that we can't open it at all if
+ * someone is using the MIDI device.
+ */
+static int sscape_hw_open(snd_hwdep_t * hw, struct file *file)
+{
+	register struct soundscape *sscape = get_hwdep_soundscape(hw);
+	unsigned long flags;
+	int err;
+
+	spin_lock_irqsave(&sscape->fwlock, flags);
+
+	if ((sscape->midi_usage != 0) || sscape->hw_in_use) {
+		err = -EBUSY;
+	} else {
+		sscape->hw_in_use = 1;
+		err = 0;
+	}
+
+	spin_unlock_irqrestore(&sscape->fwlock, flags);
+	return err;
+}
+
+static int sscape_hw_release(snd_hwdep_t * hw, struct file *file)
+{
+	register struct soundscape *sscape = get_hwdep_soundscape(hw);
+	unsigned long flags;
+
+	spin_lock_irqsave(&sscape->fwlock, flags);
+	sscape->hw_in_use = 0;
+	spin_unlock_irqrestore(&sscape->fwlock, flags);
+	return 0;
+}
+
+static int sscape_hw_ioctl(snd_hwdep_t * hw, struct file *file,
+                           unsigned int cmd, unsigned long arg)
+{
+	struct soundscape *sscape = get_hwdep_soundscape(hw);
+	int err = -EBUSY;
+
+	switch (cmd) {
+	case SND_SSCAPE_LOAD_BOOTB:
+		{
+			register struct sscape_bootblock *bb = (struct sscape_bootblock *) arg;
+
+			/*
+			 * We are going to have to copy this data into a special
+			 * DMA-able buffer before we can upload it. We shall therefore
+			 * just check that the data pointer is valid for now ...
+			 */
+			if ((err = verify_area(VERIFY_READ, bb->code, sizeof(bb->code))) != 0)
+				return err;
+
+			/*
+			 * Now check that we can write the firmware version number too...
+			 */
+			if ((err = verify_area(VERIFY_WRITE, &bb->version, sizeof(bb->version))) != 0)
+				return err;
+
+			err = sscape_upload_bootblock(sscape, bb);
+		}
+		break;
+
+	case SND_SSCAPE_LOAD_MCODE:
+		{
+			register const struct sscape_microcode *mc = (const struct sscape_microcode *) arg;
+
+			/*
+			 * We are going to have to copy this data into a special
+			 * DMA-able buffer before we can upload it. We shall therefore
+			 * just check that the data pointer is valid for now.
+			 *
+			 * NOTE: This buffer is 64K long! That's WAY too big to
+			 *       copy into a stack-temporary anyway.
+			 */
+			if ((err = verify_area(VERIFY_READ, mc->code, sizeof(mc->code))) != 0)
+				return err;
+
+			err = sscape_upload_microcode(sscape, mc);
+		}
+		break;
+
+	default:
+		err = -EINVAL;
+		break;
+	} /* switch */
+
+	return err;
+}
+
+
+/*
+ * Mixer control for the SoundScape's MIDI device.
+ */
+static int sscape_midi_info(snd_kcontrol_t * ctl,
+                            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 sscape_midi_get(snd_kcontrol_t * kctl,
+                           snd_ctl_elem_value_t * uctl)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kctl);
+	snd_card_t *card = chip->card;
+	register struct soundscape *s = get_card_soundscape(card);
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	set_host_mode_unsafe(s->io_base);
+
+	if (host_write_ctrl_unsafe(s->io_base, CMD_GET_MIDI_VOL, 100)) {
+		uctl->value.integer.value[0] = host_read_ctrl_unsafe(s->io_base, 100);
+	}
+
+	set_midi_mode_unsafe(s->io_base);
+	spin_unlock_irqrestore(&s->lock, flags);
+	return 0;
+}
+
+static int sscape_midi_put(snd_kcontrol_t * kctl,
+                           snd_ctl_elem_value_t * uctl)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kctl);
+	snd_card_t *card = chip->card;
+	register struct soundscape *s = get_card_soundscape(card);
+	unsigned long flags;
+	int change;
+
+	spin_lock_irqsave(&s->lock, flags);
+
+	/*
+	 * We need to put the board into HOST mode before we
+	 * can send any volume-changing HOST commands ...
+	 */
+	set_host_mode_unsafe(s->io_base);
+
+	/*
+	 * To successfully change the MIDI volume setting, you seem to
+	 * have to write a volume command, write the new volume value,
+	 * and then perform another volume-related command. Perhaps the
+	 * first command is an "open" and the second command is a "close"?
+	 */
+	if (s->midi_vol == ((unsigned char) uctl->value.integer. value[0] & 127)) {
+		change = 0;
+		goto __skip_change;
+	}
+	change = (host_write_ctrl_unsafe(s->io_base, CMD_SET_MIDI_VOL, 100)
+	          && host_write_ctrl_unsafe(s->io_base, ((unsigned char) uctl->value.integer. value[0]) & 127, 100)
+	          && host_write_ctrl_unsafe(s->io_base, CMD_XXX_MIDI_VOL, 100));
+      __skip_change:
+
+	/*
+	 * Take the board out of HOST mode and back into MIDI mode ...
+	 */
+	set_midi_mode_unsafe(s->io_base);
+
+	spin_unlock_irqrestore(&s->lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t midi_mixer_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "MIDI",
+	.info = sscape_midi_info,
+	.get = sscape_midi_get,
+	.put = sscape_midi_put
+};
+
+/*
+ * The SoundScape can use two IRQs from a possible set of four.
+ * These IRQs are encoded as bit patterns so that they can be
+ * written to the control registers.
+ */
+static unsigned __devinit get_irq_config(int irq)
+{
+	static const int valid_irq[] __devinitdata = { 9, 5, 7, 10 };
+	unsigned cfg;
+
+	for (cfg = 0; cfg < ARRAY_SIZE(valid_irq); ++cfg) {
+		if (irq == valid_irq[cfg])
+			return cfg;
+	} /* for */
+
+	return INVALID_IRQ;
+}
+
+
+/*
+ * Perform certain arcane port-checks to see whether there
+ * is a SoundScape board lurking behind the given ports.
+ */
+static int __devinit detect_sscape(struct soundscape *s)
+{
+	unsigned long flags;
+	unsigned d;
+	int retval = 0;
+
+	spin_lock_irqsave(&s->lock, flags);
+
+	/*
+	 * The following code is lifted from the original OSS driver,
+	 * and as I don't have a datasheet I cannot really comment
+	 * on what it is doing...
+	 */
+	if ((inb(HOST_CTRL_IO(s->io_base)) & 0x78) != 0)
+		goto _done;
+
+	d = inb(ODIE_ADDR_IO(s->io_base)) & 0xf0;
+	if ((d & 0x80) != 0)
+		goto _done;
+
+	if (d == 0) {
+		s->codec_type = 1;
+		s->ic_type = IC_ODIE;
+	} else if ((d & 0x60) != 0) {
+		s->codec_type = 2;
+		s->ic_type = IC_OPUS;
+	} else
+		goto _done;
+
+	outb(0xfa, ODIE_ADDR_IO(s->io_base));
+	if ((inb(ODIE_ADDR_IO(s->io_base)) & 0x9f) != 0x0a)
+		goto _done;
+
+	outb(0xfe, ODIE_ADDR_IO(s->io_base));
+	if ((inb(ODIE_ADDR_IO(s->io_base)) & 0x9f) != 0x0e)
+		goto _done;
+	if ((inb(ODIE_DATA_IO(s->io_base)) & 0x9f) != 0x0e)
+		goto _done;
+
+	/*
+	 * SoundScape successfully detected!
+	 */
+	retval = 1;
+
+	_done:
+	spin_unlock_irqrestore(&s->lock, flags);
+	return retval;
+}
+
+/*
+ * ALSA callback function, called when attempting to open the MIDI device.
+ * Check that the MIDI firmware has been loaded, because we don't want
+ * to crash the machine. Also check that someone isn't using the hardware
+ * IOCTL device.
+ */
+static int mpu401_open(mpu401_t * mpu)
+{
+	int err;
+
+	if (!verify_mpu401(mpu)) {
+		snd_printk(KERN_ERR "sscape: MIDI disabled, please load firmware\n");
+		err = -ENODEV;
+	} else {
+		register struct soundscape *sscape = get_mpu401_soundscape(mpu);
+		unsigned long flags;
+
+		spin_lock_irqsave(&sscape->fwlock, flags);
+
+		if (sscape->hw_in_use || (sscape->midi_usage == ULONG_MAX)) {
+			err = -EBUSY;
+		} else {
+			++(sscape->midi_usage);
+			err = 0;
+		}
+
+		spin_unlock_irqrestore(&sscape->fwlock, flags);
+	}
+
+	return err;
+}
+
+static void mpu401_close(mpu401_t * mpu)
+{
+	register struct soundscape *sscape = get_mpu401_soundscape(mpu);
+	unsigned long flags;
+
+	spin_lock_irqsave(&sscape->fwlock, flags);
+	--(sscape->midi_usage);
+	spin_unlock_irqrestore(&sscape->fwlock, flags);
+}
+
+/*
+ * Initialse an MPU-401 subdevice for MIDI support on the SoundScape.
+ */
+static int __devinit create_mpu401(snd_card_t * card, int devnum, unsigned long port, int irq)
+{
+	struct soundscape *sscape = get_card_soundscape(card);
+	snd_rawmidi_t *rawmidi;
+	int err;
+
+#define MPU401_SHARE_HARDWARE  1
+	if ((err = snd_mpu401_uart_new(card, devnum,
+	                               MPU401_HW_MPU401,
+	                               port, MPU401_SHARE_HARDWARE,
+	                               irq, SA_INTERRUPT,
+	                               &rawmidi)) == 0) {
+		mpu401_t *mpu = (mpu401_t *) rawmidi->private_data;
+		mpu->open_input = mpu401_open;
+		mpu->open_output = mpu401_open;
+		mpu->close_input = mpu401_close;
+		mpu->close_output = mpu401_close;
+		mpu->private_data = sscape;
+		sscape->mpu = mpu;
+
+		initialise_mpu401(mpu);
+	}
+
+	return err;
+}
+
+
+/*
+ * Override for the CS4231 playback format function.
+ * The AD1845 has much simpler format and rate selection.
+ */
+static void ad1845_playback_format(cs4231_t * chip, snd_pcm_hw_params_t * params, unsigned char format)
+{
+	unsigned long flags;
+	unsigned rate = params_rate(params);
+
+	/*
+	 * The AD1845 can't handle sample frequencies
+	 * outside of 4 kHZ to 50 kHZ
+	 */
+	if (rate > 50000)
+		rate = 50000;
+	else if (rate < 4000)
+		rate = 4000;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+
+	/*
+	 * Program the AD1845 correctly for the playback stream.
+	 * Note that we do NOT need to toggle the MCE bit because
+	 * the PLAYBACK_ENABLE bit of the Interface Configuration
+	 * register is set.
+	 * 
+	 * NOTE: We seem to need to write to the MSB before the LSB
+	 *       to get the correct sample frequency.
+	 */
+	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, (format & 0xf0));
+	snd_cs4231_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8));
+	snd_cs4231_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate);
+
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/*
+ * Override for the CS4231 capture format function. 
+ * The AD1845 has much simpler format and rate selection.
+ */
+static void ad1845_capture_format(cs4231_t * chip, snd_pcm_hw_params_t * params, unsigned char format)
+{
+	unsigned long flags;
+	unsigned rate = params_rate(params);
+
+	/*
+	 * The AD1845 can't handle sample frequencies 
+	 * outside of 4 kHZ to 50 kHZ
+	 */
+	if (rate > 50000)
+		rate = 50000;
+	else if (rate < 4000)
+		rate = 4000;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+
+	/*
+	 * Program the AD1845 correctly for the playback stream.
+	 * Note that we do NOT need to toggle the MCE bit because
+	 * the CAPTURE_ENABLE bit of the Interface Configuration
+	 * register is set.
+	 *
+	 * NOTE: We seem to need to write to the MSB before the LSB
+	 *       to get the correct sample frequency.
+	 */
+	snd_cs4231_out(chip, CS4231_REC_FORMAT, (format & 0xf0));
+	snd_cs4231_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8));
+	snd_cs4231_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate);
+
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/*
+ * Create an AD1845 PCM subdevice on the SoundScape. The AD1845
+ * is very much like a CS4231, with a few extra bits. We will
+ * try to support at least some of the extra bits by overriding
+ * some of the CS4231 callback.
+ */
+static int __devinit create_ad1845(snd_card_t * card, unsigned port, int irq, int dma1)
+{
+	register struct soundscape *sscape = get_card_soundscape(card);
+	cs4231_t *chip;
+	int err;
+
+#define CS4231_SHARE_HARDWARE  (CS4231_HWSHARE_DMA1 | CS4231_HWSHARE_DMA2)
+	/*
+	 * The AD1845 PCM device is only half-duplex, and so
+	 * we only give it one DMA channel ...
+	 */
+	if ((err = snd_cs4231_create(card,
+				     port, -1, irq, dma1, dma1,
+				     CS4231_HW_DETECT,
+				     CS4231_HWSHARE_DMA1, &chip)) == 0) {
+		unsigned long flags;
+		snd_pcm_t *pcm;
+
+#define AD1845_FREQ_SEL_ENABLE  0x08
+
+#define AD1845_PWR_DOWN_CTRL   0x1b
+#define AD1845_CRYS_CLOCK_SEL  0x1d
+
+/*
+ * It turns out that the PLAYBACK_ENABLE bit is set
+ * by the lowlevel driver ...
+ *
+#define AD1845_IFACE_CONFIG  \
+           (CS4231_AUTOCALIB | CS4231_RECORD_ENABLE | CS4231_PLAYBACK_ENABLE)
+    snd_cs4231_mce_up(chip);
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    snd_cs4231_out(chip, CS4231_IFACE_CTRL, AD1845_IFACE_CONFIG);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    snd_cs4231_mce_down(chip);
+ */
+
+		/*
+		 * The input clock frequency on the SoundScape must
+		 * be 14.31818 MHz, because we must set this register
+		 * to get the playback to sound correct ...
+		 */
+		snd_cs4231_mce_up(chip);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		snd_cs4231_out(chip, AD1845_CRYS_CLOCK_SEL, 0x20);
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		snd_cs4231_mce_down(chip);
+
+		/*
+		 * More custom configuration:
+		 * a) select "mode 2", and provide a current drive of 8 mA
+		 * b) enable frequency selection (for capture/playback)
+		 */
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		snd_cs4231_out(chip, CS4231_MISC_INFO, (CS4231_MODE2 | 0x10));
+		snd_cs4231_out(chip, AD1845_PWR_DOWN_CTRL, snd_cs4231_in(chip, AD1845_PWR_DOWN_CTRL) | AD1845_FREQ_SEL_ENABLE);
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+		if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) {
+			snd_printk(KERN_ERR "sscape: No PCM device for AD1845 chip\n");
+			goto _error;
+		}
+
+		if ((err = snd_cs4231_mixer(chip)) < 0) {
+			snd_printk(KERN_ERR "sscape: No mixer device for AD1845 chip\n");
+			goto _error;
+		}
+
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&midi_mixer_ctl, chip))) < 0) {
+			snd_printk(KERN_ERR "sscape: Could not create MIDI mixer control\n");
+			goto _error;
+		}
+
+		strcpy(card->driver, "SoundScape");
+		strcpy(card->shortname, pcm->name);
+		snprintf(card->longname, sizeof(card->longname),
+		         "%s at 0x%lx, IRQ %d, DMA %d\n",
+		         pcm->name, chip->port, chip->irq, chip->dma1);
+		chip->set_playback_format = ad1845_playback_format;
+		chip->set_capture_format = ad1845_capture_format;
+		sscape->chip = chip;
+	}
+
+	_error:
+	return err;
+}
+
+
+struct params
+{
+	int index;
+	const char *id;
+	unsigned port;
+	int irq;
+	int mpu_irq;
+	int dma1;
+};
+
+
+static inline struct params*
+init_params(struct params *params,
+            int index,
+            const char *id,
+            unsigned port,
+            int irq,
+            int mpu_irq,
+            int dma1)
+{
+	params->index = index;
+	params->id = id;
+	params->port = port;
+	params->irq = irq;
+	params->mpu_irq = mpu_irq;  
+	params->dma1 = (dma1 & 0x03);
+
+	return params;
+}
+
+
+/*
+ * Create an ALSA soundcard entry for the SoundScape, using
+ * the given list of port, IRQ and DMA resources.
+ */
+static int __devinit create_sscape(const struct params *params, snd_card_t **rcardp)
+{
+	snd_card_t *card;
+	register struct soundscape *sscape;
+	register unsigned dma_cfg;
+	unsigned irq_cfg;
+	unsigned mpu_irq_cfg;
+	struct resource *io_res;
+	unsigned long flags;
+	int err;
+
+	/*
+	 * Check that the user didn't pass us garbage data ...
+	 */
+	irq_cfg = get_irq_config(params->irq);
+	if (irq_cfg == INVALID_IRQ) {
+		snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", params->irq);
+		return -ENXIO;
+	}
+
+	mpu_irq_cfg = get_irq_config(params->mpu_irq);
+	if (mpu_irq_cfg == INVALID_IRQ) {
+		printk(KERN_ERR "sscape: Invalid IRQ %d\n", params->mpu_irq);
+		return -ENXIO;
+	}
+
+	/*
+	 * Grab IO ports that we will need to probe so that we
+	 * can detect and control this hardware ...
+	 */
+	if ((io_res = request_region(params->port, 8, "SoundScape")) == NULL) {
+		return -EBUSY;
+	}
+
+	/*
+	 * Grab both DMA channels (OK, only one for now) ...
+	 */
+	if ((err = request_dma(params->dma1, "SoundScape")) < 0) {
+		goto _release_region;
+	}
+
+	/*
+	 * Create a new ALSA sound card entry, in anticipation
+	 * of detecting our hardware ...
+	 */
+	if ((card = snd_card_new(params->index, params->id, THIS_MODULE, sizeof(struct soundscape))) == NULL) {
+		err = -ENOMEM;
+		goto _release_dma;
+	}
+
+	sscape = get_card_soundscape(card);
+	spin_lock_init(&sscape->lock);
+	spin_lock_init(&sscape->fwlock);
+	sscape->io_res = io_res;
+	sscape->io_base = params->port;
+
+	if (!detect_sscape(sscape)) {
+		printk(KERN_ERR "sscape: hardware not detected at 0x%x\n", sscape->io_base);
+		err = -ENODEV;
+		goto _release_card;
+	}
+
+	printk(KERN_INFO "sscape: hardware detected at 0x%x, using IRQ %d, DMA %d\n",
+	                 sscape->io_base, params->irq, params->dma1);
+
+	/*
+	 * Now create the hardware-specific device so that we can
+	 * load the microcode into the on-board processor.
+	 * We cannot use the MPU-401 MIDI system until this firmware
+	 * has been loaded into the card.
+	 */
+	if ((err = snd_hwdep_new(card, "MC68EC000", 0, &(sscape->hw))) < 0) {
+		printk(KERN_ERR "sscape: Failed to create firmware device\n");
+		goto _release_card;
+	}
+	strlcpy(sscape->hw->name, "SoundScape M68K", sizeof(sscape->hw->name));
+	sscape->hw->name[sizeof(sscape->hw->name) - 1] = '\0';
+	sscape->hw->iface = SNDRV_HWDEP_IFACE_SSCAPE;
+	sscape->hw->ops.open = sscape_hw_open;
+	sscape->hw->ops.release = sscape_hw_release;
+	sscape->hw->ops.ioctl = sscape_hw_ioctl;
+	sscape->hw->private_data = sscape;
+
+	/*
+	 * Tell the on-board devices where their resources are (I think -
+	 * I can't be sure without a datasheet ... So many magic values!)
+	 */
+	spin_lock_irqsave(&sscape->lock, flags);
+
+	activate_ad1845_unsafe(sscape->io_base);
+
+	sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x00); /* disable */
+	sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
+	sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
+
+	/*
+	 * Enable and configure the DMA channels ...
+	 */
+	sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
+	dma_cfg = (sscape->ic_type == IC_ODIE ? 0x70 : 0x40);
+	sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
+	sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
+
+	sscape_write_unsafe(sscape->io_base,
+	                    GA_INTCFG_REG, 0xf0 | (mpu_irq_cfg << 2) | mpu_irq_cfg);
+	sscape_write_unsafe(sscape->io_base,
+	                    GA_CDCFG_REG, 0x09 | DMA_8BIT | (params->dma1 << 4) | (irq_cfg << 1));
+
+	spin_unlock_irqrestore(&sscape->lock, flags);
+
+	/*
+	 * We have now enabled the codec chip, and so we should
+	 * detect the AD1845 device ...
+	 */
+	if ((err = create_ad1845(card, CODEC_IO(params->port), params->irq, params->dma1)) < 0) {
+		printk(KERN_ERR "sscape: No AD1845 device at 0x%x, IRQ %d\n",
+		                CODEC_IO(params->port), params->irq);
+		goto _release_card;
+	}
+#define MIDI_DEVNUM  0
+	if ((err = create_mpu401(card, MIDI_DEVNUM, MPU401_IO(params->port), params->mpu_irq)) < 0) {
+		printk(KERN_ERR "sscape: Failed to create MPU-401 device at 0x%x\n",
+		                MPU401_IO(params->port));
+		goto _release_card;
+	}
+
+	/*
+	 * Enable the master IRQ ...
+	 */
+	sscape_write(sscape, GA_INTENA_REG, 0x80);
+
+	if ((err = snd_card_register(card)) < 0) {
+		printk(KERN_ERR "sscape: Failed to register sound card\n");
+		goto _release_card;
+	}
+
+	/*
+	 * Initialize mixer
+	 */
+	sscape->midi_vol = 0;
+	host_write_ctrl_unsafe(sscape->io_base, CMD_SET_MIDI_VOL, 100);
+	host_write_ctrl_unsafe(sscape->io_base, 0, 100);
+	host_write_ctrl_unsafe(sscape->io_base, CMD_XXX_MIDI_VOL, 100);
+
+	/*
+	 * Now that we have successfully created this sound card,
+	 * it is safe to store the pointer.
+	 * NOTE: we only register the sound card's "destructor"
+	 *       function now that our "constructor" has completed.
+	 */
+	card->private_free = soundscape_free;
+	*rcardp = card;
+
+	return 0;
+
+	_release_card:
+	snd_card_free(card);
+
+	_release_dma:
+	free_dma(params->dma1);
+
+	_release_region:
+	release_resource(io_res);
+	kfree_nocheck(io_res);
+
+	return err;
+}
+
+
+static int sscape_cards __devinitdata;
+static struct params sscape_params[SNDRV_CARDS] __devinitdata;
+
+#ifdef CONFIG_PNP
+static inline int __devinit get_next_autoindex(int i)
+{
+	while ((i < SNDRV_CARDS) && (port[i] != SNDRV_AUTO_PORT)) {
+		++i;
+	} /* while */
+
+	return i;
+}
+
+
+static inline int __devinit is_port_known(unsigned io, struct params *params, int cards)
+{
+	while (--cards >= 0) {
+		if (params[cards].port == io)
+			return 1;
+	} /* while */
+
+	return 0;
+}
+
+static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
+				       const struct pnp_card_device_id *pid)
+{
+	struct pnp_dev *dev;
+	static int idx = 0;
+	int ret;
+
+	/*
+	 * Allow this function to fail *quietly* if all the ISA PnP
+	 * devices were configured using module parameters instead.
+	 */
+	if ((idx = get_next_autoindex(idx)) >= SNDRV_CARDS) {
+		return -ENOSPC;
+	}
+
+	/*
+	 * We have found a candidate ISA PnP card. Now we
+	 * have to check that it has the devices that we
+	 * expect it to have.
+	 *
+	 * We will NOT try and autoconfigure all of the resources
+	 * needed and then activate the card as we are assuming that
+	 * has already been done at boot-time using /proc/isapnp.
+	 * We shall simply try to give each active card the resources
+	 * that it wants. This is a sensible strategy for a modular
+	 * system where unused modules are unloaded regularly.
+	 *
+	 * This strategy is utterly useless if we compile the driver
+	 * into the kernel, of course.
+	 */
+	// printk(KERN_INFO "sscape: %s\n", card->name);
+
+	/*
+	 * Check that we still have room for another sound card ...
+	 */
+	if (sscape_cards >= SNDRV_CARDS) {
+		printk(KERN_ERR "sscape: No room for another ALSA device\n");
+		return -ENOSPC;
+	}
+
+	ret = -ENODEV;
+
+	dev = pnp_request_card_device(pcard, pid->devs[0].id, NULL);
+	if (dev) {
+		struct params *this;
+		if (!pnp_is_active(dev)) {
+			if (pnp_activate_dev(dev) < 0) {
+				printk(KERN_INFO "sscape: device is inactive\n");
+				return -EBUSY;
+			}
+		}
+		/*
+		 * Read the correct parameters off the ISA PnP bus ...
+		 */
+		this = init_params(&sscape_params[sscape_cards],
+				   index[idx],
+				   id[idx],
+				   pnp_port_start(dev, 0),
+				   pnp_irq(dev, 0),
+				   pnp_irq(dev, 1),
+				   pnp_dma(dev, 0));
+
+		/*
+		 * Do we know about this sound card already?
+		 */
+		if ( !is_port_known(this->port, sscape_params, sscape_cards) ) {
+			snd_card_t *card;
+
+			ret = create_sscape(this, &card);
+			if (ret < 0)
+				return ret;
+			pnp_set_card_drvdata(pcard, card);
+			++sscape_cards;
+			++idx;
+		}
+	}
+
+	return ret;
+}
+
+static void __devexit sscape_pnp_remove(struct pnp_card_link * pcard)
+{
+	snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard);
+        
+	pnp_set_card_drvdata(pcard, NULL);
+	snd_card_disconnect(card);
+	snd_card_free_in_thread(card);
+}
+
+static struct pnp_card_driver sscape_pnpc_driver = {
+	.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
+	.name = "sscape",
+	.id_table = sscape_pnpids,
+	.probe = sscape_pnp_detect,
+	.remove = __devexit_p(sscape_pnp_remove),
+};
+
+#endif /* CONFIG_PNP */
+
+static int __init sscape_manual_probe(struct params *params)
+{
+	int ret;
+	unsigned i;
+	snd_card_t *card;
+
+	for (i = 0; i < SNDRV_CARDS; ++i) {
+		/*
+		 * We do NOT probe for ports.
+		 * If we're not given a port number for this
+		 * card then we completely ignore this line
+		 * of parameters.
+		 */
+		if (port[i] == SNDRV_AUTO_PORT)
+			continue;
+
+		/*
+		 * Make sure we were given ALL of the other parameters.
+		 */
+		if ( (irq[i] == SNDRV_AUTO_IRQ) ||
+		     (mpu_irq[i] == SNDRV_AUTO_IRQ) ||
+		     (dma[i] == SNDRV_AUTO_DMA) ) {
+			printk(KERN_INFO
+			       "sscape: insufficient parameters, need IO, IRQ, MPU-IRQ and DMA\n");
+			return -ENXIO;
+		}
+
+		/*
+		 * This cards looks OK ...
+		 */
+		init_params(params, index[i], id[i], port[i], irq[i], mpu_irq[i], dma[i]);
+
+		ret = create_sscape(params, &card);
+		if (ret < 0)
+			return ret;
+
+		sscape_card[sscape_cards] = card;
+		params++;
+		sscape_cards++;
+	} /* for */
+
+	return 0;
+}
+
+
+static void sscape_exit(void)
+{
+	unsigned i;
+
+#ifdef CONFIG_PNP
+	pnp_unregister_card_driver(&sscape_pnpc_driver);
+#endif
+	for (i = 0; i < ARRAY_SIZE(sscape_card); ++i) {
+		snd_card_free(sscape_card[i]);
+	} /* for */
+}
+
+
+static int __init sscape_init(void)
+{
+	int ret;
+
+	/*
+	 * First check whether we were passed any parameters.
+	 * These MUST take precedence over ANY automatic way
+	 * of allocating cards, because the operator is
+	 * S-P-E-L-L-I-N-G it out for us...
+	 */
+	ret = sscape_manual_probe(sscape_params);
+	if (ret < 0) {
+		int i;
+		for (i = 0; i < sscape_cards; ++i)
+			snd_card_free(sscape_card[i]);
+		return ret;
+	}
+
+#ifdef CONFIG_PNP
+	if (sscape_cards < SNDRV_CARDS) {
+		ret = pnp_register_card_driver(&sscape_pnpc_driver);
+		if (ret < 0) {
+			sscape_exit();
+			return ret;
+		}
+	}
+#endif
+
+	return 0;
+}
+
+module_init(sscape_init);
+module_exit(sscape_exit);
+
+#ifndef MODULE
+
+/* format is: snd-sscape=index,id,port,irq,mpu_irq,dma */
+
+static int __init builtin_sscape_setup(char *str)
+{
+	static unsigned __initdata nr_dev;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+
+	(void)((get_option(&str, &index[nr_dev]) == 2) &&
+	       (get_option(&str, (int*)&id[nr_dev]) == 2) &&
+	       (get_option(&str, (int*)&port[nr_dev]) == 2) &&
+	       (get_option(&str, &irq[nr_dev]) == 2) &&
+	       (get_option(&str, &mpu_irq[nr_dev]) == 2) &&
+	       (get_option(&str, &dma[nr_dev]) == 2)); 
+ 
+	++nr_dev;
+	return 1;
+}
+
+__setup("snd-sscape=", builtin_sscape_setup);
+
+#endif
+
diff -urN linux-2.5.70-bk6/sound/isa/wavefront/wavefront.c linux-2.5.70-bk7/sound/isa/wavefront/wavefront.c
--- linux-2.5.70-bk6/sound/isa/wavefront/wavefront.c	2003-05-26 18:00:45.000000000 -0700
+++ linux-2.5.70-bk7/sound/isa/wavefront/wavefront.c	2003-06-02 04:39:12.000000000 -0700
@@ -179,6 +179,7 @@
 	*/
 
 	if (cs4232_pcm_port[dev] != SNDRV_AUTO_PORT)
+		pnp_resource_change(&cfg->port_resource[0], cs4232_pcm_port[dev], 4);
 	if (fm_port[dev] != SNDRV_AUTO_PORT)
 		pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
 	if (dma1[dev] != SNDRV_AUTO_DMA)
@@ -710,6 +711,9 @@
 	cards += pnp_register_card_driver(&wavefront_pnpc_driver);
 #endif
 	if (!cards) {
+#ifdef CONFIG_PNP
+		pnp_unregister_card_driver(&wavefront_pnpc_driver);
+#endif
 #ifdef MODULE
 		printk (KERN_ERR "No WaveFront cards found or devices busy\n");
 #endif
@@ -722,7 +726,9 @@
 {
 	int idx;
 
+#ifdef CONFIG_PNP
 	pnp_unregister_card_driver(&wavefront_pnpc_driver);
+#endif
 	for (idx = 0; idx < SNDRV_CARDS; idx++)
 		snd_card_free(snd_wavefront_legacy[idx]);
 }
diff -urN linux-2.5.70-bk6/sound/parisc/Kconfig linux-2.5.70-bk7/sound/parisc/Kconfig
--- linux-2.5.70-bk6/sound/parisc/Kconfig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/parisc/Kconfig	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,13 @@
+# ALSA PA-RISC drivers
+
+menu "ALSA PA-RISC devices"
+	depends on SND!=n && PARISC
+
+config SND_HARMONY
+	tristate "Harmony/Vivace sound chip"
+	depends on SND
+	help
+	  Say 'Y' or 'M' to include support for Harmony/Vivace soundchip
+	  on HP712s, 715/new and many other GSC based machines.
+
+endmenu
diff -urN linux-2.5.70-bk6/sound/parisc/Makefile linux-2.5.70-bk7/sound/parisc/Makefile
--- linux-2.5.70-bk6/sound/parisc/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/parisc/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,8 @@
+#
+# Makefile for ALSA
+#
+
+snd-harmony-objs := harmony.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_HARMONY) += snd-harmony.o
diff -urN linux-2.5.70-bk6/sound/parisc/harmony.c linux-2.5.70-bk7/sound/parisc/harmony.c
--- linux-2.5.70-bk6/sound/parisc/harmony.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/parisc/harmony.c	2003-06-02 04:39:12.000000000 -0700
@@ -0,0 +1,1147 @@
+/*
+ *  Harmony chipset driver
+ *
+ *	This is a sound driver for ASP's and Lasi's Harmony sound chip
+ *	and is unlikely to be used for anything other than on a HP PA-RISC.
+ *
+ *	Harmony is found in HP 712s, 715/new and many other GSC based machines.
+ *	On older 715 machines you'll find the technically identical chip 
+ *	called 'Vivace'. Both Harmony and Vicace are supported by this driver.
+ *
+ *  this ALSA driver is based on OSS driver by:
+ *	Copyright 2000 (c) Linuxcare Canada, Alex deVries <alex@linuxcare.com>
+ *	Copyright 2000-2002 (c) Helge Deller <deller@gmx.de>
+ *	Copyright 2001 (c) Matthieu Delahaye <delahaym@esiee.fr>
+ *
+ * TODO:
+ * - use generic DMA interface and ioremap()/iounmap()
+ * - capture is still untested (and probaby non-working)
+ * - spin locks
+ * - implement non-consistent DMA pages
+ * - implement gain meter
+ * - module parameters
+ * - correct cleaning sequence
+ * - better error checking
+ * - try to have a better quality.
+ *   
+ */
+
+/*
+ * Harmony chipset 'modus operandi'.
+ * - This chipset is found in some HP 32bit workstations, like 712, or B132 class.
+ * most of controls are done through registers. Register are found at a fixed offset
+ * from the hard physical adress, given in struct dev by register_parisc_driver.
+ *
+ * Playback and recording use 4kb pages (dma or not, depending on the machine).
+ *
+ * Most of PCM playback & capture is done through interrupt. When harmony needs
+ * a new buffer to put recorded data or read played PCM, it sends an interrupt.
+ * Bits 2 and 10 of DSTATUS register are '1' when harmony needs respectively
+ * a new page for recording and playing. 
+ * Interrupt are disabled/enabled by writing to bit 32 of DSTATUS. 
+ * Adresses of next page to be played is put in PNXTADD register, next page
+ * to be recorded is put in RNXTADD. There is 2 read-only registers, PCURADD and 
+ * RCURADD that provides adress of current page.
+ * 
+ * Harmony has no way to controll full duplex or half duplex mode. It means
+ * that we always need to provide adresses of playback and capture data, even
+ * when this is not needed. That's why we statically alloc one graveyard
+ * buffer (to put recorded data in play-only mode) and a silence buffer.
+ * 
+ * Bitrate, number of channels and data format are controlled with
+ * the CNTL register.
+ *
+ * Mixer work is done through one register (GAINCTL). Only input gain,
+ * output attenuation and general attenuation control is provided. There is
+ * also controls for enabling/disabling internal speaker and line
+ * input.
+ *
+ * Buffers used by this driver are all DMA consistent. Since harmony is
+ * not "real" pci device, we use a fake struct pci_dev for
+ * pci_alloc_consistent().
+ * (note that some machines -712 for ex.- don't implement DMA consistent
+ * memory, so we will need to use kmalloc instead)
+ */
+
+#include <linux/delay.h>
+#include <sound/driver.h>
+#include <linux/init.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>
+#include <sound/info.h>
+#include <asm/hardware.h>
+#include <asm/gsc.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Laurent Canet <canetl@esiee.fr>");
+MODULE_DESCRIPTION("ALSA Harmony sound driver");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ALSA,Harmony soundcard}}");
+
+#undef DEBUG
+#ifdef DEBUG
+# define DPRINTK printk 
+#else
+# define DPRINTK(x,...)
+#endif
+
+#define PFX	"harmony: "
+
+#define MAX_PCM_DEVICES		1
+#define MAX_PCM_SUBSTREAMS	4
+#define MAX_MIDI_DEVICES	0
+
+#define BUFFER_SIZE			4096
+#define MAX_BUFS			10
+
+/* number of silence & graveyard buffers */
+#define GRAVEYARD_BUFS		3
+#define SILENCE_BUFS		3
+
+#define MAX_BUFFER_SIZE		(MAX_BUFS * BUFFER_SIZE)
+#define HARMONY_BUF_SIZE	BUFFER_SIZE
+
+#define HARMONY_CNTL_C		0x80000000
+
+#define HARMONY_DSTATUS_PN	0x00000200
+#define HARMONY_DSTATUS_RN	0x00000002
+#define HARMONY_DSTATUS_IE	0x80000000
+
+#define HARMONY_DF_16BIT_LINEAR	0x00000000
+#define HARMONY_DF_8BIT_ULAW	0x00000001
+#define HARMONY_DF_8BIT_ALAW	0x00000002
+
+#define HARMONY_SS_MONO		0x00000000
+#define HARMONY_SS_STEREO	0x00000001
+
+/*
+ * Channels Mask in mixer register
+ * try some "reasonable" default gain values
+ */
+
+#define HARMONY_GAIN_TOTAL_SILENCE 0x00F00FFF
+
+/* the following should be enough (mixer is 
+ * very sensible on harmony)
+ */
+#define HARMONY_GAIN_DEFAULT       0x0F2FF082
+
+
+/* useless since only one card is supported ATM */
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+
+/* Register offset (from base hpa) */
+#define REG_ID		0x00
+#define REG_RESET	0x04
+#define REG_CNTL	0x08
+#define REG_GAINCTL	0x0C
+#define REG_PNXTADD	0x10
+#define REG_PCURADD	0x14
+#define REG_RNXTADD	0x18
+#define REG_RCURADD	0x1C
+#define REG_DSTATUS	0x20
+#define REG_OV		0x24
+#define REG_PIO		0x28
+#define REG_DIAG	0x3C
+
+/*
+ * main harmony structure
+ */
+
+typedef struct snd_card_harmony {
+
+	/* spinlocks (To be done) */
+	spinlock_t mixer_lock;
+	spinlock_t control_lock;
+
+	/* parameters */	
+	int irq;
+	unsigned long hpa;
+	int id;
+	int rev;
+	
+	u32 current_gain;
+	int data_format;		/* HARMONY_DF_xx_BIT_xxx */
+	int sample_rate;		/* HARMONY_SR_xx_KHZ */
+	int stereo_select;	/* HARMONY_SS_MONO or HARMONY_SS_STEREO */
+	int format_initialized;
+	
+	unsigned long ply_buffer;
+	int ply_buf;
+	int ply_count;
+	int ply_size;
+	int ply_stopped;
+	int ply_total;
+	
+	unsigned long cap_buffer;
+	int cap_buf;
+	int cap_count;
+	int cap_size;
+	int cap_stopped;
+	int cap_total;
+
+	struct pci_dev *fake_pci_dev; /* The fake pci_dev needed for 
+					pci_* functions under ccio. */
+
+	/* the graveyard buffer is used as recording buffer when playback, 
+	 * because harmony always want a buffer to put recorded data */
+
+	unsigned char *graveyard_addr;
+	dma_addr_t graveyard_dma;
+	int graveyard_count;
+	
+	/* same thing for silence buffer */
+	unsigned char *silence_addr;
+	dma_addr_t silence_dma;
+	int silence_count;
+
+	/* alsa stuff */
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	snd_info_entry_t *proc_entry;
+} snd_card_harmony_t;
+#define chip_t snd_card_harmony_t
+
+static snd_card_t *snd_harmony_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+/* wait to be out of control mode */
+static inline void snd_harmony_wait_cntl(snd_card_harmony_t *harmony)
+{
+	int timeout = 5000;
+
+	while ( (gsc_readl(harmony->hpa+REG_CNTL) & HARMONY_CNTL_C) && --timeout)
+	{
+		/* Wait */ ;	
+	}
+	if (timeout == 0) DPRINTK(KERN_DEBUG PFX "Error: wait cntl timeouted\n");
+}
+
+
+/*
+ * sample rate routines 
+ */
+static unsigned int snd_card_harmony_rates[] = {
+	5125, 6615, 8000, 9600,
+	11025, 16000, 18900, 22050,
+	27428, 32000, 33075, 37800,
+	44100, 48000
+};
+
+#define RATES sizeof(snd_card_harmony_rates) / sizeof(snd_card_harmony_rates[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraint_rates = {
+	.count = RATES,
+	.list = snd_card_harmony_rates,
+	.mask = 0,
+};
+
+#define HARMONY_SR_8KHZ		0x08
+#define HARMONY_SR_16KHZ	0x09
+#define HARMONY_SR_27KHZ	0x0A
+#define HARMONY_SR_32KHZ	0x0B
+#define HARMONY_SR_48KHZ	0x0E
+#define HARMONY_SR_9KHZ		0x0F
+#define HARMONY_SR_5KHZ		0x10
+#define HARMONY_SR_11KHZ	0x11
+#define HARMONY_SR_18KHZ	0x12
+#define HARMONY_SR_22KHZ	0x13
+#define HARMONY_SR_37KHZ	0x14
+#define HARMONY_SR_44KHZ	0x15
+#define HARMONY_SR_33KHZ	0x16
+#define HARMONY_SR_6KHZ		0x17
+
+/* snd_card_harmony_rate_bits
+ * @rate:	index of current data rate in list
+ * returns: harmony hex code for registers
+ */
+static unsigned int snd_card_harmony_rate_bits(int rate)
+{
+	unsigned int idx;
+	
+	for (idx = 0; idx <= RATES; idx++)
+		if (snd_card_harmony_rates[idx] == rate) break;
+	
+	switch (idx) {
+		case 0: return HARMONY_SR_5KHZ;
+		case 1: return HARMONY_SR_6KHZ;
+		case 2: return HARMONY_SR_8KHZ;
+		case 3: return HARMONY_SR_9KHZ;
+		case 4: return HARMONY_SR_11KHZ;
+		case 5: return HARMONY_SR_16KHZ;
+		case 6: return HARMONY_SR_18KHZ;
+		case 7: return HARMONY_SR_22KHZ;
+		case 8: return HARMONY_SR_27KHZ;
+		case 9: return HARMONY_SR_32KHZ;
+		case 10: return HARMONY_SR_33KHZ;
+		case 11: return HARMONY_SR_37KHZ;
+		case 12: return HARMONY_SR_44KHZ;
+		case 13: return HARMONY_SR_48KHZ;
+		default:  /* fallback */
+				return HARMONY_SR_44KHZ;
+	}
+}
+
+/*
+ * update controls (data format, sample rate, number of channels)
+ * according to value supplied in data structure
+ */
+void snd_harmony_update_control(snd_card_harmony_t *harmony) 
+{
+	u32 default_cntl;
+	
+	/* Set CNTL */
+	default_cntl = (HARMONY_CNTL_C |  	/* The C bit */
+		(harmony->data_format << 6) |	/* Set the data format */
+		(harmony->stereo_select << 5) |	/* Stereo select */
+		(harmony->sample_rate));		/* Set sample rate */
+	
+	/* initialize CNTL */
+ 	snd_harmony_wait_cntl(harmony);
+	
+	gsc_writel(default_cntl, harmony->hpa+REG_CNTL);
+	
+}
+
+/*
+ * silence a buffer
+ * XXX: alsa could probably do this by itself
+ * XXX: memset hpmc, commented.
+ */
+
+void snd_harmony_silence(snd_card_harmony_t *harmony,
+		void *addr, int length)
+{
+	u8 silence_char;
+	
+	switch(harmony->data_format) {
+			case HARMONY_DF_8BIT_ULAW: silence_char = 0x55; break;
+			case HARMONY_DF_8BIT_ALAW: silence_char = 0xff; break;
+			case HARMONY_DF_16BIT_LINEAR:
+			default:
+									   silence_char = 0;
+	}
+	//memset(addr, silence_char, length);
+}
+
+/*
+ * interruption controls routines
+ */
+
+static void snd_harmony_disable_interrupts(snd_card_harmony_t *chip) 
+{
+ 	snd_harmony_wait_cntl(chip);
+	gsc_writel(0, chip->hpa+REG_DSTATUS); 
+}
+
+static void snd_harmony_enable_interrupts(snd_card_harmony_t *chip) 
+{
+ 	snd_harmony_wait_cntl(chip);
+	gsc_writel(HARMONY_DSTATUS_IE, chip->hpa+REG_DSTATUS); 
+}
+
+/*
+ * interruption routine:
+ * The interrupt routine must provide adresse of next physical pages 
+ * used by harmony
+ */
+void snd_card_harmony_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+	snd_card_harmony_t *harmony = (snd_card_harmony_t *)dev;
+	u32 dstatus = 0;
+	unsigned long hpa = harmony->hpa;
+	
+	/* Turn off interrupts */
+	snd_harmony_disable_interrupts(harmony);
+	
+	/* wait for control to free */
+ 	snd_harmony_wait_cntl(harmony);
+	
+	/* Read dstatus and pcuradd (the current address) */
+	dstatus = gsc_readl(hpa+REG_DSTATUS);
+	
+	/* Check if this is a request to get the next play buffer */
+	if (dstatus & HARMONY_DSTATUS_PN) {
+		if (harmony->playback_substream) {
+			harmony->ply_buf += harmony->ply_count;
+			harmony->ply_buf %= harmony->ply_size;
+		
+			gsc_writel(harmony->ply_buffer + harmony->ply_buf,
+					hpa+REG_PNXTADD);
+		
+			snd_pcm_period_elapsed(harmony->playback_substream);
+			harmony->ply_total++;
+		} else {
+			gsc_writel(harmony->silence_dma + 
+					(HARMONY_BUF_SIZE*harmony->silence_count),
+					hpa+REG_PNXTADD);
+			harmony->silence_count++;
+			harmony->silence_count %= SILENCE_BUFS;
+		}
+	}
+	
+	/* Check if we're being asked to fill in a recording buffer */
+	if (dstatus & HARMONY_DSTATUS_RN) {
+		if (harmony->capture_substream) {
+			harmony->cap_buf += harmony->cap_count;
+			harmony->cap_buf %= harmony->cap_size;
+		
+			gsc_writel(harmony->cap_buffer + harmony->cap_buf,
+					hpa+REG_RNXTADD);
+		
+			snd_pcm_period_elapsed(harmony->capture_substream);
+			harmony->cap_total++;
+		} else {
+			/* graveyard buffer */
+			gsc_writel(harmony->graveyard_dma +
+						(HARMONY_BUF_SIZE*harmony->graveyard_count),
+						hpa+REG_RNXTADD);
+			harmony->graveyard_count++;
+			harmony->graveyard_count %= GRAVEYARD_BUFS;
+		}
+	}
+	snd_harmony_enable_interrupts(harmony);
+}
+
+/* 
+ * proc entry
+ * this proc file will give some debugging info
+ */
+
+static void snd_harmony_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	snd_card_harmony_t *harmony = (snd_card_harmony_t *)entry->private_data;
+
+	snd_iprintf(buffer, "LASI Harmony driver\nLaurent Canet <canetl@esiee.fr>\n\n");
+	snd_iprintf(buffer, "IRQ %d, hpa %lx, id %d rev %d\n",
+			harmony->irq, harmony->hpa,
+			harmony->id, harmony->rev);
+	snd_iprintf(buffer, "Current gain %lx\n", (unsigned long) harmony->current_gain);
+	snd_iprintf(buffer, "\tsample rate=%d\n", harmony->sample_rate);
+	snd_iprintf(buffer, "\tstereo select=%d\n", harmony->stereo_select);
+	snd_iprintf(buffer, "\tbitperchan=%d\n\n", harmony->data_format);
+	
+	snd_iprintf(buffer, "Play status:\n");
+	snd_iprintf(buffer, "\tstopped %d\n", harmony->ply_stopped);
+	snd_iprintf(buffer, "\tbuffer %lx, count %d\n", harmony->ply_buffer, harmony->ply_count);
+	snd_iprintf(buffer, "\tbuf %d size %d\n\n", harmony->ply_buf, harmony->ply_size);
+	
+	snd_iprintf(buffer, "Capture status:\n");
+	snd_iprintf(buffer, "\tstopped %d\n", harmony->cap_stopped);
+	snd_iprintf(buffer, "\tbuffer %lx, count %d\n", harmony->cap_buffer, harmony->cap_count);
+	snd_iprintf(buffer, "\tbuf %d, size %d\n\n", harmony->cap_buf, harmony->cap_size);
+
+	snd_iprintf(buffer, "Funny stats: total played=%d, recorded=%d\n\n", harmony->ply_total, harmony->cap_total);
+		
+	snd_iprintf(buffer, "Register:\n");
+	snd_iprintf(buffer, "\tgainctl: %lx\n", (unsigned long) gsc_readl(harmony->hpa+REG_GAINCTL));
+	snd_iprintf(buffer, "\tcntl: %lx\n", (unsigned long) gsc_readl(harmony->hpa+REG_CNTL));
+	snd_iprintf(buffer, "\tid: %lx\n", (unsigned long) gsc_readl(harmony->hpa+REG_ID));
+	snd_iprintf(buffer, "\tpcuradd: %lx\n", (unsigned long) gsc_readl(harmony->hpa+REG_PCURADD));
+	snd_iprintf(buffer, "\trcuradd: %lx\n", (unsigned long) gsc_readl(harmony->hpa+REG_RCURADD));
+	snd_iprintf(buffer, "\tpnxtadd: %lx\n", (unsigned long) gsc_readl(harmony->hpa+REG_PNXTADD));
+	snd_iprintf(buffer, "\trnxtadd: %lx\n", (unsigned long) gsc_readl(harmony->hpa+REG_RNXTADD));
+	snd_iprintf(buffer, "\tdstatus: %lx\n", (unsigned long) gsc_readl(harmony->hpa+REG_DSTATUS));
+	snd_iprintf(buffer, "\tov: %lx\n\n", (unsigned long) gsc_readl(harmony->hpa+REG_OV));
+	
+}
+
+static void __devinit snd_harmony_proc_init(snd_card_harmony_t *harmony)
+{
+	snd_info_entry_t *entry;
+	
+	if ((entry = snd_info_create_card_entry(harmony->card, "harmony", harmony->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = harmony;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 2048;	 /* should be enough */
+		entry->c.text.read = snd_harmony_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	harmony->proc_entry = entry;
+}
+
+static void snd_harmony_proc_done(snd_card_harmony_t *harmony)
+{
+	if (harmony->proc_entry) {
+		snd_info_unregister(harmony->proc_entry);
+		harmony->proc_entry = NULL;
+	}
+}
+
+/* 
+ * PCM Stuff
+ */
+
+static int snd_card_harmony_playback_ioctl(snd_pcm_substream_t * substream,
+				         unsigned int cmd,
+				         void *arg)
+{
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_card_harmony_capture_ioctl(snd_pcm_substream_t * substream,
+					unsigned int cmd,
+					void *arg)
+{
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_card_harmony_playback_trigger(snd_pcm_substream_t * substream,
+					   int cmd)
+{
+	snd_card_harmony_t *harmony = snd_pcm_substream_chip(substream);
+	
+	switch (cmd) {
+		case SNDRV_PCM_TRIGGER_STOP:
+			if (harmony->ply_stopped) 
+				return -EBUSY;
+			harmony->ply_stopped = 1;
+			snd_harmony_disable_interrupts(harmony);
+			break;
+		case SNDRV_PCM_TRIGGER_START:
+			if (!harmony->ply_stopped)
+				return -EBUSY;
+			harmony->ply_stopped = 0;
+			/* write the location of the first buffer to play */
+			gsc_writel(harmony->ply_buffer, harmony->hpa+REG_PNXTADD);
+			snd_harmony_enable_interrupts(harmony);
+			break;
+		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		case SNDRV_PCM_TRIGGER_SUSPEND:
+			DPRINTK(KERN_INFO PFX "received unimplemented trigger: %d\n", cmd);
+		default:
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static int snd_card_harmony_capture_trigger(snd_pcm_substream_t * substream,
+					  int cmd)
+{
+	snd_card_harmony_t *harmony = snd_pcm_substream_chip(substream);
+	
+	switch (cmd) {
+		case SNDRV_PCM_TRIGGER_STOP:
+			if (harmony->cap_stopped) 
+				return -EBUSY;
+			harmony->cap_stopped = 1;;
+			snd_harmony_disable_interrupts(harmony);
+			break;
+		case SNDRV_PCM_TRIGGER_START:
+			if (!harmony->cap_stopped)
+				return -EBUSY;
+			harmony->cap_stopped = 0;
+			snd_harmony_enable_interrupts(harmony);
+			break;
+		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		case SNDRV_PCM_TRIGGER_SUSPEND:
+			DPRINTK(KERN_INFO PFX "Received unimplemented trigger: %d\n", cmd);
+		default:
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static int snd_card_harmony_playback_prepare(snd_pcm_substream_t * substream)
+{
+	snd_card_harmony_t *harmony = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	
+	harmony->ply_size 			= snd_pcm_lib_buffer_bytes(substream);
+	harmony->ply_count 			= snd_pcm_lib_period_bytes(substream);
+	harmony->ply_buf			= 0;
+	harmony->ply_stopped		= 1;
+	
+	/* initialize given sample rate */
+	harmony->sample_rate = snd_card_harmony_rate_bits(runtime->rate);
+
+	/* data format */
+	if (snd_pcm_format_width(runtime->format) == 16) harmony->data_format = HARMONY_DF_16BIT_LINEAR;
+	else harmony->data_format = HARMONY_DF_8BIT_ULAW;
+	
+	/* number of channels */
+	if (runtime->channels == 2) harmony->stereo_select = HARMONY_SS_STEREO;
+	else harmony->stereo_select = HARMONY_SS_MONO;
+	
+	DPRINTK(KERN_INFO PFX "Playback_prepare, sr=%d(%x), df=%x, ss=%x hpa=%lx\n", runtime->rate,
+				harmony->sample_rate, harmony->data_format, harmony->stereo_select, harmony->hpa);
+	snd_harmony_update_control(harmony);
+	harmony->format_initialized = 1;
+	harmony->ply_buffer = runtime->dma_addr;
+	
+	return 0;
+}
+
+static int snd_card_harmony_capture_prepare(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_card_harmony_t *harmony = snd_pcm_substream_chip(substream);
+	
+	harmony->cap_size 			= snd_pcm_lib_buffer_bytes(substream);
+	harmony->cap_count 			= snd_pcm_lib_period_bytes(substream);
+	harmony->cap_count			= 0;
+	harmony->cap_stopped		= 1;
+
+	/* initialize given sample rate */
+	harmony->sample_rate = snd_card_harmony_rate_bits(runtime->rate);
+	
+	/* data format */
+	if (snd_pcm_format_width(runtime->format) == 16) harmony->data_format = HARMONY_DF_16BIT_LINEAR;
+	else harmony->data_format = HARMONY_DF_8BIT_ULAW;
+	
+	/* number of channels */
+	if (runtime->channels == 1) harmony->stereo_select = HARMONY_SS_MONO;
+	else if (runtime->channels == 2) harmony->stereo_select = HARMONY_SS_STEREO;
+		
+	snd_harmony_update_control(harmony);
+	harmony->format_initialized = 1;
+	
+	harmony->cap_buffer = runtime->dma_addr;
+
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_card_harmony_capture_pointer(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_card_harmony_t *harmony = snd_pcm_substream_chip(substream);
+	unsigned long rcuradd;
+	int recorded;
+	
+	if (harmony->cap_stopped) return 0;
+	if (harmony->capture_substream == NULL) return 0;
+
+	rcuradd = gsc_readl(harmony->hpa+REG_RCURADD);
+	recorded = (rcuradd - harmony->cap_buffer);
+	recorded %= harmony->cap_size;
+		
+	return bytes_to_frames(runtime, recorded);
+}
+
+/*
+ */
+
+static snd_pcm_uframes_t snd_card_harmony_playback_pointer(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_card_harmony_t *harmony = snd_pcm_substream_chip(substream);
+	int played;
+	long int pcuradd = gsc_readl(harmony->hpa+REG_PCURADD);
+	
+	if ((harmony->ply_stopped) || (harmony->playback_substream == NULL)) return 0;
+	if ((harmony->ply_buffer == 0) || (harmony->ply_size == 0)) return 0;
+	
+	played = (pcuradd - harmony->ply_buffer);
+	
+	printk(KERN_DEBUG PFX "Pointer is %lx-%lx = %d\n", pcuradd, harmony->ply_buffer, played);	
+
+	if (pcuradd > harmony->ply_buffer + harmony->ply_size) return 0;
+	
+	return bytes_to_frames(runtime, played);
+}
+
+static snd_pcm_hardware_t snd_card_harmony_playback =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 
+					SNDRV_PCM_INFO_JOINT_DUPLEX | 
+					SNDRV_PCM_INFO_MMAP_VALID |
+					SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_BE | 
+					SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_MU_LAW),
+	.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 =	MAX_BUFFER_SIZE,
+	.period_bytes_min =	HARMONY_BUF_SIZE,
+	.period_bytes_max =	HARMONY_BUF_SIZE,
+	.periods_min =		1,
+	.periods_max =		MAX_BUFS,
+	.fifo_size =		0,
+};
+
+static snd_pcm_hardware_t snd_card_harmony_capture =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 
+					SNDRV_PCM_INFO_JOINT_DUPLEX | 
+					SNDRV_PCM_INFO_MMAP_VALID |
+					SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_BE | 
+					SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_MU_LAW),
+	.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 =	MAX_BUFFER_SIZE,
+	.period_bytes_min =	HARMONY_BUF_SIZE,
+	.period_bytes_max =	HARMONY_BUF_SIZE,
+	.periods_min =		1,
+	.periods_max =		MAX_BUFS,
+	.fifo_size =		0,
+};
+
+static int snd_card_harmony_playback_open(snd_pcm_substream_t * substream)
+{
+	snd_card_harmony_t *harmony = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+	
+	/*
+	 * harmony is not "real" pci, but we need a pci_dev
+	 * to alloc PCI DMA pages
+	 */
+	substream->dma_private = harmony->fake_pci_dev;
+	substream->dma_type = SNDRV_PCM_DMA_TYPE_PCI;
+	
+	harmony->playback_substream = substream;
+	runtime->hw = snd_card_harmony_playback;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates);
+	
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	
+	return 0;
+}
+
+static int snd_card_harmony_capture_open(snd_pcm_substream_t * substream)
+{
+	snd_card_harmony_t *harmony = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+	
+	
+	/*
+	 * harmony is not "real" pci, but we need a pci_dev
+	 * to alloc PCI DMA pages
+	 */
+	substream->dma_private = harmony->fake_pci_dev;
+	substream->dma_type = SNDRV_PCM_DMA_TYPE_PCI;
+
+	harmony->capture_substream = substream;
+	runtime->hw = snd_card_harmony_capture;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates);
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	return 0;
+
+}
+
+static int snd_card_harmony_playback_close(snd_pcm_substream_t * substream)
+{
+	snd_card_harmony_t *harmony = snd_pcm_substream_chip(substream);
+	snd_pcm_lib_free_pages(substream);
+	
+	harmony->playback_substream = NULL;
+	harmony->ply_size 			= 0;
+	harmony->ply_buf			= 0;
+	harmony->ply_buffer			= 0;
+	harmony->ply_count			= 0;
+	harmony->ply_stopped		= 1;
+	harmony->format_initialized = 0;
+	
+	return 0;
+}
+
+static int snd_card_harmony_capture_close(snd_pcm_substream_t * substream)
+{
+	snd_card_harmony_t *harmony = snd_pcm_substream_chip(substream);
+	
+	snd_pcm_lib_free_pages(substream);
+	
+	harmony->capture_substream = NULL;
+	harmony->cap_size 			= 0;
+	harmony->cap_buf			= 0;
+	harmony->cap_buffer			= 0;
+	harmony->cap_count			= 0;
+	harmony->cap_stopped		= 1;
+	harmony->format_initialized = 0;
+	
+	return 0;
+}
+
+static int snd_card_harmony_hw_params(snd_pcm_substream_t *substream, 
+	                   snd_pcm_hw_params_t * hw_params)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+	
+	err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+	DPRINTK(KERN_INFO PFX "HW Params returned %d, dma_addr %lx\n", err,
+			(unsigned long)runtime->dma_addr);
+	return err;
+}
+
+static int snd_card_harmony_hw_free(snd_pcm_substream_t *substream) 
+{
+	snd_pcm_lib_free_pages(substream);		
+	return 0;
+}
+
+static snd_pcm_ops_t snd_card_harmony_playback_ops = {
+	.open =			snd_card_harmony_playback_open,
+	.close =		snd_card_harmony_playback_close,
+	.ioctl =		snd_card_harmony_playback_ioctl,
+	.hw_params = 	snd_card_harmony_hw_params,
+	.hw_free = 		snd_card_harmony_hw_free,
+	.prepare =		snd_card_harmony_playback_prepare,
+	.trigger =		snd_card_harmony_playback_trigger,
+ 	.pointer =		snd_card_harmony_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_card_harmony_capture_ops = {
+	.open =			snd_card_harmony_capture_open,
+	.close =		snd_card_harmony_capture_close,
+	.ioctl =		snd_card_harmony_capture_ioctl,
+	.hw_params = 	snd_card_harmony_hw_params,
+	.hw_free = 		snd_card_harmony_hw_free,
+	.prepare =		snd_card_harmony_capture_prepare,
+	.trigger =		snd_card_harmony_capture_trigger,
+	.pointer =		snd_card_harmony_capture_pointer,
+};
+
+static int snd_card_harmony_pcm_init(snd_card_harmony_t *harmony, int device)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	/* Request that IRQ */
+	if (request_irq(harmony->irq, snd_card_harmony_interrupt, 0 ,"harmony", harmony)) {
+		printk(KERN_ERR PFX "Error requesting irq %d.\n", harmony->irq);
+		return -EFAULT;
+	}
+	
+	snd_harmony_disable_interrupts(harmony);
+	
+   	if ((err = snd_pcm_new(harmony->card, "Harmony", device, 1, 1, &pcm)) < 0)
+		return err;
+	
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_harmony_playback_ops);
+ 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_harmony_capture_ops); 
+	
+	pcm->private_data = harmony;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "Harmony");
+	harmony->pcm = pcm;
+	
+	/* initialize graveyard buffer */
+	harmony->graveyard_addr = snd_malloc_pci_pages(harmony->fake_pci_dev, 
+			HARMONY_BUF_SIZE*GRAVEYARD_BUFS, &harmony->graveyard_dma);
+	harmony->graveyard_count = 0;
+	
+	/* initialize silence buffers */
+	harmony->silence_addr = snd_malloc_pci_pages(harmony->fake_pci_dev,
+			HARMONY_BUF_SIZE*SILENCE_BUFS, &harmony->silence_dma);
+	harmony->silence_count = 0;
+
+
+	harmony->ply_stopped = harmony->cap_stopped = 1;
+	
+	harmony->playback_substream = NULL;
+	harmony->capture_substream = NULL;
+	harmony->graveyard_count = 0;
+	
+	return 0;
+}
+
+/*
+ * mixer routines
+ */
+
+static void snd_harmony_set_new_gain(snd_card_harmony_t *harmony)
+{
+	DPRINTK(KERN_INFO PFX "Setting new gain %x at %lx\n", harmony->current_gain, harmony->hpa+REG_GAINCTL);
+	/* Wait until we're out of control mode */
+ 	snd_harmony_wait_cntl(harmony);
+	
+	gsc_writel(harmony->current_gain, harmony->hpa+REG_GAINCTL);
+}
+
+#define HARMONY_VOLUME(xname, left_shift, right_shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+  .info = snd_harmony_mixercontrol_info, \
+  .get = snd_harmony_volume_get, .put = snd_harmony_volume_put, \
+  .private_value = ((left_shift) | ((right_shift) << 8) | ((mask) << 16) | ((invert) << 24)) }
+
+static int snd_harmony_mixercontrol_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int left_shift = (kcontrol->private_value) & 0xff;
+	int right_shift = (kcontrol->private_value >> 8) & 0xff;
+	
+	uinfo->type = (mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER);
+	uinfo->count = (left_shift == right_shift) ? 1 : 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+ 
+static int snd_harmony_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_card_harmony_t *harmony = _snd_kcontrol_chip(kcontrol);
+	int shift_left = (kcontrol->private_value) & 0xff;
+	int shift_right = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned long flags;
+	int left, right;
+	
+	spin_lock_irqsave(&harmony->mixer_lock, flags);
+	left = (harmony->current_gain >> shift_left) & mask;
+	right = (harmony->current_gain >> shift_right) & mask;
+
+	if (invert) {
+		left = mask - left;
+		right = mask - right;
+	}
+	ucontrol->value.integer.value[0] = left;
+	ucontrol->value.integer.value[1] = right;
+	spin_unlock_irqrestore(&harmony->mixer_lock, flags);
+
+	return 0;
+}  
+
+static int snd_harmony_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_card_harmony_t *harmony = _snd_kcontrol_chip(kcontrol);
+	int shift_left = (kcontrol->private_value) & 0xff;
+	int shift_right = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned long flags;
+	int left, right;
+	int old_gain = harmony->current_gain;
+	
+	left = ucontrol->value.integer.value[0] & mask;
+	right = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		left = mask - left;
+		right = mask - right;
+	}
+	
+	spin_lock_irqsave(&harmony->mixer_lock, flags);
+	harmony->current_gain = harmony->current_gain & ~( (mask << shift_right) | (mask << shift_left));
+ 	harmony->current_gain = harmony->current_gain | ((left << shift_left) | (right << shift_right) );
+	snd_harmony_set_new_gain(harmony);
+	spin_unlock_irqrestore(&harmony->mixer_lock, flags);
+	
+	return (old_gain - harmony->current_gain);
+}
+
+#define HARMONY_CONTROLS (sizeof(snd_harmony_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_harmony_controls[] = {
+HARMONY_VOLUME("PCM Capture Volume", 12, 16, 0x0f, 0),
+HARMONY_VOLUME("Master Volume", 20, 20, 0x0f, 1),
+HARMONY_VOLUME("PCM Playback Volume", 6, 0, 0x3f, 1),
+};
+
+static void snd_harmony_reset_codec(snd_card_harmony_t *harmony)
+{
+ 	snd_harmony_wait_cntl(harmony);
+	gsc_writel(1, harmony->hpa+REG_RESET);
+	mdelay(50);		/* wait 50 ms */
+	gsc_writel(0, harmony->hpa+REG_RESET);
+}
+
+/*
+ * Mute all the output and reset Harmony.
+ */
+
+static void __init snd_harmony_mixer_reset(snd_card_harmony_t *harmony)
+{
+	harmony->current_gain = HARMONY_GAIN_TOTAL_SILENCE;
+	snd_harmony_set_new_gain(harmony);
+	snd_harmony_reset_codec(harmony);
+	harmony->current_gain = HARMONY_GAIN_DEFAULT;
+	snd_harmony_set_new_gain(harmony);
+}
+
+
+int __init snd_card_harmony_mixer_init(snd_card_harmony_t *harmony)
+{
+	snd_card_t *card = harmony->card;
+	int idx, err;
+
+	snd_assert(harmony != NULL, return -EINVAL);
+	strcpy(card->mixername, "Harmony Gain control interface");
+
+	for (idx = 0; idx < HARMONY_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_harmony_controls[idx], harmony))) < 0)
+			return err;
+	}
+	
+	snd_harmony_mixer_reset(harmony);
+
+	return 0;
+}
+
+static int snd_card_harmony_create(snd_card_t *card, struct parisc_device *pa_dev, snd_card_harmony_t *harmony)
+{
+	u32	cntl;
+	
+	harmony->card = card;
+	
+	/* Set the HPA of harmony */
+	harmony->hpa = pa_dev->hpa;
+	
+
+	harmony->irq = pa_dev->irq;
+	if (!harmony->irq) {
+		printk(KERN_ERR PFX "no irq found\n");
+		return -ENODEV;
+	}
+
+	/* Grab the ID and revision from the device */
+	harmony->id = (gsc_readl(harmony->hpa+REG_ID)&0x00ff0000) >> 16;
+	if ((harmony->id | 1) != 0x15) {
+		printk(KERN_WARNING PFX "wrong harmony id 0x%02x\n", harmony->id);
+		return -EBUSY;
+	}
+	cntl = gsc_readl(harmony->hpa+REG_CNTL);
+	harmony->rev = (cntl>>20) & 0xff;
+
+	printk(KERN_INFO "Lasi Harmony Audio driver h/w id %i, rev. %i at 0x%lx, IRQ %i\n",	harmony->id, harmony->rev, pa_dev->hpa, harmony->irq);
+	
+	/* Make sure the control bit isn't set, although I don't think it 
+	   ever is. */
+	if (cntl & HARMONY_CNTL_C) {
+		printk(KERN_WARNING PFX "CNTL busy\n");
+		harmony->hpa = 0;
+		return -EBUSY;
+	}
+	
+	/* a fake pci_dev is needed for pci_* functions under ccio */
+	harmony->fake_pci_dev = ccio_get_fake(pa_dev);
+	return 0;
+}
+	
+static int __init snd_card_harmony_probe(struct parisc_device *pa_dev)
+{
+	static int dev;
+	snd_card_harmony_t *chip;
+	snd_card_t *card;
+	int err;
+	
+    if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+	
+	snd_harmony_cards[dev] = snd_card_new(index[dev], id[dev], THIS_MODULE,
+			    sizeof(snd_card_harmony_t));
+	card = snd_harmony_cards[dev];
+				
+	if (card == NULL)
+		return -ENOMEM;
+	chip = (struct snd_card_harmony *)card->private_data;
+	
+	if ((err = snd_card_harmony_create(card, pa_dev, chip)) < 0) {
+		printk(KERN_ERR PFX "Creation failed\n");
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_card_harmony_pcm_init(chip, dev)) < 0) {
+		printk(KERN_ERR PFX "PCM Init failed\n");
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_card_harmony_mixer_init(chip)) < 0) {
+		printk(KERN_ERR PFX "Mixer init failed\n");
+		snd_card_free(card);
+		return err;
+	}
+	
+	snd_harmony_proc_init(chip);
+	
+	strcpy(card->driver, "Harmony");
+	strcpy(card->shortname, "ALSA driver for LASI Harmony");
+	sprintf(card->longname, "%s at h/w, id %i, rev. %i hpa 0x%lx, IRQ %i\n",card->shortname, chip->id, chip->rev, pa_dev->hpa, chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	printk(KERN_DEBUG PFX "Successfully registered harmony pcm backend & mixer %d\n", dev);
+	dev++;
+	return 0;
+}
+
+static struct parisc_device_id snd_card_harmony_devicetbl[] = {
+ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A }, /* Bushmaster/Flounder */
+ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007B }, /* 712/715 Audio */
+ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007E }, /* Pace Audio */
+ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007F }, /* Outfield / Coral II */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(parisc, snd_card_harmony_devicetbl);
+
+/*
+ * bloc device parisc. c'est une structure qui definit un device
+ * que l'on trouve sur parisc. 
+ * On y trouve les differents numeros HVERSION correspondant au device
+ * en question (ce qui permet a l'inventory de l'identifier) et la fonction
+ * d'initialisation du chose 
+ */
+
+static struct parisc_driver snd_card_harmony_driver = {
+	name:		"Lasi ALSA Harmony",
+	id_table:	snd_card_harmony_devicetbl,
+	probe:		snd_card_harmony_probe,
+};
+
+static int __init alsa_card_harmony_init(void)
+{
+	int err;
+	
+	if ((err = register_parisc_driver(&snd_card_harmony_driver)) < 0) {
+		printk(KERN_ERR "Harmony soundcard not found or device busy\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static void __exit alsa_card_harmony_exit(void)
+{
+	int idx;
+	snd_card_harmony_t *harmony;
+	
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+	{
+		if (snd_harmony_cards[idx] != NULL)
+		{	
+			DPRINTK(KERN_INFO PFX "Freeing card %d\n", idx);
+			harmony = snd_harmony_cards[idx]->private_data;
+			snd_harmony_proc_done(harmony);
+			free_irq(harmony->irq, snd_card_harmony_interrupt);
+			printk(KERN_INFO PFX "Card unloaded %d, irq=%d\n", idx, harmony->irq);
+			snd_card_free(snd_harmony_cards[idx]);
+		}
+	}	
+}
+
+module_init(alsa_card_harmony_init)
+module_exit(alsa_card_harmony_exit)
diff -urN linux-2.5.70-bk6/sound/pci/Kconfig linux-2.5.70-bk7/sound/pci/Kconfig
--- linux-2.5.70-bk6/sound/pci/Kconfig	2003-05-26 18:00:37.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/Kconfig	2003-06-02 04:39:12.000000000 -0700
@@ -177,5 +177,11 @@
 	help
 	  Say 'Y' or 'M' to include support for VIA VT82C686A/B, VT8233 South Bridge.
 
+config SND_VX222
+	tristate "Digigram VX222"
+	depends on SND
+	help
+	  Say 'Y' or 'M' to include support for Digigram VX222 soundcards
+
 endmenu
 
diff -urN linux-2.5.70-bk6/sound/pci/Makefile linux-2.5.70-bk7/sound/pci/Makefile
--- linux-2.5.70-bk6/sound/pci/Makefile	2003-05-26 18:00:20.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -34,4 +34,4 @@
 obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o
 obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o
 
-obj-$(CONFIG_SND) += ac97/ ali5451/ cs46xx/ emu10k1/ korg1212/ nm256/ rme9652/ trident/ ymfpci/ ice1712/
+obj-$(CONFIG_SND) += ac97/ ali5451/ cs46xx/ emu10k1/ korg1212/ nm256/ rme9652/ trident/ ymfpci/ ice1712/ vx222/
diff -urN linux-2.5.70-bk6/sound/pci/ac97/Makefile linux-2.5.70-bk7/sound/pci/ac97/Makefile
--- linux-2.5.70-bk6/sound/pci/ac97/Makefile	2003-05-26 18:00:41.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ac97/Makefile	2003-06-02 04:39:12.000000000 -0700
@@ -13,6 +13,7 @@
 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_ICE1724) += snd-ac97-codec.o
 obj-$(CONFIG_SND_INTEL8X0) += snd-ac97-codec.o
 obj-$(CONFIG_SND_MAESTRO3) += snd-ac97-codec.o
 obj-$(CONFIG_SND_VIA82XX) += snd-ac97-codec.o
diff -urN linux-2.5.70-bk6/sound/pci/ac97/ac97_codec.c linux-2.5.70-bk7/sound/pci/ac97/ac97_codec.c
--- linux-2.5.70-bk6/sound/pci/ac97/ac97_codec.c	2003-05-26 18:00:38.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ac97/ac97_codec.c	2003-06-02 04:39:12.000000000 -0700
@@ -89,7 +89,6 @@
 };
 
 static const ac97_codec_id_t snd_ac97_codec_ids[] = {
-{ 0x014b0502, 0xffffffff, "NM256AV",		NULL,		NULL }, // FIXME: which real one?
 { 0x414b4d00, 0xffffffff, "AK4540",		NULL,		NULL },
 { 0x414b4d01, 0xffffffff, "AK4542",		NULL,		NULL },
 { 0x414b4d02, 0xffffffff, "AK4543",		NULL,		NULL },
@@ -104,6 +103,7 @@
 { 0x41445363, 0xffffffff, "AD1886A",		patch_ad1881,	NULL },
 { 0x41445370, 0xffffffff, "AD1980",		patch_ad1980,	NULL },
 { 0x41445372, 0xffffffff, "AD1981A",		patch_ad1881,	NULL },
+{ 0x41445375, 0xffffffff, "AD1985",		patch_ad1980,	NULL },
 { 0x414c4300, 0xfffffff0, "RL5306",	 	NULL,		NULL },
 { 0x414c4310, 0xfffffff0, "RL5382", 		NULL,		NULL },
 { 0x414c4320, 0xfffffff0, "RL5383", 		NULL,		NULL },
@@ -143,10 +143,12 @@
 { 0x54584e20, 0xffffffff, "TLC320AD9xC",	NULL,		NULL },
 { 0x56494161, 0xffffffff, "VIA1612A",		NULL,		NULL }, // modified ICE1232 with S/PDIF
 { 0x57454301, 0xffffffff, "W83971D",		NULL,		NULL },
-{ 0x574d4c00, 0xffffffff, "WM9701A",		patch_wolfson00,NULL },
-{ 0x574d4c03, 0xffffffff, "WM9703/9707",	patch_wolfson03,NULL },
-{ 0x574d4c04, 0xffffffff, "WM9704/quad",	patch_wolfson04,NULL },
-{ 0x574d4c05, 0xffffffff, "WM9705",		NULL,		NULL },	// patch?
+{ 0x574d4c00, 0xffffffff, "WM9701A",		NULL,		NULL },
+{ 0x574d4C03, 0xffffffff, "WM9703/WM9707/WM9708/WM9717", patch_wolfson03, NULL},
+{ 0x574d4C04, 0xffffffff, "WM9704M/WM9704Q",	patch_wolfson04, NULL},
+{ 0x574d4C05, 0xffffffff, "WM9705/WM9710",	patch_wolfson05, NULL},
+{ 0x574d4C09, 0xffffffff, "WM9709",		NULL,		NULL},
+{ 0x574d4C12, 0xffffffff, "WM9711/WM9712",	patch_wolfson11, NULL},
 { 0x594d4800, 0xffffffff, "YMF743",		NULL,		NULL },
 { 0x594d4802, 0xffffffff, "YMF752",		NULL,		NULL },
 { 0x594d4803, 0xffffffff, "YMF753",		patch_yamaha_ymf753,	NULL },
@@ -1078,7 +1080,6 @@
 	/* 7: Independent Master Volume Left */
 	/* 8: reserved */
 	AC97_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0),
-	AC97_SINGLE("Mic As Center/LFE", AC97_ALC650_MULTICH, 10, 1, 0),
 	AC97_SINGLE("Swap Surround Slot", AC97_ALC650_MULTICH, 14, 1, 0),
 #if 0 /* always set in patch_alc650 */
 	AC97_SINGLE("IEC958 Input Clock Enable", AC97_ALC650_CLOCK, 0, 1, 0),
@@ -1090,6 +1091,43 @@
 #endif
 };
 
+static const snd_kcontrol_new_t snd_ac97_control_alc650_mic =
+AC97_SINGLE("Mic As Center/LFE", AC97_ALC650_MULTICH, 10, 1, 0);
+
+
+static int snd_ac97_alc650_mic_gpio_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1;
+	return 0;
+}
+
+static int snd_ac97_alc650_mic_gpio_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	int change;
+	change = snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10,
+				      ucontrol->value.integer.value[0] ? (1 << 10) : 0);
+	if (change) {
+		/* GPIO0 write for mic */
+		snd_ac97_update_bits(ac97, 0x76, 0x01,
+				     ucontrol->value.integer.value[0] ? 0 : 0x01);
+		/* GPIO0 high for mic */
+		snd_ac97_update_bits(ac97, 0x78, 0x100,
+				     ucontrol->value.integer.value[0] ? 0 : 0x100);
+	}
+	return change;
+}
+
+static const snd_kcontrol_new_t snd_ac97_control_alc650_mic_gpio = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Mic As Center/LFE",
+	.info = snd_ac97_info_single,
+	.get = snd_ac97_alc650_mic_gpio_get,
+	.put = snd_ac97_alc650_mic_gpio_put,
+	.private_value = (1 << 16), /* for info */
+};
+
 static const snd_kcontrol_new_t snd_ac97_spdif_controls_alc650[] = {
 	AC97_SINGLE("IEC958 Capture Switch", AC97_ALC650_MULTICH, 11, 1, 0),
 	AC97_SINGLE("Analog to IEC958 Output", AC97_ALC650_MULTICH, 12, 1, 0),
@@ -1829,9 +1867,14 @@
 				return err;
 		break;
 	case AC97_ID_ALC650:
+		/* detect ALC650 rev.E of later */
 		for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_alc650); idx++)
 			if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_alc650[idx], ac97))) < 0)
 				return err;
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(ac97->spec.dev_flags ?
+							   &snd_ac97_control_alc650_mic :
+							   &snd_ac97_control_alc650_mic_gpio, ac97))) < 0)
+			return err;
 		if (ac97->ext_id & AC97_EI_SPDIF) {
 			for (idx = 0; idx < ARRAY_SIZE(snd_ac97_spdif_controls_alc650); idx++)
 				if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_spdif_controls_alc650[idx], ac97))) < 0)
@@ -2024,7 +2067,7 @@
 	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);
+	ac97 = snd_magic_kmalloc(ac97_t, 0, GFP_KERNEL);
 	if (ac97 == NULL)
 		return -ENOMEM;
 	*ac97 = *_ac97;
@@ -2058,10 +2101,13 @@
 	}
 	
 	/* test for AC'97 */
-	/* test if we can write to the record gain volume register */
-	snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a06);
-	if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a06) {
-		ac97->scaps |= AC97_SCAP_AUDIO;
+	if (! (ac97->scaps & AC97_SCAP_AUDIO)) {
+		/* test if we can write to the record gain volume register */
+		snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a06);
+		if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a06)
+			ac97->scaps |= AC97_SCAP_AUDIO;
+	}
+	if (ac97->scaps & AC97_SCAP_AUDIO) {
 		ac97->caps = snd_ac97_read(ac97, AC97_RESET);
 		ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID);
 		if (ac97->ext_id == 0xffff)	/* invalid combination */
@@ -2862,16 +2908,36 @@
 	return snd_ctl_remove_id(ac97->card, &id);
 }
 
-static int rename_ctl(ac97_t *ac97, const char *src, const char *dst)
+static snd_kcontrol_t *ctl_find(ac97_t *ac97, const char *name)
 {
-	snd_ctl_elem_id_t sid, did;
+	snd_ctl_elem_id_t sid;
 	memset(&sid, 0, sizeof(sid));
-	strcpy(sid.name, src);
+	strcpy(sid.name, name);
 	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-	memset(&did, 0, sizeof(did));
-	strcpy(did.name, dst);
-	did.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-	return snd_ctl_rename_id(ac97->card, &sid, &did);
+	return snd_ctl_find_id(ac97->card, &sid);
+}
+
+static int rename_ctl(ac97_t *ac97, const char *src, const char *dst)
+{
+	snd_kcontrol_t *kctl = ctl_find(ac97, src);
+	if (kctl) {
+		strcpy(kctl->id.name, dst);
+		return 0;
+	}
+	return -ENOENT;
+}
+
+static int swap_ctl(ac97_t *ac97, const char *s1, const char *s2)
+{
+	snd_kcontrol_t *kctl1, *kctl2;
+	kctl1 = ctl_find(ac97, s1);
+	kctl2 = ctl_find(ac97, s2);
+	if (kctl1 && kctl2) {
+		strcpy(kctl1->id.name, s2);
+		strcpy(kctl2->id.name, s1);
+		return 0;
+	}
+	return -ENOENT;
 }
 
 static int swap_headphone(ac97_t *ac97, int remove_master)
@@ -2889,6 +2955,14 @@
 	return 0;
 }
 
+static int swap_surround(ac97_t *ac97)
+{
+	/* FIXME: error checks.. */
+	swap_ctl(ac97, "Master Playback Switch", "Surround Playback Switch");
+	swap_ctl(ac97, "Master Playback Volume", "Surround Playback Volume");
+	return 0;
+}
+
 
 /**
  * snd_ac97_tune_hardware - tune up the hardware
@@ -2914,6 +2988,8 @@
 				return swap_headphone(ac97, 1);
 			case AC97_TUNE_SWAP_HP:
 				return swap_headphone(ac97, 0);
+			case AC97_TUNE_SWAP_SURROUND:
+				return swap_surround(ac97);
 			}
 			snd_printk(KERN_ERR "invalid quirk type %d for %s\n", quirk->type, quirk->name);
 			return -EINVAL;
diff -urN linux-2.5.70-bk6/sound/pci/ac97/ac97_patch.c linux-2.5.70-bk7/sound/pci/ac97/ac97_patch.c
--- linux-2.5.70-bk6/sound/pci/ac97/ac97_patch.c	2003-05-26 18:00:58.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ac97/ac97_patch.c	2003-06-02 04:39:12.000000000 -0700
@@ -51,52 +51,60 @@
 	return 0;
 }
 
-int patch_wolfson00(ac97_t * ac97)
+/*
+ * May 2, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ *  removed broken wolfson00 patch.
+ *  added support for WM9705,WM9708,WM9709,WM9710,WM9711,WM9712 and WM9717.
+ */
+
+#define AC97_WM97XX_FMIXER_VOL	0x72
+#define AC97_WM9704_RMIXER_VOL	0x74
+#define AC97_WM9704_TEST	0x5a
+#define AC97_WM9704_RPCM_VOL	0x70
+#define AC97_WM9711_OUT3VOL	0x16
+ 
+int patch_wolfson03(ac97_t * ac97)
 {
-	/* This sequence is suspect because it was designed for
-	   the WM9704, and is known to fail when applied to the
-	   WM9707.  If you're having trouble initializing a
-	   WM9700, this is the place to start looking.
+	/* This is known to work for the ViewSonic ViewPad 1000
 	   Randolph Bentson <bentson@holmsjoen.com> */
 
-	// WM9701A
-	snd_ac97_write_cache(ac97, 0x72, 0x0808);
-	snd_ac97_write_cache(ac97, 0x74, 0x0808);
-
+	// WM9703/9707/9708/9717
+	snd_ac97_write_cache(ac97, AC97_WM97XX_FMIXER_VOL, 0x0808);
+	snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0x8000);
+	return 0;
+}
+  
+int patch_wolfson04(ac97_t * ac97)
+{
+	// WM9704M/9704Q
+	// set front and rear mixer volume
+	snd_ac97_write_cache(ac97, AC97_WM97XX_FMIXER_VOL, 0x0808);
+	snd_ac97_write_cache(ac97, AC97_WM9704_RMIXER_VOL, 0x0808);
+	
 	// patch for DVD noise
-	snd_ac97_write_cache(ac97, 0x5a, 0x0200);
-
+	snd_ac97_write_cache(ac97, AC97_WM9704_TEST, 0x0200);
+ 
 	// init vol
-	snd_ac97_write_cache(ac97, 0x70, 0x0808);
-
+	snd_ac97_write_cache(ac97, AC97_WM9704_RPCM_VOL, 0x0808);
+ 
+	// set rear surround volume
 	snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000);
 	return 0;
 }
-
-int patch_wolfson03(ac97_t * ac97)
+  
+int patch_wolfson05(ac97_t * ac97)
 {
-	/* This is known to work for the ViewSonic ViewPad 1000
-	   Randolph Bentson <bentson@holmsjoen.com> */
-
-	// WM9703/9707
-	snd_ac97_write_cache(ac97, 0x72, 0x0808);
-	snd_ac97_write_cache(ac97, 0x20, 0x8000);
+	// WM9705, WM9710
+	// set front mixer volume
+	snd_ac97_write_cache(ac97, AC97_WM97XX_FMIXER_VOL, 0x0808);
 	return 0;
 }
 
-int patch_wolfson04(ac97_t * ac97)
+int patch_wolfson11(ac97_t * ac97)
 {
-	// WM9704
-	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);
+	// WM9711, WM9712
+	// set out3 volume
+	snd_ac97_write_cache(ac97, AC97_WM9711_OUT3VOL, 0x0808);
 	return 0;
 }
 
@@ -360,10 +368,21 @@
 int patch_alc650(ac97_t * ac97)
 {
 	unsigned short val;
+	int spdif = 0;
+
+	/* FIXME: set the above 1 if we can detect the chip rev.E correctly.
+	 *        this is used for switching mic and center/lfe, which needs
+	 *        resetting GPIO0 level on the older revision.
+	 */
+	ac97->spec.dev_flags = 0;
 
 	/* check spdif */
-	val = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
-	if (val & AC97_EA_SPCV) {
+	if (ac97->spec.dev_flags) {
+		val = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
+		if (val & AC97_EA_SPCV)
+			spdif = 1;
+	}
+	if (spdif) {
 		/* enable spdif in */
 		snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK,
 				     snd_ac97_read(ac97, AC97_ALC650_CLOCK) | 0x03);
@@ -374,6 +393,25 @@
 	val &= ~0xc000; /* slot: 3,4,7,8,6,9 */
 	snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, val);
 
+	if (! ac97->spec.dev_flags) {
+		/* set GPIO */
+		int mic_off;
+		mic_off = snd_ac97_read(ac97, AC97_ALC650_MULTICH) & (1 << 10);
+		/* GPIO0 direction */
+		val = snd_ac97_read(ac97, 0x76);
+		if (mic_off)
+			val &= ~0x01;
+		else
+			val |= 0x01;
+		snd_ac97_write_cache(ac97, 0x76, val);
+		val = snd_ac97_read(ac97, 0x78);
+		if (mic_off)
+			val &= ~0x100;
+		else
+			val = val | 0x100;
+		snd_ac97_write_cache(ac97, 0x78, val);
+	}
+
 	/* full DAC volume */
 	snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808);
 	snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808);
diff -urN linux-2.5.70-bk6/sound/pci/ac97/ac97_patch.h linux-2.5.70-bk7/sound/pci/ac97/ac97_patch.h
--- linux-2.5.70-bk6/sound/pci/ac97/ac97_patch.h	2003-05-26 18:00:20.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ac97/ac97_patch.h	2003-06-02 04:39:12.000000000 -0700
@@ -26,6 +26,8 @@
 int patch_wolfson00(ac97_t * ac97);
 int patch_wolfson03(ac97_t * ac97);
 int patch_wolfson04(ac97_t * ac97);
+int patch_wolfson05(ac97_t * ac97);
+int patch_wolfson11(ac97_t * ac97);
 int patch_tritech_tr28028(ac97_t * ac97);
 int patch_sigmatel_stac9708(ac97_t * ac97);
 int patch_sigmatel_stac9721(ac97_t * ac97);
diff -urN linux-2.5.70-bk6/sound/pci/ali5451/ali5451.c linux-2.5.70-bk7/sound/pci/ali5451/ali5451.c
--- linux-2.5.70-bk6/sound/pci/ali5451/ali5451.c	2003-05-26 18:00:58.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ali5451/ali5451.c	2003-06-02 04:39:12.000000000 -0700
@@ -52,6 +52,7 @@
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
 static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 32};
+static int spdif[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
 
 MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
 MODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio.");
@@ -65,6 +66,9 @@
 MODULE_PARM(pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
 MODULE_PARM_DESC(pcm_channels, "PCM Channels");
 MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",default:32,allows:{{1,32}}");
+MODULE_PARM(spdif, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(spdif, "Support SPDIF I/O");
+MODULE_PARM_SYNTAX(spdif, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC);
 
 /*
  *  Debug part definitions
@@ -125,9 +129,13 @@
 #define ALI_AC97_READ		0x44
 
 #define ALI_SCTRL		0x48
+#define   ALI_SPDIF_OUT_ENABLE		0x20
 #define ALI_AC97_GPIO		0x4c
 #define ALI_SPDIF_CS		0x70
 #define ALI_SPDIF_CTRL		0x74
+#define   ALI_SPDIF_IN_FUNC_ENABLE	0x02
+#define   ALI_SPDIF_IN_CH_STATUS	0x40
+#define   ALI_SPDIF_OUT_CH_STATUS	0xbf
 #define ALI_START		0x80
 #define ALI_STOP		0x84
 #define ALI_CSPF		0x90
@@ -147,6 +155,11 @@
 #define ALI_SBCTRL_SBE2R_SBDD   0xc4
 #define ALI_STIMER		0xc8
 #define ALI_GLOBAL_CONTROL	0xd4
+#define   ALI_SPDIF_OUT_SEL_PCM		0x00000400 /* bit 10 */
+#define   ALI_SPDIF_IN_SUPPORT		0x00000800 /* bit 11 */
+#define   ALI_SPDIF_OUT_CH_ENABLE	0x00008000 /* bit 15 */
+#define   ALI_SPDIF_IN_CH_ENABLE	0x00080000 /* bit 19 */
+#define   ALI_PCM_IN_ENABLE		0x80000000 /* bit 31 */
 
 #define ALI_CSO_ALPHA_FMS	0xe0
 #define ALI_LBA			0xe4
@@ -234,6 +247,7 @@
 	unsigned char	revision;
 
 	unsigned int hw_initialized: 1;
+	unsigned int spdif_support: 1;
 	struct resource *res_port;
 
 	struct pci_dev	*pci;
@@ -673,8 +687,8 @@
 
 	// recording
 	if (rec) {
-		if (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & (1<<11) &&
-			( codec->revision == ALI_5451_V02 ))
+		if (codec->spdif_support &&
+		    (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_IN_SUPPORT))
 			idx = ALI_SPDIF_IN_CHANNEL;
 		else
 			idx = ALI_PCM_IN_CHANNEL;
@@ -688,7 +702,8 @@
 	}
 
 	//playback...
-	if (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & (1<<15)) {
+	if (codec->spdif_support &&
+	    (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_OUT_CH_ENABLE)) {
 		idx = ALI_SPDIF_OUT_CHANNEL;
 		if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) {
 			return result;
@@ -840,7 +855,7 @@
 	unsigned int dwVal;
 
 	dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
-	dwVal |= 1<<11;
+	dwVal |= ALI_SPDIF_IN_SUPPORT;
 	outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
 
 	dwVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
@@ -855,7 +870,7 @@
 	unsigned int dwVal;
 	
 	dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
-	dwVal &= ~(1<<11);
+	dwVal &= ~ALI_SPDIF_IN_SUPPORT;
 	outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
 	
 	snd_ali_disable_special_channel(codec, ALI_SPDIF_IN_CHANNEL);	
@@ -906,14 +921,14 @@
         pci_write_config_byte(pci_dev, 0x7e, bVal);
 
 	bVal = inb(ALI_REG(codec, ALI_SCTRL));
-	outb(bVal | 0x20, ALI_REG(codec, ALI_SCTRL));
+	outb(bVal | ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL));
 
 	bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
-	outb(bVal & ~(1<<6), ALI_REG(codec, ALI_SPDIF_CTRL));
+	outb(bVal & ALI_SPDIF_OUT_CH_STATUS, ALI_REG(codec, ALI_SPDIF_CTRL));
    
 	{
    		wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
-   		wVal |= (1<<10);
+   		wVal |= ALI_SPDIF_OUT_SEL_PCM;
    		outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
 		snd_ali_disable_special_channel(codec,ALI_SPDIF_OUT_CHANNEL);
    	}
@@ -924,7 +939,7 @@
 	unsigned short wVal = 0;
 
 	wVal  = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
-   	wVal &= ~(1<<10);
+   	wVal &= ~ALI_SPDIF_OUT_SEL_PCM;
    	outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
 /*
 	wVal = inw(ALI_REG(codec, ALI_SPDIF_CS));
@@ -941,7 +956,7 @@
 {
 	unsigned short wVal = 0;
   	wVal  = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
-   	wVal |= (1<<10);
+   	wVal |= ALI_SPDIF_OUT_SEL_PCM;
    	outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
 
 	snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL);
@@ -952,7 +967,7 @@
 	unsigned char  bVal;
 
 	bVal = inb(ALI_REG(codec, ALI_SCTRL));
-	outb(bVal & (~0x20), ALI_REG(codec, ALI_SCTRL));
+	outb(bVal & ~ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL));
 
 	snd_ali_disable_spdif_chnout(codec);
 }
@@ -1033,10 +1048,10 @@
 
 
 static irqreturn_t snd_ali_card_interrupt(int irq,
-					  void *dev_id,
-					  struct pt_regs *regs)
+				   void *dev_id,
+				   struct pt_regs *regs)
 {
-	ali_t *codec = snd_magic_cast(ali_t, dev_id, return IRQ_NONE);
+	ali_t 	*codec = snd_magic_cast(ali_t, dev_id, return IRQ_NONE);
 
 	if (codec == NULL)
 		return IRQ_NONE;
@@ -1211,6 +1226,7 @@
 				    
 {
 	ali_t *codec = snd_pcm_substream_chip(substream);
+	struct list_head *pos;
 	snd_pcm_substream_t *s;
 	unsigned int what, whati, capture_flag;
 	snd_ali_voice_t *pvoice = NULL, *evoice = NULL;
@@ -1229,8 +1245,8 @@
 	}
 
 	what = whati = capture_flag = 0;
-	s = substream;
-	do {
+	snd_pcm_group_for_each(pos, substream) {
+		s = snd_pcm_group_substream_entry(pos);
 		if ((ali_t *) _snd_pcm_chip(s->pcm) == codec) {
 			pvoice = (snd_ali_voice_t *) s->runtime->private_data;
 			evoice = pvoice->extra;
@@ -1254,8 +1270,7 @@
 			if (pvoice->mode)
 				capture_flag = 1;
 		}
-		s = s->link_next;
-	} while (s != substream);
+	}
 	spin_lock(&codec->reg_lock);
 	if (! do_start) {
 		outl(what, ALI_REG(codec, ALI_STOP));
@@ -1360,12 +1375,11 @@
 	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;
-		}
+	else if (codec->spdif_support &&
+		 (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_OUT_CH_ENABLE)
+		 && (pvoice->number == ALI_SPDIF_OUT_CHANNEL)) {
+		snd_ali_set_spdif_out_rate(codec, runtime->rate);
+		Delta = 0x1000;
 	}
 	
 	/* set Loop Back Address */
@@ -1867,7 +1881,7 @@
 		snd_printk("ali mixer creating error.\n");
 		return err;
 	}
-	if (codec->revision == ALI_5451_V02) {
+	if (codec->spdif_support) {
 		for(idx = 0; idx < ARRAY_SIZE(snd_ali5451_mixer_spdif); idx++) {
 			err=snd_ctl_add(codec->card, snd_ctl_new1(&snd_ali5451_mixer_spdif[idx], codec));
 			if (err < 0) return err;
@@ -2039,7 +2053,7 @@
 
 	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) {
+	if (codec->spdif_support) {
 		snd_ali_enable_spdif_out(codec);
 		codec->spdif_mask = 0x00000002;
 	}
@@ -2073,9 +2087,10 @@
 }
 
 static int __devinit snd_ali_create(snd_card_t * card,
-				 struct pci_dev *pci,
-				 int pcm_streams,
-				 ali_t ** r_ali)
+				    struct pci_dev *pci,
+				    int pcm_streams,
+				    int spdif_support,
+				    ali_t ** r_ali)
 {
 	ali_t *codec;
 	int i, err;
@@ -2112,6 +2127,7 @@
 	codec->irq = -1;
 	codec->port = pci_resource_start(pci, 0);
 	pci_read_config_byte(pci, PCI_REVISION_ID, &codec->revision);
+	codec->spdif_support = spdif_support;
 
 	if (pcm_streams < 1)
 		pcm_streams = 1;
@@ -2222,7 +2238,7 @@
 	if (card == NULL)
 		return -ENOMEM;
 
-	if ((err = snd_ali_create(card, pci, pcm_channels[dev], &codec)) < 0) {
+	if ((err = snd_ali_create(card, pci, pcm_channels[dev], spdif[dev], &codec)) < 0) {
 		snd_card_free(card);
 		return err;
 	}
diff -urN linux-2.5.70-bk6/sound/pci/cmipci.c linux-2.5.70-bk7/sound/pci/cmipci.c
--- linux-2.5.70-bk6/sound/pci/cmipci.c	2003-05-26 18:00:42.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/cmipci.c	2003-06-02 04:39:13.000000000 -0700
@@ -1496,6 +1496,7 @@
 			/* SPD24SEL for 037, 0x02 */
 			/* SPD24SEL for 039, 0x20, but cannot be set */
 			snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL);
+			snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
 		} else { /* can_ac3_sw */
 #ifdef DO_SOFT_AC3
 			/* FIXME: ugly hack! */
@@ -1519,7 +1520,14 @@
 		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);
+			/* chip model >= 37 */
+			if (snd_pcm_format_width(subs->runtime->format) > 16) {
+				snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+				snd_cmipci_set_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);
+			}
 		} else {
 #ifdef DO_SOFT_AC3
 			snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
@@ -1669,7 +1677,6 @@
 		if ((status & CM_CHINT1) && cm->channel[1].running)
 			snd_pcm_period_elapsed(cm->channel[1].substream);
 	}
-
 	return IRQ_HANDLED;
 }
 
@@ -1742,7 +1749,7 @@
 {
 	.info =			(SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE),
-	.formats =		SNDRV_PCM_FMTBIT_S16_LE /*| SNDRV_PCM_FMTBIT_S32_LE*/,
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
 	.rates =		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
 	.rate_min =		44100,
 	.rate_max =		48000,
@@ -1897,6 +1904,8 @@
 	if (cm->can_ac3_hw)
 #endif
 		runtime->hw.info |= SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID;
+	if (cm->chip_version >= 37)
+		runtime->hw.formats |= SNDRV_PCM_FMTBIT_S32_LE;
 	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000);
 	cm->dig_pcm_status = cm->dig_status;
 	return 0;
diff -urN linux-2.5.70-bk6/sound/pci/cs46xx/cs46xx_lib.c linux-2.5.70-bk7/sound/pci/cs46xx/cs46xx_lib.c
--- linux-2.5.70-bk6/sound/pci/cs46xx/cs46xx_lib.c	2003-05-26 18:00:22.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/cs46xx/cs46xx_lib.c	2003-06-02 04:39:13.000000000 -0700
@@ -2545,6 +2545,14 @@
 	chip->eapd_switch = snd_ctl_find_id(chip->card, &id);
     
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
+	if (chip->nr_ac97_codecs == 1 && 
+	    snd_cs46xx_codec_read(chip, AC97_VENDOR_ID2,
+				  CS46XX_PRIMARY_CODEC_INDEX) == 0x592d) {
+		/* set primary cs4294 codec into Extended Audio Mode */
+		snd_printdd("setting EAM bit on cs4294 CODEC\n");
+		snd_cs46xx_codec_write(chip, AC97_CSR_ACMODE, 0x200,
+				       CS46XX_PRIMARY_CODEC_INDEX);
+	}
 	/* do soundcard specific mixer setup */
 	if (chip->mixer_init) {
 		snd_printdd ("calling chip->mixer_init(chip);\n");
diff -urN linux-2.5.70-bk6/sound/pci/emu10k1/emufx.c linux-2.5.70-bk7/sound/pci/emu10k1/emufx.c
--- linux-2.5.70-bk6/sound/pci/emu10k1/emufx.c	2003-05-26 18:00:40.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/emu10k1/emufx.c	2003-06-02 04:39:13.000000000 -0700
@@ -2204,7 +2204,7 @@
 	unsigned short fxbus_mask, extin_mask, extout_mask;
 	int res;
 
-	memset(info, 0, sizeof(*info));
+	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;
diff -urN linux-2.5.70-bk6/sound/pci/emu10k1/irq.c linux-2.5.70-bk7/sound/pci/emu10k1/irq.c
--- linux-2.5.70-bk6/sound/pci/emu10k1/irq.c	2003-05-26 18:00:45.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/emu10k1/irq.c	2003-06-02 04:39:13.000000000 -0700
@@ -33,22 +33,24 @@
 irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	emu10k1_t *emu = snd_magic_cast(emu10k1_t, dev_id, return IRQ_NONE);
-	unsigned int status;
+	unsigned int status, orig_status;
 	int handled = 0;
 
 	while ((status = inl(emu->port + IPR)) != 0) {
 		// printk("irq - status = 0x%x\n", status);
+		orig_status = status;
 		handled = 1;
 		if (status & IPR_PCIERROR) {
 			snd_printk("interrupt: PCI error\n");
 			snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
+			status &= ~IPR_PCIERROR;
 		}
 		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);
+			status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE);
 		}
 		if (status & IPR_CHANNELLOOP) {
 			int voice;
@@ -88,64 +90,69 @@
 					pvoice++;
 				}
 			}
-			outl(IPR_CHANNELLOOP | (status & IPR_CHANNELNUMBERMASK), emu->port + IPR);
+			status &= ~IPR_CHANNELLOOP;
 		}
+		status &= ~IPR_CHANNELNUMBERMASK;
 		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);
+			status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL);
 		}
 		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);
+			status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL);
 		}
 		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);
+			status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL);
 		}
 		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);
+			status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY);
 		}
 		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);
+			status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2);
 		}
 		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);
+			status &= ~IPR_INTERVALTIMER;
 		}
 		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);
+			status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE);
 		}
 		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);
+			status &= ~IPR_FXDSP;
 		}
+		if (status) {
+			snd_printd(KERN_WARNING "emu10k1: unhandled interrupt: 0x%08x\n", status);
+		}
+		outl(orig_status, emu->port + IPR); /* ack */
 	}
 	return IRQ_RETVAL(handled);
 }
diff -urN linux-2.5.70-bk6/sound/pci/ens1370.c linux-2.5.70-bk7/sound/pci/ens1370.c
--- linux-2.5.70-bk6/sound/pci/ens1370.c	2003-05-26 18:00:46.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ens1370.c	2003-06-02 04:39:13.000000000 -0700
@@ -111,8 +111,11 @@
 #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_1373_TEST_BIT	(1<<29)		/* should be set to 0 for normal operation */
-#define   ES_1373_RECEN_B	(1<<28)		/* mix record with playback for I2S/SPDIF out */
+#define   ES_1373_BYPASS_P1	(1<<31)		/* bypass SRC for PB1 */
+#define   ES_1373_BYPASS_P2	(1<<30)		/* bypass SRC for PB2 */
+#define   ES_1373_BYPASS_R	(1<<29)		/* bypass SRC for REC */
+#define   ES_1373_TEST_BIT	(1<<28)		/* should be set to 0 for normal operation */
+#define   ES_1373_RECEN_B	(1<<27)		/* mix record with playback for I2S/SPDIF out */
 #define   ES_1373_SPDIF_THRU	(1<<26)		/* 0 = SPDIF thru mode, 1 = SPDIF == dig out */
 #define   ES_1371_JOY_ASEL(o)	(((o)&0x03)<<24)/* joystick port mapping */
 #define   ES_1371_JOY_ASELM	(0x03<<24)	/* mask for above */
@@ -739,8 +742,10 @@
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 	{
 		unsigned int what = 0;
-		snd_pcm_substream_t *s = substream;
-		do {
+		struct list_head *pos;
+		snd_pcm_substream_t *s;
+		snd_pcm_group_for_each(pos, substream) {
+			s = snd_pcm_group_substream_entry(pos);
 			if (s == ensoniq->playback1_substream) {
 				what |= ES_P1_PAUSE;
 				snd_pcm_trigger_done(s, substream);
@@ -749,8 +754,7 @@
 				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;
@@ -764,8 +768,10 @@
 	case SNDRV_PCM_TRIGGER_STOP:
 	{
 		unsigned int what = 0;
-		snd_pcm_substream_t *s = substream;
-		do {
+		struct list_head *pos;
+		snd_pcm_substream_t *s;
+		snd_pcm_group_for_each(pos, substream) {
+			s = snd_pcm_group_substream_entry(pos);
 			if (s == ensoniq->playback1_substream) {
 				what |= ES_DAC1_EN;
 				snd_pcm_trigger_done(s, substream);
@@ -776,8 +782,7 @@
 				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;
@@ -1509,6 +1514,7 @@
 	ac97.read = snd_es1371_codec_read;
 	ac97.private_data = ensoniq;
 	ac97.private_free = snd_ensoniq_mixer_free_ac97;
+	ac97.scaps = AC97_SCAP_AUDIO;
 	if ((err = snd_ac97_mixer(card, &ac97, &ensoniq->u.es1371.ac97)) < 0)
 		return err;
 	for (idx = 0; es1371_spdif_present[idx].vid != (unsigned short)PCI_ANY_ID; idx++)
diff -urN linux-2.5.70-bk6/sound/pci/ice1712/Makefile linux-2.5.70-bk7/sound/pci/ice1712/Makefile
--- linux-2.5.70-bk6/sound/pci/ice1712/Makefile	2003-05-26 18:00:38.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ice1712/Makefile	2003-06-02 04:39:13.000000000 -0700
@@ -3,8 +3,8 @@
 # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
 #
 
-snd-ice1712-objs := ice1712.o ak4524.o delta.o hoontech.o ews.o
-snd-ice1724-objs := ice1724.o ak4524.o amp.o revo.o
+snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o ak4xxx.o
+snd-ice1724-objs := ice1724.o amp.o revo.o ak4xxx.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o
diff -urN linux-2.5.70-bk6/sound/pci/ice1712/ak4524.c linux-2.5.70-bk7/sound/pci/ice1712/ak4524.c
--- linux-2.5.70-bk6/sound/pci/ice1712/ak4524.c	2003-05-26 18:00:41.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ice1712/ak4524.c	1969-12-31 16:00:00.000000000 -0800
@@ -1,525 +0,0 @@
-/*
- *   ALSA driver for ICEnsemble ICE1712 (Envy24)
- *
- *   AK4524 / AK4528 / AK4529 / AK4355 / AK4381 interface
- *
- *	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 <sound/core.h>
-#include "ice1712.h"
-
-
-/*
- * write AK4xxx register
- */
-void snd_ice1712_akm4xxx_write(akm4xxx_t *ak, int chip,
-			      unsigned char addr, unsigned char data)
-{
-	unsigned int tmp;
-	int idx;
-	unsigned int addrdata;
-	ice1712_t *ice = ak->chip;
-
-	snd_assert(chip >= 0 && chip < 4, return);
-
-	if (ak->ops.start) {
-		if (ak->ops.start(ak, chip) < 0)
-			return;
-	} else
-		snd_ice1712_save_gpio_status(ice);
-
-	tmp = snd_ice1712_gpio_read(ice);
-	tmp |= ak->add_flags;
-	tmp &= ~ak->mask_flags;
-	if (ak->cs_mask == ak->cs_addr) {
-		if (ak->cif) {
-			tmp |= ak->cs_mask; /* start without chip select */
-		}  else {
-			tmp &= ~ak->cs_mask; /* chip select low */
-			snd_ice1712_gpio_write(ice, tmp);
-			udelay(1);
-		}
-	} else {
-		/* doesn't handle cf=1 yet */
-		tmp &= ~ak->cs_mask;
-		tmp |= ak->cs_addr;
-		snd_ice1712_gpio_write(ice, tmp);
-		udelay(1);
-	}
-
-	/* build I2C address + data byte */
-	addrdata = (ak->caddr << 6) | 0x20 | (addr & 0x1f);
-	addrdata = (addrdata << 8) | data;
-	for (idx = 15; idx >= 0; idx--) {
-		/* drop clock */
-		tmp &= ~ak->clk_mask;
-		snd_ice1712_gpio_write(ice, tmp);
-		udelay(1);
-		/* set data */
-		if (addrdata & (1 << idx))
-			tmp |= ak->data_mask;
-		else
-			tmp &= ~ak->data_mask;
-		snd_ice1712_gpio_write(ice, tmp);
-		udelay(1);
-		/* raise clock */
-		tmp |= ak->clk_mask;
-		snd_ice1712_gpio_write(ice, tmp);
-		udelay(1);
-	}
-
-	/* save the data */
-	if (ak->type == SND_AK4524 || ak->type == SND_AK4528) {
-		if ((addr != 0x04 && addr != 0x05) || (data & 0x80) == 0)
-			ak->images[chip][addr] = data;
-		else
-			ak->ipga_gain[chip][addr-4] = data;
-	} else {
-		/* AK4529, or else */
-		ak->images[chip][addr] = data;
-	}
-	
-	if (ak->cs_mask == ak->cs_addr) {
-		if (ak->cif) {
-			/* assert a cs pulse to trigger */
-			tmp &= ~ak->cs_mask;
-			snd_ice1712_gpio_write(ice, tmp);
-			udelay(1);
-		}
-		tmp |= ak->cs_mask; /* chip select high to trigger */
-	} else {
-		tmp &= ~ak->cs_mask;
-		tmp |= ak->cs_none; /* deselect address */
-	}
-	snd_ice1712_gpio_write(ice, tmp);
-	udelay(1);
-
-	if (ak->ops.stop)
-		ak->ops.stop(ak);
-	else
-		snd_ice1712_restore_gpio_status(ice);
-}
-
-/*
- * reset the AKM codecs
- * @state: 1 = reset codec, 0 = restore the registers
- *
- * assert the reset operation and restores the register values to the chips.
- */
-void snd_ice1712_akm4xxx_reset(akm4xxx_t *ak, int state)
-{
-	unsigned int chip;
-	unsigned char reg;
-	
-	switch (ak->type) {
-	case SND_AK4524:
-	case SND_AK4528:
-		for (chip = 0; chip < ak->num_dacs/2; chip++) {
-			snd_ice1712_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
-			if (state)
-				continue;
-			/* DAC volumes */
-			for (reg = 0x04; reg < (ak->type == SND_AK4528 ? 0x06 : 0x08); reg++)
-				snd_ice1712_akm4xxx_write(ak, chip, reg, ak->images[chip][reg]);
-			if (ak->type == SND_AK4528)
-				continue;
-			/* IPGA */
-			for (reg = 0x04; reg < 0x06; reg++)
-				snd_ice1712_akm4xxx_write(ak, chip, reg, ak->ipga_gain[chip][reg-4]);
-		}
-		break;
-	case SND_AK4529:
-		/* FIXME: needed for ak4529? */
-		break;
-	case SND_AK4355:
-		snd_ice1712_akm4xxx_write(ak, 0, 0x01, state ? 0x02 : 0x01);
-		if (state)
-			return;
-		for (reg = 0x00; reg < 0x0a; reg++)
-			if (reg != 0x01)
-				snd_ice1712_akm4xxx_write(ak, 0, reg, ak->images[0][reg]);
-		break;
-	case SND_AK4381:
-		for (chip = 0; chip < ak->num_dacs/2; chip++) {
-			snd_ice1712_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
-			if (state)
-				continue;
-			for (reg = 0x01; reg < 0x05; reg++)
-				snd_ice1712_akm4xxx_write(ak, chip, reg, ak->images[chip][reg]);
-		}
-		break;
-	}
-}
-
-/*
- * initialize all the ak4xxx chips
- */
-static void __devinit snd_ice1712_akm4xxx_init_chip(akm4xxx_t *ak)
-{
-	static unsigned char inits_ak4524[] = {
-		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
-	};
-	static unsigned char inits_ak4528[] = {
-		0x00, 0x07, /* 0: all power up */
-		0x01, 0x00, /* 1: ADC/DAC reset */
-		0x02, 0x60, /* 2: 24bit I2S */
-		0x03, 0x0d, /* 3: deemphasis off, turn LR highpass filters on */
-		0x01, 0x03, /* 1: ADC/DAC enable */
-		0x04, 0x00, /* 4: ADC left muted */
-		0x05, 0x00, /* 5: ADC right muted */
-		0xff, 0xff
-	};
-	static unsigned char inits_ak4529[] = {
-		0x09, 0x01, /* 9: ATS=0, RSTN=1 */
-		0x0a, 0x3f, /* A: all power up, no zero/overflow detection */
-		0x00, 0x0c, /* 0: TDM=0, 24bit I2S, SMUTE=0 */
-		0x01, 0x00, /* 1: ACKS=0, ADC, loop off */
-		0x02, 0xff, /* 2: LOUT1 muted */
-		0x03, 0xff, /* 3: ROUT1 muted */
-		0x04, 0xff, /* 4: LOUT2 muted */
-		0x05, 0xff, /* 5: ROUT2 muted */
-		0x06, 0xff, /* 6: LOUT3 muted */
-		0x07, 0xff, /* 7: ROUT3 muted */
-		0x0b, 0xff, /* B: LOUT4 muted */
-		0x0c, 0xff, /* C: ROUT4 muted */
-		0x08, 0x55, /* 8: deemphasis all off */
-		0xff, 0xff
-	};
-	static unsigned char inits_ak4355[] = {
-		0x01, 0x02, /* 1: reset and soft-mute */
-		0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, disable DZF, sharp roll-off, RSTN#=0 */
-		// 0x02, 0x0e, /* 2: DA's power up, normal speed, RSTN#=0 */
-		0x02, 0x2e,
-		0x03, 0x01, /* 3: de-emphasis off */
-		0x04, 0x00, /* 4: LOUT1 volume muted */
-		0x05, 0x00, /* 5: ROUT1 volume muted */
-		0x06, 0x00, /* 6: LOUT2 volume muted */
-		0x07, 0x00, /* 7: ROUT2 volume muted */
-		0x08, 0x00, /* 8: LOUT3 volume muted */
-		0x09, 0x00, /* 9: ROUT3 volume muted */
-		0x0a, 0x00, /* a: DATT speed=0, ignore DZF */
-		0x01, 0x01, /* 1: un-reset, unmute */
-		0xff, 0xff
-	};
-	static unsigned char inits_ak4381[] = {
-		0x00, 0x0c, /* 0: mode3(i2s), disable auto-clock detect */
-		// 0x01, 0x02, /* 1: de-emphasis off, normal speed, sharp roll-off, DZF off */
-		0x01, 0x12,
-		0x02, 0x00, /* 2: DZF disabled */
-		0x03, 0x00, /* 3: LATT 0 */
-		0x04, 0x00, /* 4: RATT 0 */
-		0x00, 0x0f, /* 0: power-up, un-reset */
-		0xff, 0xff
-	};
-
-	int chip, num_chips;
-	unsigned char *ptr, reg, data, *inits;
-
-	switch (ak->type) {
-	case SND_AK4524:
-		inits = inits_ak4524;
-		num_chips = ak->num_dacs / 2;
-		break;
-	case SND_AK4528:
-		inits = inits_ak4528;
-		num_chips = ak->num_dacs / 2;
-		break;
-	case SND_AK4529:
-		inits = inits_ak4529;
-		num_chips = 1;
-		break;
-	case SND_AK4355:
-		inits = inits_ak4355;
-		num_chips = 1;
-		break;
-	case SND_AK4381:
-		inits = inits_ak4381;
-		num_chips = ak->num_dacs / 2;
-		break;
-	default:
-		snd_BUG();
-		return;
-	}
-
-	for (chip = 0; chip < num_chips; chip++) {
-		ptr = inits;
-		while (*ptr != 0xff) {
-			reg = *ptr++;
-			data = *ptr++;
-			snd_ice1712_akm4xxx_write(ak, chip, reg, data);
-		}
-	}
-}
-
-
-/*
- * initialize the akm4xxx_t record with the template
- */
-void snd_ice1712_akm4xxx_init(akm4xxx_t *ak, const akm4xxx_t *temp, ice1712_t *ice)
-{
-	*ak = *temp;
-	ak->chip = ice;
-	snd_ice1712_akm4xxx_init_chip(ak);
-}
-
-
-#define AK_GET_CHIP(val)		(((val) >> 8) & 0xff)
-#define AK_GET_ADDR(val)		((val) & 0xff)
-#define AK_GET_SHIFT(val)		(((val) >> 16) & 0x7f)
-#define AK_GET_INVERT(val)		(((val) >> 23) & 1)
-#define AK_GET_MASK(val)		(((val) >> 24) & 0xff)
-#define AK_COMPOSE(chip,addr,shift,mask) (((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24))
-#define AK_INVERT 			(1<<23)
-
-static int snd_ice1712_akm4xxx_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
-{
-	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
-
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 1;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = mask;
-	return 0;
-}
-
-static int snd_ice1712_akm4xxx_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol);
-	int chip = AK_GET_CHIP(kcontrol->private_value);
-	int addr = AK_GET_ADDR(kcontrol->private_value);
-	int invert = AK_GET_INVERT(kcontrol->private_value);
-	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
-	unsigned char val = ak->images[chip][addr];
-	
-	ucontrol->value.integer.value[0] = invert ? mask - val : val;
-	return 0;
-}
-
-static int snd_ice1712_akm4xxx_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol);
-	int chip = AK_GET_CHIP(kcontrol->private_value);
-	int addr = AK_GET_ADDR(kcontrol->private_value);
-	int invert = AK_GET_INVERT(kcontrol->private_value);
-	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
-	unsigned char nval = ucontrol->value.integer.value[0] % (mask+1);
-	int change;
-
-	if (invert)
-		nval = mask - nval;
-	change = ak->images[chip][addr] != nval;
-	if (change)
-		snd_ice1712_akm4xxx_write(ak, chip, addr, nval);
-	return change;
-}
-
-static int snd_ice1712_akm4xxx_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_akm4xxx_ipga_gain_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol);
-	int chip = AK_GET_CHIP(kcontrol->private_value);
-	int addr = AK_GET_ADDR(kcontrol->private_value);
-	ucontrol->value.integer.value[0] = ak->ipga_gain[chip][addr-4] & 0x7f;
-	return 0;
-}
-
-static int snd_ice1712_akm4xxx_ipga_gain_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol);
-	int chip = AK_GET_CHIP(kcontrol->private_value);
-	int addr = AK_GET_ADDR(kcontrol->private_value);
-	unsigned char nval = (ucontrol->value.integer.value[0] % 37) | 0x80;
-	int change = ak->ipga_gain[chip][addr] != nval;
-	if (change)
-		snd_ice1712_akm4xxx_write(ak, chip, addr, nval);
-	return change;
-}
-
-static int snd_ice1712_akm4xxx_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_akm4xxx_deemphasis_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol);
-	int chip = AK_GET_CHIP(kcontrol->private_value);
-	int addr = AK_GET_ADDR(kcontrol->private_value);
-	int shift = AK_GET_SHIFT(kcontrol->private_value);
-	ucontrol->value.enumerated.item[0] = (ak->images[chip][addr] >> shift) & 3;
-	return 0;
-}
-
-static int snd_ice1712_akm4xxx_deemphasis_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol);
-	int chip = AK_GET_CHIP(kcontrol->private_value);
-	int addr = AK_GET_ADDR(kcontrol->private_value);
-	int shift = AK_GET_SHIFT(kcontrol->private_value);
-	unsigned char nval = ucontrol->value.enumerated.item[0] & 3;
-	int change;
-	
-	nval = (nval << shift) | (ak->images[chip][addr] & ~(3 << shift));
-	change = ak->images[chip][addr] != nval;
-	if (change)
-		snd_ice1712_akm4xxx_write(ak, chip, addr, nval);
-	return change;
-}
-
-/*
- * build AK4524 controls
- */
-
-int __devinit snd_ice1712_akm4xxx_build_controls(ice1712_t *ice)
-{
-	unsigned int idx;
-	int err;
-	unsigned int akidx;
-
-	for (akidx = 0; akidx < ice->akm_codecs; akidx++) {
-		akm4xxx_t *ak = &ice->akm[akidx];
-		for (idx = 0; idx < ak->num_dacs; ++idx) {
-			snd_kcontrol_t ctl;
-			memset(&ctl, 0, sizeof(ctl));
-			strcpy(ctl.id.name, "DAC Volume");
-			ctl.id.index = idx + ak->idx_offset * 2;
-			ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-			ctl.count = 1;
-			ctl.info = snd_ice1712_akm4xxx_volume_info;
-			ctl.get = snd_ice1712_akm4xxx_volume_get;
-			ctl.put = snd_ice1712_akm4xxx_volume_put;
-			switch (ak->type) {
-			case SND_AK4524:
-				ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127); /* register 6 & 7 */
-				break;
-			case SND_AK4528:
-				ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); /* register 4 & 5 */
-				break;
-			case SND_AK4529: {
-				int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb; /* registers 2-7 and b,c */
-				ctl.private_value = AK_COMPOSE(0, val, 0, 255) | AK_INVERT;
-				break;
-			}
-			case SND_AK4355:
-				ctl.private_value = AK_COMPOSE(0, idx + 4, 0, 255); /* register 4-9, chip #0 only */
-				break;
-			case SND_AK4381:
-				ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255); /* register 3 & 4 */
-				break;
-			default:
-				return -EINVAL;
-			}
-			ctl.private_data = ak;
-			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
-				return err;
-		}
-		for (idx = 0; idx < ak->num_adcs && ak->type == SND_AK4524; ++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.count = 1;
-			ctl.info = snd_ice1712_akm4xxx_volume_info;
-			ctl.get = snd_ice1712_akm4xxx_volume_get;
-			ctl.put = snd_ice1712_akm4xxx_volume_put;
-			ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); /* register 4 & 5 */
-			ctl.private_data = ak;
-			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 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.count = 1;
-			ctl.info = snd_ice1712_akm4xxx_ipga_gain_info;
-			ctl.get = snd_ice1712_akm4xxx_ipga_gain_get;
-			ctl.put = snd_ice1712_akm4xxx_ipga_gain_put;
-			ctl.private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 0); /* register 4 & 5 */
-			ctl.private_data = ak;
-			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
-				return err;
-		}
-		for (idx = 0; idx < ak->num_dacs/2; idx++) {
-			snd_kcontrol_t ctl;
-			memset(&ctl, 0, sizeof(ctl));
-			strcpy(ctl.id.name, "Deemphasis");
-			ctl.id.index = idx + ak->idx_offset;
-			ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-			ctl.count = 1;
-			ctl.info = snd_ice1712_akm4xxx_deemphasis_info;
-			ctl.get = snd_ice1712_akm4xxx_deemphasis_get;
-			ctl.put = snd_ice1712_akm4xxx_deemphasis_put;
-			switch (ak->type) {
-			case SND_AK4524:
-			case SND_AK4528:
-				ctl.private_value = AK_COMPOSE(idx, 3, 0, 0); /* register 3 */
-				break;
-			case SND_AK4529: {
-				int shift = idx == 3 ? 6 : (2 - idx) * 2;
-				ctl.private_value = AK_COMPOSE(0, 8, shift, 0); /* register 8 with shift */
-				break;
-			}
-			case SND_AK4355:
-				ctl.private_value = AK_COMPOSE(idx, 3, 0, 0);
-				break;
-			case SND_AK4381:
-				ctl.private_value = AK_COMPOSE(idx, 1, 1, 0);
-				break;
-			}
-			ctl.private_data = ak;
-			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
-				return err;
-		}
-	}
-	return 0;
-}
diff -urN linux-2.5.70-bk6/sound/pci/ice1712/ak4xxx.c linux-2.5.70-bk7/sound/pci/ice1712/ak4xxx.c
--- linux-2.5.70-bk6/sound/pci/ice1712/ak4xxx.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/pci/ice1712/ak4xxx.c	2003-06-02 04:39:13.000000000 -0700
@@ -0,0 +1,181 @@
+/*
+ *   ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ *   AK4524 / AK4528 / AK4529 / AK4355 / AK4381 interface
+ *
+ *	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 <sound/core.h>
+#include "ice1712.h"
+
+static void snd_ice1712_akm4xxx_lock(akm4xxx_t *ak, int chip)
+{
+	ice1712_t *ice = ak->private_data[0];
+
+	snd_ice1712_save_gpio_status(ice);
+}
+
+static void snd_ice1712_akm4xxx_unlock(akm4xxx_t *ak, int chip)
+{
+	ice1712_t *ice = ak->private_data[0];
+
+	snd_ice1712_restore_gpio_status(ice);
+}
+
+/*
+ * write AK4xxx register
+ */
+static void snd_ice1712_akm4xxx_write(akm4xxx_t *ak, int chip,
+				      unsigned char addr, unsigned char data)
+{
+	unsigned int tmp;
+	int idx;
+	unsigned int addrdata;
+	struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
+	ice1712_t *ice = ak->private_data[0];
+
+	snd_assert(chip >= 0 && chip < 4, return);
+
+	tmp = snd_ice1712_gpio_read(ice);
+	tmp |= priv->add_flags;
+	tmp &= ~priv->mask_flags;
+	if (priv->cs_mask == priv->cs_addr) {
+		if (priv->cif) {
+			tmp |= priv->cs_mask; /* start without chip select */
+		}  else {
+			tmp &= ~priv->cs_mask; /* chip select low */
+			snd_ice1712_gpio_write(ice, tmp);
+			udelay(1);
+		}
+	} else {
+		/* doesn't handle cf=1 yet */
+		tmp &= ~priv->cs_mask;
+		tmp |= priv->cs_addr;
+		snd_ice1712_gpio_write(ice, tmp);
+		udelay(1);
+	}
+
+	/* build I2C address + data byte */
+	addrdata = (priv->caddr << 6) | 0x20 | (addr & 0x1f);
+	addrdata = (addrdata << 8) | data;
+	for (idx = 15; idx >= 0; idx--) {
+		/* drop clock */
+		tmp &= ~priv->clk_mask;
+		snd_ice1712_gpio_write(ice, tmp);
+		udelay(1);
+		/* set data */
+		if (addrdata & (1 << idx))
+			tmp |= priv->data_mask;
+		else
+			tmp &= ~priv->data_mask;
+		snd_ice1712_gpio_write(ice, tmp);
+		udelay(1);
+		/* raise clock */
+		tmp |= priv->clk_mask;
+		snd_ice1712_gpio_write(ice, tmp);
+		udelay(1);
+	}
+
+	/* save the data */
+	if (ak->type == SND_AK4524 || ak->type == SND_AK4528) {
+		if ((addr != 0x04 && addr != 0x05) || (data & 0x80) == 0)
+			ak->images[chip][addr] = data;
+		else
+			ak->ipga_gain[chip][addr-4] = data;
+	} else {
+		/* AK4529, or else */
+		ak->images[chip][addr] = data;
+	}
+	
+	if (priv->cs_mask == priv->cs_addr) {
+		if (priv->cif) {
+			/* assert a cs pulse to trigger */
+			tmp &= ~priv->cs_mask;
+			snd_ice1712_gpio_write(ice, tmp);
+			udelay(1);
+		}
+		tmp |= priv->cs_mask; /* chip select high to trigger */
+	} else {
+		tmp &= ~priv->cs_mask;
+		tmp |= priv->cs_none; /* deselect address */
+	}
+	snd_ice1712_gpio_write(ice, tmp);
+	udelay(1);
+}
+
+/*
+ * initialize the akm4xxx_t record with the template
+ */
+int __devinit snd_ice1712_akm4xxx_init(akm4xxx_t *ak, const akm4xxx_t *temp,
+				       const struct snd_ak4xxx_private *_priv, ice1712_t *ice)
+{
+	struct snd_ak4xxx_private *priv;
+
+	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL)
+		return -ENOMEM;
+	*ak = *temp;
+	ak->card = ice->card;
+	*priv = *_priv;
+        ak->private_value[0] = (unsigned long)priv;
+	ak->private_data[0] = ice;
+	if (ak->ops.lock == NULL)
+		ak->ops.lock = snd_ice1712_akm4xxx_lock;
+	if (ak->ops.unlock == NULL)
+		ak->ops.unlock = snd_ice1712_akm4xxx_unlock;
+	if (ak->ops.write == NULL)
+		ak->ops.write = snd_ice1712_akm4xxx_write;
+	snd_akm4xxx_init(ak);
+	return 0;
+}
+
+void __devexit snd_ice1712_akm4xxx_free(ice1712_t *ice)
+{
+	unsigned int akidx;
+	if (ice->akm == NULL)
+		return;
+	for (akidx = 0; akidx < ice->akm_codecs; akidx++) {
+		akm4xxx_t *ak = &ice->akm[akidx];
+		if (ak->private_value[0])
+			kfree((void *)ak->private_value[0]);
+	}
+	kfree(ice->akm);
+}
+
+/*
+ * build AK4xxx controls
+ */
+int __devinit snd_ice1712_akm4xxx_build_controls(ice1712_t *ice)
+{
+	unsigned int akidx;
+	int err;
+
+	for (akidx = 0; akidx < ice->akm_codecs; akidx++) {
+		akm4xxx_t *ak = &ice->akm[akidx];
+		err = snd_akm4xxx_build_controls(ak);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
diff -urN linux-2.5.70-bk6/sound/pci/ice1712/delta.c linux-2.5.70-bk7/sound/pci/ice1712/delta.c
--- linux-2.5.70-bk6/sound/pci/ice1712/delta.c	2003-05-26 18:00:23.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ice1712/delta.c	2003-06-02 04:39:13.000000000 -0700
@@ -232,24 +232,28 @@
 /*
  * AK4524 on Delta 44 and 66 to choose the chip mask
  */
-static int delta_ak4524_start(akm4xxx_t *ak, int chip)
+static void delta_ak4524_lock(akm4xxx_t *ak, int chip)
 {
-	snd_ice1712_save_gpio_status(ak->chip);
-	ak->cs_mask =
-	ak->cs_addr = chip == 0 ? ICE1712_DELTA_CODEC_CHIP_A :
-				  ICE1712_DELTA_CODEC_CHIP_B;
-	return 0;
+        struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
+        ice1712_t *ice = ak->private_data[0];
+
+	snd_ice1712_save_gpio_status(ice);
+	priv->cs_mask =
+	priv->cs_addr = chip == 0 ? ICE1712_DELTA_CODEC_CHIP_A :
+				    ICE1712_DELTA_CODEC_CHIP_B;
 }
 
 /*
  * AK4524 on Delta1010LT to choose the chip address
  */
-static int delta1010lt_ak4524_start(akm4xxx_t *ak, int chip)
+static void delta1010lt_ak4524_lock(akm4xxx_t *ak, int chip)
 {
-	snd_ice1712_save_gpio_status(ak->chip);
-	ak->cs_mask = ICE1712_DELTA_1010LT_CS;
-	ak->cs_addr = chip << 4;
-	return 0;
+        struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
+        ice1712_t *ice = ak->private_data[0];
+
+	snd_ice1712_save_gpio_status(ice);
+	priv->cs_mask = ICE1712_DELTA_1010LT_CS;
+	priv->cs_addr = chip << 4;
 }
 
 /*
@@ -258,7 +262,7 @@
 static void delta_ak4524_set_rate_val(akm4xxx_t *ak, unsigned int rate)
 {
 	unsigned char tmp, tmp2;
-	ice1712_t *ice = ak->chip;
+	ice1712_t *ice = ak->private_data[0];
 
 	if (rate == 0)	/* no hint - S/PDIF input is master, simply return */
 		return;
@@ -275,14 +279,14 @@
 		return;
 
 	/* do it again */
-	snd_ice1712_akm4xxx_reset(ak, 1);
+	snd_akm4xxx_reset(ak, 1);
 	down(&ice->gpio_mutex);
 	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ~ICE1712_DELTA_DFS;
 	if (rate > 48000)
 		tmp |= ICE1712_DELTA_DFS;
 	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
 	up(&ice->gpio_mutex);
-	snd_ice1712_akm4xxx_reset(ak, 0);
+	snd_akm4xxx_reset(ak, 0);
 }
 
 
@@ -330,6 +334,12 @@
 	.type = SND_AK4528,
 	.num_adcs = 2,
 	.num_dacs = 2,
+	.ops = {
+		.set_rate_val = delta_ak4524_set_rate_val
+	}
+};
+
+static struct snd_ak4xxx_private akm_audiophile_priv __devinitdata = {
 	.caddr = 2,
 	.cif = 0,
 	.data_mask = ICE1712_DELTA_AP_DOUT,
@@ -339,15 +349,18 @@
 	.cs_none = 0,
 	.add_flags = ICE1712_DELTA_AP_CS_DIGITAL,
 	.mask_flags = 0,
-	.ops = {
-		.set_rate_val = delta_ak4524_set_rate_val
-	}
 };
 
 static akm4xxx_t akm_delta410 __devinitdata = {
 	.type = SND_AK4529,
 	.num_adcs = 2,
 	.num_dacs = 8,
+	.ops = {
+		.set_rate_val = delta_ak4524_set_rate_val
+	}
+};
+
+static struct snd_ak4xxx_private akm_delta410_priv __devinitdata = {
 	.caddr = 0,
 	.cif = 0,
 	.data_mask = ICE1712_DELTA_AP_DOUT,
@@ -357,15 +370,19 @@
 	.cs_none = 0,
 	.add_flags = ICE1712_DELTA_AP_CS_DIGITAL,
 	.mask_flags = 0,
-	.ops = {
-		.set_rate_val = delta_ak4524_set_rate_val
-	}
 };
 
 static akm4xxx_t akm_delta1010lt __devinitdata = {
 	.type = SND_AK4524,
 	.num_adcs = 8,
 	.num_dacs = 8,
+	.ops = {
+		.lock = delta1010lt_ak4524_lock,
+		.set_rate_val = delta_ak4524_set_rate_val
+	}
+};
+
+static struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = {
 	.caddr = 2,
 	.cif = 0, /* the default level of the CIF pin from AK4524 */
 	.data_mask = ICE1712_DELTA_1010LT_DOUT,
@@ -375,16 +392,19 @@
 	.cs_none = ICE1712_DELTA_1010LT_CS_NONE,
 	.add_flags = 0,
 	.mask_flags = 0,
-	.ops = {
-		.start = delta1010lt_ak4524_start,
-		.set_rate_val = delta_ak4524_set_rate_val
-	}
 };
 
 static akm4xxx_t akm_delta44 __devinitdata = {
 	.type = SND_AK4524,
 	.num_adcs = 4,
 	.num_dacs = 4,
+	.ops = {
+		.lock = delta_ak4524_lock,
+		.set_rate_val = delta_ak4524_set_rate_val
+	}
+};
+
+static struct snd_ak4xxx_private akm_delta44_priv __devinitdata = {
 	.caddr = 2,
 	.cif = 0, /* the default level of the CIF pin from AK4524 */
 	.data_mask = ICE1712_DELTA_CODEC_SERIAL_DATA,
@@ -394,10 +414,6 @@
 	.cs_none = 0,
 	.add_flags = 0,
 	.mask_flags = 0,
-	.ops = {
-		.start = delta_ak4524_start,
-		.set_rate_val = delta_ak4524_set_rate_val
-	}
 };
 
 static int __devinit snd_ice1712_delta_init(ice1712_t *ice)
@@ -466,24 +482,24 @@
 
 	switch (ice->eeprom.subvendor) {
 	case ICE1712_SUBDEVICE_AUDIOPHILE:
-		snd_ice1712_akm4xxx_init(ak, &akm_audiophile, ice);
+		err = snd_ice1712_akm4xxx_init(ak, &akm_audiophile, &akm_audiophile_priv, ice);
 		break;
 	case ICE1712_SUBDEVICE_DELTA410:
-		snd_ice1712_akm4xxx_init(ak, &akm_delta410, ice);
+		err = snd_ice1712_akm4xxx_init(ak, &akm_delta410, &akm_delta410_priv, ice);
 		break;
 	case ICE1712_SUBDEVICE_DELTA1010LT:
-		snd_ice1712_akm4xxx_init(ak, &akm_delta1010lt, ice);
+		err = snd_ice1712_akm4xxx_init(ak, &akm_delta1010lt, &akm_delta1010lt_priv, ice);
 		break;
 	case ICE1712_SUBDEVICE_DELTA66:
 	case ICE1712_SUBDEVICE_DELTA44:
-		snd_ice1712_akm4xxx_init(ak, &akm_delta44, ice);
+		err = snd_ice1712_akm4xxx_init(ak, &akm_delta44, &akm_delta44_priv, ice);
 		break;
 	default:
 		snd_BUG();
 		return -EINVAL;
 	}
 
-	return 0;
+	return err;
 }
 
 
@@ -534,9 +550,6 @@
 	case ICE1712_SUBDEVICE_DELTA1010:
 	case ICE1712_SUBDEVICE_DELTADIO2496:
 	case ICE1712_SUBDEVICE_DELTA66:
-	case ICE1712_SUBDEVICE_AUDIOPHILE:
-	case ICE1712_SUBDEVICE_DELTA410:
-	case ICE1712_SUBDEVICE_DELTA1010LT:
 		err = snd_ice1712_spdif_build_controls(ice);
 		if (err < 0)
 			return err;
diff -urN linux-2.5.70-bk6/sound/pci/ice1712/envy24ht.h linux-2.5.70-bk7/sound/pci/ice1712/envy24ht.h
--- linux-2.5.70-bk6/sound/pci/ice1712/envy24ht.h	2003-05-26 18:01:03.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ice1712/envy24ht.h	2003-06-02 04:39:13.000000000 -0700
@@ -150,8 +150,7 @@
 #define   VT1724_AC97_WRITE	0x20	/* W: write, R: write in progress */
 #define   VT1724_AC97_READ	0x10	/* W: read, R: read in progress */
 #define   VT1724_AC97_READY	0x08	/* codec ready status bit */
-#define   VT1724_AC97_PBK_VSR	0x02	/* playback VSR */
-#define   VT1724_AC97_CAP_VSR	0x01	/* capture VSR */
+#define   VT1724_AC97_ID_MASK	0x03	/* codec id mask */
 #define VT1724_MT_AC97_DATA		0x06	/* word - AC'97 data */
 #define VT1724_MT_PLAYBACK_ADDR		0x10	/* dword - playback address */
 #define VT1724_MT_PLAYBACK_SIZE		0x14	/* dword - playback size */
diff -urN linux-2.5.70-bk6/sound/pci/ice1712/ews.c linux-2.5.70-bk7/sound/pci/ice1712/ews.c
--- linux-2.5.70-bk6/sound/pci/ice1712/ews.c	2003-05-26 18:00:26.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ice1712/ews.c	2003-06-02 04:39:13.000000000 -0700
@@ -155,13 +155,13 @@
 }
 
 /* start callback for EWS88MT, needs to select a certain chip mask */
-static int ews88mt_ak4524_start(akm4xxx_t *ak, int chip)
+static void ews88mt_ak4524_lock(akm4xxx_t *ak, int chip)
 {
-	ice1712_t *ice = ak->chip;
+	ice1712_t *ice = ak->private_data[0];
 	unsigned char tmp;
 	/* assert AK4524 CS */
 	if (snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f) < 0)
-		return -EINVAL;
+		snd_printk(KERN_ERR "fatal error (ews88mt chip select)\n");
 	snd_ice1712_save_gpio_status(ice);
 	tmp = ICE1712_EWS88_SERIAL_DATA |
 		ICE1712_EWS88_SERIAL_CLOCK |
@@ -169,22 +169,21 @@
 	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
 			  ice->gpio.direction | tmp);
 	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp);
-	return 0;
 }
 
 /* stop callback for EWS88MT, needs to deselect chip mask */
-static void ews88mt_ak4524_stop(akm4xxx_t *ak)
+static void ews88mt_ak4524_unlock(akm4xxx_t *ak, int chip)
 {
-	ice1712_t *ice = ak->chip;
+	ice1712_t *ice = ak->private_data[0];
 	snd_ice1712_restore_gpio_status(ice);
 	udelay(1);
 	snd_ice1712_ews88mt_chip_select(ice, 0x0f);
 }
 
 /* start callback for EWX24/96 */
-static int ewx2496_ak4524_start(akm4xxx_t *ak, int chip)
+static void ewx2496_ak4524_lock(akm4xxx_t *ak, int chip)
 {
-	ice1712_t *ice = ak->chip;
+	ice1712_t *ice = ak->private_data[0];
 	unsigned char tmp;
 	snd_ice1712_save_gpio_status(ice);
 	tmp =  ICE1712_EWX2496_SERIAL_DATA |
@@ -194,26 +193,24 @@
 	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
 			  ice->gpio.direction | tmp);
 	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp);
-	return 0;
 }
 
 /* start callback for DMX 6fire */
-static int dmx6fire_ak4524_start(akm4xxx_t *ak, int chip)
+static void dmx6fire_ak4524_lock(akm4xxx_t *ak, int chip)
 {
-	ice1712_t *ice = ak->chip;
+	struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
+	ice1712_t *ice = ak->private_data[0];
 	unsigned char tmp;
 	snd_ice1712_save_gpio_status(ice);
-	tmp = ak->cs_mask = ak->cs_addr = (1 << chip) & ICE1712_6FIRE_AK4524_CS_MASK;
+	tmp = priv->cs_mask = priv->cs_addr = (1 << chip) & ICE1712_6FIRE_AK4524_CS_MASK;
 	tmp |= ICE1712_6FIRE_SERIAL_DATA |
 		ICE1712_6FIRE_SERIAL_CLOCK |
 		ICE1712_6FIRE_RW;
 	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
 			  ice->gpio.direction | tmp);
 	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp);
-	return 0;
 }
 
-
 /*
  * CS8404 interface on EWS88MT/D
  */
@@ -328,6 +325,13 @@
 	.num_adcs = 8,
 	.num_dacs = 8,
 	.type = SND_AK4524,
+	.ops = {
+		.lock = ews88mt_ak4524_lock,
+		.unlock = ews88mt_ak4524_unlock
+	}
+};
+
+static struct snd_ak4xxx_private akm_ews88mt_priv __devinitdata = {
 	.caddr = 2,
 	.cif = 1, /* CIF high */
 	.data_mask = ICE1712_EWS88_SERIAL_DATA,
@@ -337,16 +341,18 @@
 	.cs_none = 0, /* no chip select on gpio */
 	.add_flags = ICE1712_EWS88_RW, /* set rw bit high */
 	.mask_flags = 0,
-	.ops = {
-		.start = ews88mt_ak4524_start,
-		.stop = ews88mt_ak4524_stop
-	}
 };
 
 static akm4xxx_t akm_ewx2496 __devinitdata = {
 	.num_adcs = 2,
 	.num_dacs = 2,
 	.type = SND_AK4524,
+	.ops = {
+		.lock = ewx2496_ak4524_lock
+	}
+};
+
+static struct snd_ak4xxx_private akm_ewx2496_priv __devinitdata = {
 	.caddr = 2,
 	.cif = 1, /* CIF high */
 	.data_mask = ICE1712_EWS88_SERIAL_DATA,
@@ -356,15 +362,18 @@
 	.cs_none = 0,
 	.add_flags = ICE1712_EWS88_RW, /* set rw bit high */
 	.mask_flags = 0,
-	.ops = {
-		.start = ewx2496_ak4524_start
-	}
 };
 
 static akm4xxx_t akm_6fire __devinitdata = {
 	.num_adcs = 6,
 	.num_dacs = 6,
 	.type = SND_AK4524,
+	.ops = {
+		.lock = dmx6fire_ak4524_lock
+	}
+};
+
+static struct snd_ak4xxx_private akm_6fire_priv __devinitdata = {
 	.caddr = 2,
 	.cif = 1, /* CIF high */
 	.data_mask = ICE1712_6FIRE_SERIAL_DATA,
@@ -374,12 +383,8 @@
 	.cs_none = 0,
 	.add_flags = ICE1712_6FIRE_RW, /* set rw bit high */
 	.mask_flags = 0,
-	.ops = {
-		.start = dmx6fire_ak4524_start
-	}
 };
 
-
 /*
  * initialize the chip
  */
@@ -479,17 +484,19 @@
 	switch (ice->eeprom.subvendor) {
 	case ICE1712_SUBDEVICE_EWS88MT:
 	case ICE1712_SUBDEVICE_EWS88MT_NEW:
-		snd_ice1712_akm4xxx_init(ak, &akm_ews88mt, ice);
+		err = snd_ice1712_akm4xxx_init(ak, &akm_ews88mt, &akm_ews88mt_priv, ice);
 		break;
 	case ICE1712_SUBDEVICE_EWX2496:
-		snd_ice1712_akm4xxx_init(ak, &akm_ewx2496, ice);
+		err = snd_ice1712_akm4xxx_init(ak, &akm_ewx2496, &akm_ewx2496_priv, ice);
 		break;
 	case ICE1712_SUBDEVICE_DMX6FIRE:
-		snd_ice1712_akm4xxx_init(ak, &akm_6fire, ice);
+		err = snd_ice1712_akm4xxx_init(ak, &akm_6fire, &akm_6fire_priv, ice);
 		break;
+	default:
+		err = 0;
 	}
 
-	return 0;
+	return err;
 }
 
 /*
@@ -602,7 +609,7 @@
 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;
+	int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	unsigned char data;
 
 	snd_assert(channel >= 0 && channel <= 7, return 0);
@@ -621,7 +628,7 @@
 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;
+	int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	unsigned char data, ndata;
 
 	snd_assert(channel >= 0 && channel <= 7, return 0);
@@ -645,6 +652,7 @@
 	.info = snd_ice1712_ewx_io_sense_info,
 	.get = snd_ice1712_ews88mt_input_sense_get,
 	.put = snd_ice1712_ews88mt_input_sense_put,
+	.count = 8,
 };
 
 static snd_kcontrol_new_t snd_ice1712_ews88mt_output_sense __devinitdata = {
@@ -894,12 +902,13 @@
 {
 	unsigned int idx;
 	int err;
-	snd_kcontrol_t *kctl;
 	
-	/* all terratec cards have spdif */
-	err = snd_ice1712_spdif_build_controls(ice);
-	if (err < 0)
-		return err;
+	/* all terratec cards have spdif, but cs8427 module builds it's own controls */
+	if (ice->cs8427 == NULL) {
+		err = snd_ice1712_spdif_build_controls(ice);
+		if (err < 0)
+			return err;
+	}
 
 	/* ak4524 controls */
 	switch (ice->eeprom.subvendor) {
@@ -924,13 +933,9 @@
 		break;
 	case ICE1712_SUBDEVICE_EWS88MT:
 	case ICE1712_SUBDEVICE_EWS88MT_NEW:
-		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_input_sense, ice));
+		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;
diff -urN linux-2.5.70-bk6/sound/pci/ice1712/ice1712.c linux-2.5.70-bk7/sound/pci/ice1712/ice1712.c
--- linux-2.5.70-bk6/sound/pci/ice1712/ice1712.c	2003-05-26 18:00:37.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ice1712/ice1712.c	2003-06-02 04:39:13.000000000 -0700
@@ -660,6 +660,8 @@
 	if (!(snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL) & 1))
 		return 0;
 	ptr = runtime->buffer_size - inw(ice->ddma_port + 4);
+	if (ptr == runtime->buffer_size)
+		ptr = 0;
 	return bytes_to_frames(substream->runtime, ptr);
 }
 
@@ -677,6 +679,8 @@
 		addr = ICE1712_DSC_ADDR0;
 	ptr = snd_ice1712_ds_read(ice, substream->number * 2, addr) -
 		ice->playback_con_virt_addr[substream->number];
+	if (ptr == substream->runtime->buffer_size)
+		ptr = 0;
 	return bytes_to_frames(substream->runtime, ptr);
 }
 
@@ -688,6 +692,8 @@
 	if (!(snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL) & 1))
 		return 0;
 	ptr = inl(ICEREG(ice, CONCAP_ADDR)) - ice->capture_con_virt_addr;
+	if (ptr == substream->runtime->buffer_size)
+		ptr = 0;
 	return bytes_to_frames(substream->runtime, ptr);
 }
 
@@ -965,9 +971,11 @@
 	{
 		unsigned int what = 0;
 		unsigned int old;
-		snd_pcm_substream_t *s = substream;
+		struct list_head *pos;
+		snd_pcm_substream_t *s;
 
-		do {
+		snd_pcm_group_for_each(pos, substream) {
+			s = snd_pcm_group_substream_entry(pos);
 			if (s == ice->playback_pro_substream) {
 				what |= ICE1712_PLAYBACK_START;
 				snd_pcm_trigger_done(s, substream);
@@ -975,8 +983,7 @@
 				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)
@@ -1097,6 +1104,8 @@
 	if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_PLAYBACK_START))
 		return 0;
 	ptr = ice->playback_pro_size - (inw(ICEMT(ice, PLAYBACK_SIZE)) << 2);
+	if (ptr == substream->runtime->buffer_size)
+		ptr = 0;
 	return bytes_to_frames(substream->runtime, ptr);
 }
 
@@ -1108,6 +1117,8 @@
 	if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_CAPTURE_START_SHADOW))
 		return 0;
 	ptr = ice->capture_pro_size - (inw(ICEMT(ice, CAPTURE_SIZE)) << 2);
+	if (ptr == substream->runtime->buffer_size)
+		ptr = 0;
 	return bytes_to_frames(substream->runtime, ptr);
 }
 
@@ -1299,7 +1310,7 @@
 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);
-	int index = kcontrol->private_value;
+	int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
 	
 	spin_lock_irq(&ice->reg_lock);
 	ucontrol->value.integer.value[0] = !((ice->pro_volumes[index] >> 15) & 1);
@@ -1311,7 +1322,7 @@
 static int snd_ice1712_pro_mixer_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
 {
 	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int index = kcontrol->private_value;
+	int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
 	unsigned int nval, change;
 
 	nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) |
@@ -1337,7 +1348,7 @@
 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);
-	int index = kcontrol->private_value;
+	int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
 	
 	spin_lock_irq(&ice->reg_lock);
 	ucontrol->value.integer.value[0] = (ice->pro_volumes[index] >> 0) & 127;
@@ -1349,7 +1360,7 @@
 static int snd_ice1712_pro_mixer_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
 {
 	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int index = kcontrol->private_value;
+	int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
 	unsigned int nval, change;
 
 	nval = (ucontrol->value.integer.value[0] & 127) |
@@ -1364,65 +1375,55 @@
 }
 
 
+static snd_kcontrol_new_t snd_ice1712_multi_ctrls[] __devinitdata = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Multi Playback Switch",
+		.info = snd_ice1712_pro_mixer_switch_info,
+		.get = snd_ice1712_pro_mixer_switch_get,
+		.put = snd_ice1712_pro_mixer_switch_put,
+		.private_value = 0,
+		.count = 10,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Multi Playback Volume",
+		.info = snd_ice1712_pro_mixer_volume_info,
+		.get = snd_ice1712_pro_mixer_volume_get,
+		.put = snd_ice1712_pro_mixer_volume_put,
+		.private_value = 0,
+		.count = 10,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Multi Capture Switch",
+		.info = snd_ice1712_pro_mixer_switch_info,
+		.get = snd_ice1712_pro_mixer_switch_get,
+		.put = snd_ice1712_pro_mixer_switch_put,
+		.private_value = 10,
+		.count = 10,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Multi Capture Volume",
+		.info = snd_ice1712_pro_mixer_volume_info,
+		.get = snd_ice1712_pro_mixer_volume_get,
+		.put = snd_ice1712_pro_mixer_volume_put,
+		.private_value = 10,
+		.count = 10,
+	},
+};
+
 static int __devinit 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.count = 1;
-		ctl.info = snd_ice1712_pro_mixer_switch_info;
-		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, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 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.count = 1;
-		ctl.info = snd_ice1712_pro_mixer_volume_info;
-		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, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
-			return err;
-	}
+	unsigned int idx;
+	int 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.count = 1;
-		ctl.info = snd_ice1712_pro_mixer_switch_info;
-		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, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 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.count = 1;
-		ctl.info = snd_ice1712_pro_mixer_volume_info;
-		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, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
+	/* multi-channel mixer */
+	for (idx = 0; idx < ARRAY_SIZE(snd_ice1712_multi_ctrls); idx++) {
+		err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_ctrls[idx], ice));
+		if (err < 0)
 			return err;
 	}
 	
@@ -1911,7 +1912,7 @@
 	
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
-	uinfo->value.enumerated.items = kcontrol->id.index < 2 ? 12 : 11;
+	uinfo->value.enumerated.items = snd_ctl_get_ioffidx(kcontrol, &uinfo->id) < 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]);
@@ -1921,7 +1922,7 @@
 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;
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	unsigned int val, cval;
 
 	spin_lock_irq(&ice->reg_lock);
@@ -1947,7 +1948,7 @@
 {
 	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
 	int change, shift;
-	int idx = kcontrol->id.index;
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	unsigned int val, old_val, nval;
 	
 	/* update PSDOUT */
@@ -1995,7 +1996,7 @@
 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;
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	unsigned int val, cval;
 	val = inw(ICEMT(ice, ROUTE_SPDOUT));
 	cval = (val >> (idx * 4 + 8)) & 0x0f;
@@ -2015,7 +2016,7 @@
 {
 	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
 	int change, shift;
-	int idx = kcontrol->id.index;
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	unsigned int val, old_val, nval;
 	
 	/* update SPDOUT */
@@ -2063,6 +2064,7 @@
 	.info = snd_ice1712_pro_route_info,
 	.get = snd_ice1712_pro_route_spdif_get,
 	.put = snd_ice1712_pro_route_spdif_put,
+	.count = 2,
 };
 
 
@@ -2153,7 +2155,7 @@
 static int __devinit snd_ice1712_read_eeprom(ice1712_t *ice)
 {
 	int dev = 0xa0;		/* EEPROM device address */
-	unsigned int i;
+	unsigned int i, size;
 
 	if ((inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_EEPROM) == 0) {
 		snd_printk("ICE1712 has not detected EEPROM\n");
@@ -2164,7 +2166,9 @@
 				(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 > 32) {
+	if (ice->eeprom.size < 6)
+		ice->eeprom.size = 32; /* FIXME: any cards without the correct size? */
+	else if (ice->eeprom.size > 32) {
 		snd_printk("invalid EEPROM (size = %i)\n", ice->eeprom.size);
 		return -EIO;
 	}
@@ -2173,7 +2177,8 @@
 		snd_printk("invalid EEPROM version %i\n", ice->eeprom.version);
 		/* return -EIO; */
 	}
-	for (i = 0; i < ice->eeprom.size; i++)
+	size = ice->eeprom.size - 6;
+	for (i = 0; i < size; i++)
 		ice->eeprom.data[i] = snd_ice1712_read_i2c(ice, dev, i + 6);
 
 	ice->eeprom.gpiomask = ice->eeprom.data[ICE_EEP1_GPIO_MASK];
@@ -2249,8 +2254,6 @@
 
 static int __devinit 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));
@@ -2267,25 +2270,17 @@
 	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 (ice->num_total_dacs > 0) {
+		snd_kcontrol_new_t tmp = snd_ice1712_mixer_pro_analog_route;
+		tmp.count = ice->num_total_dacs;
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&tmp, ice));
 		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_spdif_route, ice));
+	if (err < 0)
+		return err;
 
 	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_volume_rate, ice));
 	if (err < 0)
@@ -2326,8 +2321,7 @@
 		release_resource(ice->res_profi_port);
 		kfree_nocheck(ice->res_profi_port);
 	}
-	if (ice->akm)
-		kfree(ice->akm);
+	snd_ice1712_akm4xxx_free(ice);
 	snd_magic_kfree(ice);
 	return 0;
 }
diff -urN linux-2.5.70-bk6/sound/pci/ice1712/ice1712.h linux-2.5.70-bk7/sound/pci/ice1712/ice1712.h
--- linux-2.5.70-bk6/sound/pci/ice1712/ice1712.h	2003-05-26 18:00:21.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ice1712/ice1712.h	2003-06-02 04:39:13.000000000 -0700
@@ -26,6 +26,7 @@
 #include <sound/ac97_codec.h>
 #include <sound/rawmidi.h>
 #include <sound/i2c.h>
+#include <sound/ak4xxx-adda.h>
 #include <sound/pcm.h>
 
 
@@ -214,7 +215,6 @@
  */
 
 typedef struct _snd_ice1712 ice1712_t;
-typedef struct snd_ak4xxx akm4xxx_t;
 
 typedef struct {
 	unsigned int subvendor;	/* PCI[2c-2f] */
@@ -254,17 +254,7 @@
 #define ice_has_con_ac97(ice)	(!((ice)->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97))
 
 
-struct snd_ak4xxx {
-	unsigned int num_adcs;		/* AK4524 or AK4528 ADCs */
-	unsigned int num_dacs;		/* AK4524 or AK4528 DACs */
-	unsigned char images[4][16];	/* saved register image */
-	unsigned char ipga_gain[4][2];	/* saved register image for IPGA (AK4528) */
-	ice1712_t *chip;
-	/* template should fill the following fields */
-	unsigned int idx_offset;	/* control index offset */
-	enum {
-		SND_AK4524, SND_AK4528, SND_AK4529, SND_AK4355, SND_AK4381
-	} type;
+struct snd_ak4xxx_private {
 	unsigned int cif: 1;		/* CIF mode */
 	unsigned char caddr;		/* C0 and C1 bits */
 	unsigned int data_mask;		/* DATA gpio bit */
@@ -275,8 +265,6 @@
 	unsigned int add_flags;		/* additional bits at init */
 	unsigned int mask_flags;	/* total mask bits */
 	struct snd_akm4xxx_ops {
-		int (*start)(akm4xxx_t *ak, int chip);
-		void (*stop)(akm4xxx_t *ak);
 		void (*set_rate_val)(akm4xxx_t *ak, unsigned int rate);
 	} ops;
 };
@@ -439,9 +427,8 @@
 
 int snd_ice1712_spdif_build_controls(ice1712_t *ice);
 
-void snd_ice1712_akm4xxx_write(akm4xxx_t *ice, int chip, unsigned char addr, unsigned char data);
-void snd_ice1712_akm4xxx_reset(akm4xxx_t *ice, int state);
-void snd_ice1712_akm4xxx_init(akm4xxx_t *ak, const akm4xxx_t *template, ice1712_t *ice);
+int snd_ice1712_akm4xxx_init(akm4xxx_t *ak, const akm4xxx_t *template, const struct snd_ak4xxx_private *priv, ice1712_t *ice);
+void snd_ice1712_akm4xxx_free(ice1712_t *ice);
 int snd_ice1712_akm4xxx_build_controls(ice1712_t *ice);
 
 int snd_ice1712_init_cs8427(ice1712_t *ice, int addr);
@@ -469,6 +456,8 @@
 	int (*chip_init)(ice1712_t *);
 	int (*build_controls)(ice1712_t *);
 	int no_mpu401: 1;
+	unsigned int eeprom_size;
+	unsigned char *eeprom_data;
 };
 
 
diff -urN linux-2.5.70-bk6/sound/pci/ice1712/ice1724.c linux-2.5.70-bk7/sound/pci/ice1712/ice1724.c
--- linux-2.5.70-bk6/sound/pci/ice1712/ice1724.c	2003-05-26 18:00:39.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ice1712/ice1724.c	2003-06-02 04:39:13.000000000 -0700
@@ -116,8 +116,9 @@
 			continue;
 		if (!(old_cmd & VT1724_AC97_READY))
 			continue;
-		return 0;
+		return old_cmd;
 	}
+	snd_printd(KERN_ERR "snd_vt1724_ac97_ready: timeout\n");
 	return old_cmd;
 }
 
@@ -127,6 +128,7 @@
 	for (tm = 0; tm < 0x10000; tm++)
 		if ((inb(ICEMT1724(ice, AC97_CMD)) & bit) == 0)
 			return 0;
+	snd_printd(KERN_ERR "snd_vt1724_ac97_wait_bit: timeout\n");
 	return -EIO;
 }
 
@@ -138,9 +140,10 @@
 	unsigned char old_cmd;
 
 	old_cmd = snd_vt1724_ac97_ready(ice);
+	old_cmd &= ~VT1724_AC97_ID_MASK;
+	old_cmd |= ac97->num;
 	outb(reg, ICEMT1724(ice, AC97_INDEX));
 	outw(val, ICEMT1724(ice, AC97_DATA));
-	old_cmd &= ~(VT1724_AC97_PBK_VSR | VT1724_AC97_CAP_VSR);
 	outb(old_cmd | VT1724_AC97_WRITE, ICEMT1724(ice, AC97_CMD));
 	snd_vt1724_ac97_wait_bit(ice, VT1724_AC97_WRITE);
 }
@@ -151,6 +154,8 @@
 	unsigned char old_cmd;
 
 	old_cmd = snd_vt1724_ac97_ready(ice);
+	old_cmd &= ~VT1724_AC97_ID_MASK;
+	old_cmd |= ac97->num;
 	outb(reg, ICEMT1724(ice, AC97_INDEX));
 	outb(old_cmd | VT1724_AC97_READ, ICEMT1724(ice, AC97_CMD));
 	if (snd_vt1724_ac97_wait_bit(ice, VT1724_AC97_READ) < 0)
@@ -284,14 +289,15 @@
 	ice1712_t *ice = snd_pcm_substream_chip(substream);
 	unsigned int what;
 	unsigned int old;
+	struct list_head *pos;
 	snd_pcm_substream_t *s;
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		what = 0;
-		s = substream;
-		do {
+		snd_pcm_group_for_each(pos, substream) {
+			s = snd_pcm_group_substream_entry(pos);
 			if (s == ice->playback_pro_substream)
 				what |= VT1724_PDMA0_PAUSE;
 			else if (s == ice->capture_pro_substream)
@@ -300,8 +306,7 @@
 				what |= VT1724_PDMA4_PAUSE;
 			else if (s == ice->capture_con_substream)
 				what |= VT1724_RDMA1_PAUSE;
-			s = s->link_next;
-		} while (s != substream);
+		}
 		spin_lock(&ice->reg_lock);
 		old = inl(ICEMT1724(ice, DMA_PAUSE));
 		if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
@@ -316,7 +321,8 @@
 	case SNDRV_PCM_TRIGGER_STOP:
 		what = 0;
 		s = substream;
-		do {
+		snd_pcm_group_for_each(pos, substream) {
+			s = snd_pcm_group_substream_entry(pos);
 			if (s == ice->playback_pro_substream) {
 				what |= VT1724_PDMA0_START;
 				snd_pcm_trigger_done(s, substream);
@@ -330,8 +336,7 @@
 				what |= VT1724_RDMA1_START;
 				snd_pcm_trigger_done(s, substream);
 			}
-			s = s->link_next;
-		} while (s != substream);
+		}
 		spin_lock(&ice->reg_lock);
 		old = inl(ICEMT1724(ice, DMA_CONTROL));
 		if (cmd == SNDRV_PCM_TRIGGER_START)
@@ -878,6 +883,11 @@
 
 	if (! (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S)) {
 		ac97_t ac97;
+		/* cold reset */
+		outb(inb(ICEMT1724(ice, AC97_CMD)) | 0x80, ICEMT1724(ice, AC97_CMD));
+		mdelay(5); /* FIXME */
+		outb(inb(ICEMT1724(ice, AC97_CMD)) & ~0x80, ICEMT1724(ice, AC97_CMD));
+
 		memset(&ac97, 0, sizeof(ac97));
 		ac97.write = snd_vt1724_ac97_write;
 		ac97.read = snd_vt1724_ac97_read;
@@ -1297,7 +1307,7 @@
 	return change;
 }
 
-static snd_kcontrol_new_t snd_vt1724_pro_internal_clock = __devinitdata {
+static snd_kcontrol_new_t snd_vt1724_pro_internal_clock __devinitdata = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "Multi Track Internal Clock",
 	.info = snd_vt1724_pro_internal_clock_info,
@@ -1452,7 +1462,7 @@
 static int snd_vt1724_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;
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	ucontrol->value.enumerated.item[0] = get_route_val(ice, analog_route_shift(idx));
 	return 0;
 }
@@ -1460,7 +1470,7 @@
 static int snd_vt1724_pro_route_analog_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
 	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int idx = kcontrol->id.index;
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	return put_route_val(ice, ucontrol->value.enumerated.item[0],
 			     analog_route_shift(idx));
 }
@@ -1468,7 +1478,7 @@
 static int snd_vt1724_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;
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	ucontrol->value.enumerated.item[0] = get_route_val(ice, digital_route_shift(idx));
 	return 0;
 }
@@ -1476,7 +1486,7 @@
 static int snd_vt1724_pro_route_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
 	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int idx = kcontrol->id.index;
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	return put_route_val(ice, ucontrol->value.enumerated.item[0],
 			     digital_route_shift(idx));
 }
@@ -1495,6 +1505,7 @@
 	.info = snd_vt1724_pro_route_info,
 	.get = snd_vt1724_pro_route_spdif_get,
 	.put = snd_vt1724_pro_route_spdif_put,
+	.count = 2,
 };
 
 
@@ -1533,6 +1544,18 @@
  *
  */
 
+static struct snd_ice1712_card_info no_matched __devinitdata;
+
+static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
+	snd_vt1724_revo_cards,
+	snd_vt1724_amp_cards, 
+	0,
+};
+
+
+/*
+ */
+
 static unsigned char __devinit snd_vt1724_read_i2c(ice1712_t *ice,
 						 unsigned char dev,
 						 unsigned char addr)
@@ -1548,7 +1571,7 @@
 static int __devinit snd_vt1724_read_eeprom(ice1712_t *ice)
 {
 	int dev = 0xa0;		/* EEPROM device address */
-	unsigned int i;
+	unsigned int i, size;
 
 	if ((inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_EEPROM) == 0) {
 		snd_printk("ICE1724 has not detected EEPROM\n");
@@ -1559,7 +1582,9 @@
 				(snd_vt1724_read_i2c(ice, dev, 0x02) << 16) | 
 				(snd_vt1724_read_i2c(ice, dev, 0x03) << 24);
 	ice->eeprom.size = snd_vt1724_read_i2c(ice, dev, 0x04);
-	if (ice->eeprom.size > 32) {
+	if (ice->eeprom.size < 6)
+		ice->eeprom.size = 32;
+	else if (ice->eeprom.size > 32) {
 		snd_printk("invalid EEPROM (size = %i)\n", ice->eeprom.size);
 		return -EIO;
 	}
@@ -1568,7 +1593,8 @@
 		snd_printk("invalid EEPROM version %i\n", ice->eeprom.version);
 		// return -EIO;
 	}
-	for (i = 0; i < ice->eeprom.size; i++)
+	size = ice->eeprom.size - 6;
+	for (i = 0; i < size; i++)
 		ice->eeprom.data[i] = snd_vt1724_read_i2c(ice, dev, i + 6);
 
 	ice->eeprom.gpiomask = eeprom_triple(ice, ICE_EEP2_GPIO_MASK);
@@ -1605,20 +1631,13 @@
 static int __devinit snd_vt1724_spdif_build_controls(ice1712_t *ice)
 {
 	int err;
-	unsigned int idx;
 	snd_kcontrol_t *kctl;
 
 	snd_assert(ice->pcm != NULL, return -EIO);
 
-	for (idx = 0; idx < 2; idx++) {
-		kctl = snd_ctl_new1(&snd_vt1724_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_vt1724_mixer_pro_spdif_route, ice));
+	if (err < 0)
+		return err;
 
 	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_spdif_switch, ice));
 	if (err < 0)
@@ -1649,8 +1668,6 @@
 
 static int __devinit snd_vt1724_build_controls(ice1712_t *ice)
 {
-	unsigned int idx;
-	snd_kcontrol_t *kctl;
 	int err;
 
 	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_eeprom, ice));
@@ -1667,12 +1684,10 @@
 	if (err < 0)
 		return err;
 
-	for (idx = 0; idx < ice->num_total_dacs; idx++) {
-		kctl = snd_ctl_new1(&snd_vt1724_mixer_pro_analog_route, ice);
-		if (kctl == NULL)
-			return -ENOMEM;
-		kctl->id.index = idx;
-		err = snd_ctl_add(ice->card, kctl);
+	if (ice->num_total_dacs > 0) {
+		snd_kcontrol_new_t tmp = snd_vt1724_mixer_pro_analog_route;
+		tmp.count = ice->num_total_dacs;
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&tmp, ice));
 		if (err < 0)
 			return err;
 	}
@@ -1705,8 +1720,7 @@
 		release_resource(ice->res_profi_port);
 		kfree_nocheck(ice->res_profi_port);
 	}
-	if (ice->akm)
-		kfree(ice->akm);
+	snd_ice1712_akm4xxx_free(ice);
 	snd_magic_kfree(ice);
 	return 0;
 }
@@ -1808,15 +1822,6 @@
  *
  */
 
-static struct snd_ice1712_card_info no_matched __devinitdata;
-
-static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
-	snd_vt1724_revo_cards,
-	snd_vt1724_amp_cards, 
-	0,
-};
-
-
 static int __devinit snd_vt1724_probe(struct pci_dev *pci,
 				      const struct pci_device_id *pci_id)
 {
diff -urN linux-2.5.70-bk6/sound/pci/ice1712/revo.c linux-2.5.70-bk7/sound/pci/ice1712/revo.c
--- linux-2.5.70-bk6/sound/pci/ice1712/revo.c	2003-05-26 18:00:41.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ice1712/revo.c	2003-06-02 04:39:13.000000000 -0700
@@ -65,12 +65,12 @@
 		return;
 
 	/* reset DFS */
-	snd_ice1712_akm4xxx_reset(ak, 1);
+	snd_akm4xxx_reset(ak, 1);
 	tmp = ak->images[0][reg];
 	tmp &= ~(0x03 << shift);
 	tmp |= dfs << shift;
-	snd_ice1712_akm4xxx_write(ak, 0, reg, tmp);
-	snd_ice1712_akm4xxx_reset(ak, 0);
+	snd_akm4xxx_write(ak, 0, reg, tmp);
+	snd_akm4xxx_reset(ak, 0);
 }
 
 /*
@@ -80,6 +80,12 @@
 static akm4xxx_t akm_revo_front __devinitdata = {
 	.type = SND_AK4381,
 	.num_dacs = 2,
+	.ops = {
+		.set_rate_val = revo_set_rate_val
+	}
+};
+
+static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = {
 	.caddr = 1,
 	.cif = 0,
 	.data_mask = VT1724_REVO_CDOUT,
@@ -89,15 +95,18 @@
 	.cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
 	.add_flags = VT1724_REVO_CCLK, /* high at init */
 	.mask_flags = 0,
-	.ops = {
-		.set_rate_val = revo_set_rate_val
-	}
 };
 
 static akm4xxx_t akm_revo_surround __devinitdata = {
 	.type = SND_AK4355,
 	.idx_offset = 1,
 	.num_dacs = 6,
+	.ops = {
+		.set_rate_val = revo_set_rate_val
+	}
+};
+
+static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
 	.caddr = 3,
 	.cif = 0,
 	.data_mask = VT1724_REVO_CDOUT,
@@ -107,9 +116,6 @@
 	.cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
 	.add_flags = VT1724_REVO_CCLK, /* high at init */
 	.mask_flags = 0,
-	.ops = {
-		.set_rate_val = revo_set_rate_val
-	}
 };
 
 static int __devinit revo_init(ice1712_t *ice)
@@ -133,8 +139,8 @@
 	ice->akm_codecs = 2;
 	switch (ice->eeprom.subvendor) {
 	case VT1724_SUBDEVICE_REVOLUTION71:
-		snd_ice1712_akm4xxx_init(ak, &akm_revo_front, ice);
-		snd_ice1712_akm4xxx_init(ak + 1, &akm_revo_surround, ice);
+		snd_ice1712_akm4xxx_init(ak, &akm_revo_front, &akm_revo_front_priv, ice);
+		snd_ice1712_akm4xxx_init(ak + 1, &akm_revo_surround, &akm_revo_surround_priv, ice);
 		/* unmute all codecs */
 		snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE);
 		break;
diff -urN linux-2.5.70-bk6/sound/pci/intel8x0.c linux-2.5.70-bk7/sound/pci/intel8x0.c
--- linux-2.5.70-bk6/sound/pci/intel8x0.c	2003-05-26 18:00:41.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/intel8x0.c	2003-06-02 04:39:13.000000000 -0700
@@ -53,8 +53,8 @@
 		"{Intel,ICH5},"
 		"{Intel,MX440},"
 		"{SiS,SI7012},"
-		"{NVidia,NForce Audio},"
-		"{NVidia,NForce2 Audio},"
+		"{NVidia,nForce Audio},"
+		"{NVidia,nForce2 Audio},"
 		"{AMD,AMD768},"
 		"{AMD,AMD8111},"
 	        "{ALI,M5455}}");
@@ -139,7 +139,7 @@
 #define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO	0x00da
 #endif
 
-enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI };
+enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI, DEVICE_NFORCE };
 
 #define ICHREG(x) ICH_REG_##x
 
@@ -228,6 +228,7 @@
 #define   ICH_MCINT		0x00000080	/* MIC capture interrupt */
 #define   ICH_POINT		0x00000040	/* playback interrupt */
 #define   ICH_PIINT		0x00000020	/* capture interrupt */
+#define   ICH_NVSPINT		0x00000010	/* nforce spdif interrupt */
 #define   ICH_MOINT		0x00000004	/* modem playback interrupt */
 #define   ICH_MIINT		0x00000002	/* modem capture interrupt */
 #define   ICH_GSCI		0x00000001	/* GPI status change interrupt */
@@ -266,7 +267,9 @@
 	ICH_REG_ALI_INTERRUPTSR = 0x18,	/* Interrupt  Status Register */
 	ICH_REG_ALI_FIFOCR2 = 0x1c,	/* FIFO Control Register 2   */
 	ICH_REG_ALI_CPR = 0x20,		/* Command Port Register     */
+	ICH_REG_ALI_CPR_ADDR = 0x22,	/* ac97 addr write */
 	ICH_REG_ALI_SPR = 0x24,		/* Status Port Register      */
+	ICH_REG_ALI_SPR_ADDR = 0x26,	/* ac97 addr read */
 	ICH_REG_ALI_FIFOCR3 = 0x2c,	/* FIFO Control Register 3  */
 	ICH_REG_ALI_TTSR = 0x30,	/* Transmit Tag Slot Register */
 	ICH_REG_ALI_RTSR = 0x34,	/* Receive Tag Slot  Register */
@@ -276,6 +279,12 @@
 	ICH_REG_ALI_SPDIFICS = 0xfc	/* spdif interface control/status  */
 };
 
+#define ALI_CAS_SEM_BUSY	0x80000000
+#define ALI_CSPSR_CODEC_READY	0x08
+#define ALI_CPR_ADDR_READ	0x80
+#define ALI_CSPSR_READ_OK	0x02
+#define ALI_CSPSR_WRITE_OK	0x01
+
 /* interrupts for the whole chip by interrupt status register finish */
  
 #define ALI_INT_SPDIFOUT	(1<<23)	/* controller spdif out INTERRUPT */
@@ -298,6 +307,7 @@
  */
 
 enum { ICHD_PCMIN, ICHD_PCMOUT, ICHD_MIC, ICHD_MIC2, ICHD_PCM2IN, ICHD_SPBAR, ICHD_LAST = ICHD_SPBAR };
+enum { NVD_PCMIN, NVD_PCMOUT, NVD_MIC, NVD_SPBAR, NVD_LAST = NVD_SPBAR };
 enum { ALID_PCMIN, ALID_PCMOUT, ALID_MIC, ALID_AC97SPDIFOUT, ALID_SPDIFIN, ALID_SPDIFOUT, ALID_LAST = ALID_SPDIFOUT };
 
 #define get_ichdev(substream) (ichdev_t *)(substream->runtime->private_data)
@@ -355,12 +365,8 @@
 	struct pci_dev *pci;
 	snd_card_t *card;
 
-	snd_pcm_t *pcm;
-	snd_pcm_t *pcm_mic;
-	snd_pcm_t *pcm_mic2;
-	snd_pcm_t *pcm2;
-	snd_pcm_t *pcm_spdif;
-	snd_pcm_t *pcm_ac97spdif;
+	int pcm_devs;
+	snd_pcm_t *pcm[6];
 	ichdev_t ichd[6];
 
 	int multi4: 1,
@@ -382,6 +388,7 @@
 	dma_addr_t bdbars_addr;
 	u32 int_sta_reg;		/* interrupt status register */
 	u32 int_sta_mask;		/* interrupt status mask */
+	unsigned int pcm_pos_shift;
 	
 #ifdef CONFIG_PM
 	int in_suspend;
@@ -397,9 +404,9 @@
 	{ 0x8086, 0x24d5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH5 */
 	{ 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 */
-	{ 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* NFORCE2 */
-	{ 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* NFORCE3 */
+	{ 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },	/* NFORCE */
+	{ 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },	/* NFORCE2 */
+	{ 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },	/* NFORCE3 */
 	{ 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* AMD8111 */
 	{ 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* AMD768 */
 	{ 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI },   /* Ali5455 */
@@ -594,13 +601,9 @@
 static int snd_intel8x0_ali_codec_semaphore(intel8x0_t *chip)
 {
 	int time = 100;
-	do {
-		if (igetdword(chip, ICHREG(ALI_CAS)) & 0x80000000)
-			return snd_intel8x0_ali_codec_ready(chip, 0x08);
+	while (time-- && (igetdword(chip, ICHREG(ALI_CAS)) & ALI_CAS_SEM_BUSY))
 		udelay(1);
-	} while (time--);
-	snd_printk(KERN_WARNING "intel8x0: AC97 codec semaphore timeout.\n");
-	return -EBUSY;
+	return snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_CODEC_READY);
 }
 
 static unsigned short snd_intel8x0_ali_codec_read(ac97_t *ac97, unsigned short reg)
@@ -611,15 +614,14 @@
 	spin_lock(&chip->ac97_lock);
 	if (snd_intel8x0_ali_codec_semaphore(chip))
 		goto __err;
-	reg |= 0x0080;
-	iputword(chip, ICHREG(ALI_CPR) + 2, reg | 0x0080);
-	if (snd_intel8x0_ali_codec_ready(chip, 0x02))
+	iputword(chip, ICHREG(ALI_CPR_ADDR), reg | ALI_CPR_ADDR_READ);
+	if (snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_READ_OK))
 		goto __err;
 	data = igetword(chip, ICHREG(ALI_SPR));
-	reg2 = igetword(chip, ICHREG(ALI_SPR) + 2);
+	reg2 = igetword(chip, ICHREG(ALI_SPR_ADDR));
 	if (reg != reg2) {
-		snd_printd(KERN_WARNING "intel8x0: AC97 read not completed?\n");
-		goto __err;
+		snd_printd(KERN_WARNING "intel8x0: AC97 read not completed? 0x%x != 0x%x\n", reg, reg2);
+		// goto __err;
 	}
 	spin_unlock(&chip->ac97_lock);
 	return data;
@@ -638,8 +640,8 @@
 		return;
 	}
 	iputword(chip, ICHREG(ALI_CPR), val);
-	iputbyte(chip, ICHREG(ALI_CPR) + 2, reg);
-	snd_intel8x0_ali_codec_ready(chip, 0x01);
+	iputbyte(chip, ICHREG(ALI_CPR_ADDR), reg);
+	snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_WRITE_OK);
 	spin_unlock(&chip->ac97_lock);
 }
 
@@ -652,7 +654,6 @@
 	int idx;
 	u32 *bdbar = ichdev->bdbar;
 	unsigned long port = ichdev->reg_offset;
-	int shiftlen = (chip->device_type == DEVICE_SIS) ? 0 : 1;
 
 	iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
 	if (ichdev->size == ichdev->fragsize) {
@@ -661,10 +662,10 @@
 		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);
+						     ichdev->fragsize1 >> chip->pcm_pos_shift);
 			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->fragsize1 >> chip->pcm_pos_shift);
 		}
 		ichdev->frags = 2;
 	} else {
@@ -673,7 +674,7 @@
 		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);
+						     ichdev->fragsize >> chip->pcm_pos_shift);
 			// printk("bdbar[%i] = 0x%x [0x%x]\n", idx + 0, bdbar[idx + 0], bdbar[idx + 1]);
 		}
 		ichdev->frags = ichdev->size / ichdev->fragsize;
@@ -698,7 +699,6 @@
 	unsigned long port = ichdev->reg_offset;
 	int ack = 0;
 
-	spin_lock(&chip->reg_lock);
 	ichdev->position += ichdev->fragsize1;
 	ichdev->position %= ichdev->size;
 	ichdev->lvi++;
@@ -710,9 +710,11 @@
 	// 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_OFF_PIV + port), inl(port + 4), inb(port + ICH_REG_OFF_CR));
 	if ((ack = (--ichdev->ack == 0)) != 0)
 		ichdev->ack = ichdev->ack_reload;
-	spin_unlock(&chip->reg_lock);
-	if (ack && ichdev->substream)
+	if (ack && ichdev->substream) {
+		spin_unlock(&chip->reg_lock);
 		snd_pcm_period_elapsed(ichdev->substream);
+		spin_lock(&chip->reg_lock);
+	}
 	iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
 }
 
@@ -729,15 +731,17 @@
 		spin_unlock(&chip->reg_lock);
 		return IRQ_NONE;
 	}
-	/* ack first */
-	iputdword(chip, chip->int_sta_reg, status & ~chip->int_sta_mask);
-	spin_unlock(&chip->reg_lock);
 
 	for (i = 0; i < chip->bdbars_count; i++) {
 		ichdev = &chip->ichd[i];
 		if (status & ichdev->int_sta_mask)
 			snd_intel8x0_update(chip, ichdev);
 	}
+
+	/* ack them */
+	iputdword(chip, chip->int_sta_reg, status & chip->int_sta_mask);
+	spin_unlock(&chip->reg_lock);
+	
 	return IRQ_HANDLED;
 }
 
@@ -861,9 +865,11 @@
 		snd_intel8x0_setup_multi_channels(chip, runtime->channels);
 		spin_unlock(&chip->reg_lock);
 	}
-	for (i = 0; i < 3; i++)
-		if (ichdev->ac97_rate_regs[i])
-			snd_ac97_set_rate(ichdev->ac97, ichdev->ac97_rate_regs[i], runtime->rate);
+	if (ichdev->ac97) {
+		for (i = 0; i < 3; i++)
+			if (ichdev->ac97_rate_regs[i])
+				snd_ac97_set_rate(ichdev->ac97, ichdev->ac97_rate_regs[i], runtime->rate);
+	}
 	snd_intel8x0_setup_periods(chip, ichdev);
 	return 0;
 }
@@ -875,10 +881,7 @@
 	size_t ptr;
 
 	ptr = ichdev->fragsize1;
-	if (chip->device_type == DEVICE_SIS)
-		ptr -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
-	else
-		ptr -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << 1;
+	ptr -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << chip->pcm_pos_shift;
 	ptr += ichdev->position;
 	return bytes_to_frames(substream->runtime, ptr);
 }
@@ -936,7 +939,7 @@
 
 	ichdev->substream = substream;
 	runtime->hw = snd_intel8x0_stream;
-	if (ichdev->ac97_rates_idx >= 0)
+	if (ichdev->ac97 && ichdev->ac97_rates_idx >= 0)
 		runtime->hw.rates = ichdev->ac97->rates[ichdev->ac97_rates_idx];
 	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
 		runtime->hw.rate_min = 48000;
@@ -1038,15 +1041,17 @@
 static int snd_intel8x0_spdif_open(snd_pcm_substream_t * substream)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	int idx = chip->device_type == DEVICE_NFORCE ? NVD_SPBAR : ICHD_SPBAR;
 
-	return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_SPBAR]);
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[idx]);
 }
 
 static int snd_intel8x0_spdif_close(snd_pcm_substream_t * substream)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	int idx = chip->device_type == DEVICE_NFORCE ? NVD_SPBAR : ICHD_SPBAR;
 
-	chip->ichd[ICHD_SPBAR].substream = NULL;
+	chip->ichd[idx].substream = NULL;
 	return 0;
 }
 
@@ -1227,270 +1232,182 @@
 	.pointer =	snd_intel8x0_pcm_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;
-
-	if (chip->device_type == DEVICE_ALI) {
-		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_playback_ops);
-		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_ali_capture_ops);
-	} else {
-		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 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, 0, 1, &pcm);
-	if (err < 0)
-		return err;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
-			chip->device_type == DEVICE_ALI ?
-				&snd_intel8x0_ali_capture_mic_ops :
-				&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;	
-
-	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 0, 128*1024);
-
-	if (rpcm)
-		*rpcm = pcm;
-	return 0;
-}
-
-/*
- *  PCM code - MIC2
- */
-
-static void snd_intel8x0_pcm_mic2_free(snd_pcm_t *pcm)
-{
-	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
-	chip->pcm_mic2 = NULL;
-}
-
-static int __devinit snd_intel8x0_pcm_mic2(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 - MIC2 ADC", device, 0, 1, &pcm);
-	if (err < 0)
-		return err;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_mic2_ops);
-
-	pcm->private_data = chip;
-	pcm->private_free = snd_intel8x0_pcm_mic2_free;
-	pcm->info_flags = 0;
-	sprintf(pcm->name, "%s - MIC2 ADC", chip->card->shortname);
-
-	chip->pcm_mic2 = pcm;	
-
-	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 0, 128*1024);
-
-	if (rpcm)
-		*rpcm = pcm;
-	return 0;
-}
-
-/*
- *  PCM code - capture2
- */
-
-static void snd_intel8x0_pcm_capture2_free(snd_pcm_t *pcm)
-{
-	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
-	chip->pcm2 = NULL;
-}
-
-static int __devinit snd_intel8x0_pcm_capture2(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 - ADC2", device, 0, 1, &pcm);
-	if (err < 0)
-		return err;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture2_ops);
-
-	pcm->private_data = chip;
-	pcm->private_free = snd_intel8x0_pcm_capture2_free;
-	pcm->info_flags = 0;
-	sprintf(pcm->name, "%s - ADC2", chip->card->shortname);
-
-	chip->pcm2 = pcm;	
-
-	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 0, 128*1024);
-
-	if (rpcm)
-		*rpcm = pcm;
-	return 0;
-}
-
-/*
- *  PCM code - S/PDIF
- */
-
-static void snd_intel8x0_pcm_spdif_free(snd_pcm_t *pcm)
-{
-	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
-	chip->pcm_spdif = NULL;
-}
-
-static int __devinit snd_intel8x0_pcm_spdif(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 - IEC958", device, 1, 0, &pcm);
-	if (err < 0)
-		return err;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_spdif_ops);
-
-	pcm->private_data = chip;
-	pcm->private_free = snd_intel8x0_pcm_spdif_free;
-	pcm->info_flags = 0;
-	sprintf(pcm->name, "%s - IEC958", chip->card->shortname);
-
-	chip->pcm_spdif = pcm;	
-
-	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024);
-
-	if (rpcm)
-		*rpcm = pcm;
-	return 0;
-}
-
-/*
- *  PCM code - ALI S/PDIF
- */
-
-static void snd_intel8x0_ali_spdif_free(snd_pcm_t *pcm)
-{
-	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
-	chip->pcm_spdif = NULL;
-}
+struct ich_pcm_table {
+	char *suffix;
+	snd_pcm_ops_t *playback_ops;
+	snd_pcm_ops_t *capture_ops;
+	size_t prealloc_size;
+	size_t prealloc_max_size;
+	int ac97_idx;
+};
 
-static int __devinit snd_intel8x0_ali_spdif(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
+static int __devinit snd_intel8x0_pcm1(intel8x0_t *chip, int device, struct ich_pcm_table *rec)
 {
 	snd_pcm_t *pcm;
 	int err;
+	char name[32];
 
-	if (rpcm)
-		*rpcm = NULL;
-	err = snd_pcm_new(chip->card, "Intel ICH - IEC958", device, 1, 1, &pcm);
+	if (rec->suffix)
+		sprintf(name, "Intel ICH - %s", rec->suffix);
+	else
+		strcpy(name, "Intel ICH");
+	err = snd_pcm_new(chip->card, name, device,
+			  rec->playback_ops ? 1 : 0,
+			  rec->capture_ops ? 1 : 0, &pcm);
 	if (err < 0)
 		return err;
 
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_spdifout_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_ali_spdifin_ops);
+	if (rec->playback_ops)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, rec->playback_ops);
+	if (rec->capture_ops)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, rec->capture_ops);
 
 	pcm->private_data = chip;
-	pcm->private_free = snd_intel8x0_ali_spdif_free;
 	pcm->info_flags = 0;
-	sprintf(pcm->name, "%s - IEC958", chip->card->shortname);
-
-	chip->pcm_spdif = pcm;	
+	if (rec->suffix)
+		sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix);
+	else
+		strcpy(pcm->name, chip->card->shortname);
+	chip->pcm[device] = pcm;
 
-	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024);
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, rec->prealloc_size,
+						  rec->prealloc_max_size);
 
-	if (rpcm)
-		*rpcm = pcm;
 	return 0;
 }
 
-/*
- *  PCM code - ALI AC'97 S/PDIF
- */
-
-static void snd_intel8x0_ali_ac97spdif_free(snd_pcm_t *pcm)
-{
-	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
-	chip->pcm_ac97spdif = NULL;
-}
-
-static int __devinit snd_intel8x0_ali_ac97spdif(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, "ALI - AC97 IEC958", device, 1, 0, &pcm);
-	if (err < 0)
-		return err;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_ac97spdifout_ops);
-
-	pcm->private_data = chip;
-	pcm->private_free = snd_intel8x0_ali_ac97spdif_free;
-	pcm->info_flags = 0;
-	sprintf(pcm->name, "%s - AC97 IEC958", chip->card->shortname);
-
-	chip->pcm_ac97spdif = pcm;	
+static struct ich_pcm_table intel_pcms[] __devinitdata = {
+	{
+		.playback_ops = &snd_intel8x0_playback_ops,
+		.capture_ops = &snd_intel8x0_capture_ops,
+		.prealloc_size = 64 * 1024,
+		.prealloc_max_size = 128 * 1024,
+	},
+	{
+		.suffix = "MIC ADC",
+		.capture_ops = &snd_intel8x0_capture_mic_ops,
+		.prealloc_size = 0,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = ICHD_MIC,
+	},
+	{
+		.suffix = "MIC2 ADC",
+		.capture_ops = &snd_intel8x0_capture_mic2_ops,
+		.prealloc_size = 0,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = ICHD_MIC2,
+	},
+	{
+		.suffix = "ADC2",
+		.capture_ops = &snd_intel8x0_capture2_ops,
+		.prealloc_size = 0,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = ICHD_PCM2IN,
+	},
+	{
+		.suffix = "IEC958",
+		.playback_ops = &snd_intel8x0_spdif_ops,
+		.prealloc_size = 64 * 1024,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = ICHD_SPBAR,
+	},
+};
+
+static struct ich_pcm_table nforce_pcms[] __devinitdata = {
+	{
+		.playback_ops = &snd_intel8x0_playback_ops,
+		.capture_ops = &snd_intel8x0_capture_ops,
+		.prealloc_size = 64 * 1024,
+		.prealloc_max_size = 128 * 1024,
+	},
+	{
+		.suffix = "MIC ADC",
+		.capture_ops = &snd_intel8x0_capture_mic_ops,
+		.prealloc_size = 0,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = NVD_MIC,
+	},
+	{
+		.suffix = "IEC958",
+		.playback_ops = &snd_intel8x0_spdif_ops,
+		.prealloc_size = 64 * 1024,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = NVD_SPBAR,
+	},
+};
+
+static struct ich_pcm_table ali_pcms[] __devinitdata = {
+	{
+		.playback_ops = &snd_intel8x0_ali_playback_ops,
+		.capture_ops = &snd_intel8x0_ali_capture_ops,
+		.prealloc_size = 64 * 1024,
+		.prealloc_max_size = 128 * 1024,
+	},
+	{
+		.suffix = "MIC ADC",
+		.capture_ops = &snd_intel8x0_ali_capture_mic_ops,
+		.prealloc_size = 0,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = ALID_MIC,
+	},
+	{
+		.suffix = "IEC958",
+		.playback_ops = &snd_intel8x0_ali_spdifout_ops,
+		.capture_ops = &snd_intel8x0_ali_spdifin_ops,
+		.prealloc_size = 64 * 1024,
+		.prealloc_max_size = 128 * 1024,
+	},
+	{
+		.suffix = "AC97 IEC958",
+		.playback_ops = &snd_intel8x0_ali_ac97spdifout_ops,
+		.prealloc_size = 64 * 1024,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = ALID_AC97SPDIFOUT,
+	},
+};
+
+static int __devinit snd_intel8x0_pcm(intel8x0_t *chip)
+{
+	int i, tblsize, device, err;
+	struct ich_pcm_table *tbl, *rec;
+
+	switch (chip->device_type) {
+	case DEVICE_INTEL_ICH4:
+		tbl = intel_pcms;
+		tblsize = ARRAY_SIZE(intel_pcms);
+		break;
+	case DEVICE_NFORCE:
+		tbl = nforce_pcms;
+		tblsize = ARRAY_SIZE(nforce_pcms);
+		break;
+	case DEVICE_ALI:
+		tbl = ali_pcms;
+		tblsize = ARRAY_SIZE(ali_pcms);
+		break;
+	default:
+		tbl = intel_pcms;
+		tblsize = 2;
+		break;
+	}
 
-	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024);
+	device = 0;
+	for (i = 0; i < tblsize; i++) {
+		rec = tbl + i;
+		if (i > 0 && rec->ac97_idx) {
+			/* activate PCM only when associated AC'97 codec */
+			if (! chip->ichd[rec->ac97_idx].ac97)
+				continue;
+		}
+		err = snd_intel8x0_pcm1(chip, device, rec);
+		if (err < 0)
+			return err;
+		device++;
+	}
 
-	if (rpcm)
-		*rpcm = pcm;
+	chip->pcm_devs = device;
 	return 0;
 }
+	
 
 /*
  *  Mixer part
@@ -1502,11 +1419,13 @@
 	chip->ac97[ac97->num] = NULL;
 }
 
-static struct _ac97_rate_regs {
+struct _ac97_rate_regs {
 	unsigned int ichd;
 	unsigned short regs[3];
 	short rates_idx;
-} ac97_rate_regs[] = {
+};
+
+static struct _ac97_rate_regs intel_ac97_rate_regs[] __devinitdata = {
 	{ ICHD_PCMOUT, { AC97_PCM_FRONT_DAC_RATE, AC97_PCM_SURR_DAC_RATE, AC97_PCM_LFE_DAC_RATE }, AC97_RATES_FRONT_DAC },
 	{ ICHD_PCMIN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC },
 	{ ICHD_MIC, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC },
@@ -1515,11 +1434,14 @@
 	{ ICHD_SPBAR, { AC97_SPDIF, 0, 0 }, AC97_RATES_SPDIF },
 };
 
-static struct _ac97_ali_rate_regs {
-	unsigned int ichd;
-	unsigned short regs[3];
-	short rates_idx;
-} ac97_ali_rate_regs[] = {
+static struct _ac97_rate_regs nforce_ac97_rate_regs[] __devinitdata = {
+	{ NVD_PCMOUT, { AC97_PCM_FRONT_DAC_RATE, AC97_PCM_SURR_DAC_RATE, AC97_PCM_LFE_DAC_RATE }, AC97_RATES_FRONT_DAC },
+	{ NVD_PCMIN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC },
+	{ NVD_MIC, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC },
+	{ NVD_SPBAR, { AC97_SPDIF, AC97_PCM_FRONT_DAC_RATE, 0 }, AC97_RATES_SPDIF },
+};
+
+static struct _ac97_rate_regs ali_ac97_rate_regs[] __devinitdata = {
 	{ ALID_PCMOUT, { AC97_PCM_FRONT_DAC_RATE, AC97_PCM_SURR_DAC_RATE, AC97_PCM_LFE_DAC_RATE }, AC97_RATES_FRONT_DAC },
 	{ ALID_PCMIN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC },
 	{ ALID_MIC, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC },
@@ -1528,9 +1450,13 @@
 	{ ALID_SPDIFIN, { 0, 0, 0 }, -1 },
 };
 
-static struct ac97_quirk ac97_quirks[] = {
+static struct ac97_quirk ac97_quirks[] __devinitdata = {
 	{ 0x1028, 0x0126, "Dell Optiplex GX260", AC97_TUNE_HP_ONLY },
-	{ 0x1734, 0x0088, "Fujisu-Siemens D1522", AC97_TUNE_HP_ONLY },
+	{ 0x1734, 0x0088, "Fujitsu-Siemens D1522", AC97_TUNE_HP_ONLY },
+	{ 0x10f1, 0x2665, "Fujitsu-Siemens Celcius", AC97_TUNE_HP_ONLY },
+	{ 0x8086, 0x4d44, "Intel D850EMV2", AC97_TUNE_HP_ONLY },
+	{ 0x4144, 0x5360, "AMD64 Motherboard", AC97_TUNE_HP_ONLY },
+	{ 0x1043, 0x80b0, "ASUS P4PE Mobo", AC97_TUNE_SWAP_SURROUND },
 	{ } /* terminator */
 };
 
@@ -1538,28 +1464,31 @@
 {
 	ac97_t ac97, *x97;
 	ichdev_t *ichdev;
-	int err, i, num, channels = 2, codecs, _codecs;
+	int err;
+	unsigned int i, num, channels = 2, codecs, _codecs;
 	unsigned int glob_sta = 0;
+	struct _ac97_rate_regs *tbl;
 
-	for (i = 0; i <= ICHD_LAST; i++) {
-		if (chip->device_type != DEVICE_ALI) {
-			struct _ac97_rate_regs *aregs;
-			aregs = &ac97_rate_regs[i];
-			ichdev = &chip->ichd[aregs->ichd];
-			ichdev->ac97_rate_regs[0] = aregs->regs[0];
-			ichdev->ac97_rate_regs[1] = aregs->regs[1];
-			ichdev->ac97_rate_regs[2] = aregs->regs[2];
-			ichdev->ac97_rates_idx = aregs->rates_idx;
-		} else {
-			struct _ac97_ali_rate_regs *aregs;
-			aregs = &ac97_ali_rate_regs[i];
-			ichdev = &chip->ichd[aregs->ichd];
-			ichdev->ac97_rate_regs[0] = aregs->regs[0];
-			ichdev->ac97_rate_regs[1] = aregs->regs[1];
-			ichdev->ac97_rate_regs[2] = aregs->regs[2];
-			ichdev->ac97_rates_idx = aregs->rates_idx;
-		}
+	switch (chip->device_type) {
+	case DEVICE_NFORCE:
+		tbl = nforce_ac97_rate_regs;
+		break;
+	case DEVICE_ALI:
+		tbl = ali_ac97_rate_regs;
+		break;
+	default:
+		tbl = intel_ac97_rate_regs;
+		break;
+	};
+	for (i = 0; i < chip->bdbars_count; i++) {
+		struct _ac97_rate_regs *aregs = tbl + i;
+		ichdev = &chip->ichd[aregs->ichd];
+		ichdev->ac97_rate_regs[0] = aregs->regs[0];
+		ichdev->ac97_rate_regs[1] = aregs->regs[1];
+		ichdev->ac97_rate_regs[2] = aregs->regs[2];
+		ichdev->ac97_rates_idx = aregs->rates_idx;
 	}
+
 	chip->in_ac97_init = 1;
 	memset(&ac97, 0, sizeof(ac97));
 	ac97.private_data = chip;
@@ -1616,15 +1545,24 @@
 		return err;
 	chip->ac97[0] = x97;
 	snd_ac97_tune_hardware(chip->ac97[0], ac97_quirks);
+	/* the following three entries are common among all devices */
 	chip->ichd[ICHD_PCMOUT].ac97 = x97;
 	chip->ichd[ICHD_PCMIN].ac97 = x97;
 	if (x97->ext_id & AC97_EI_VRM)
 		chip->ichd[ICHD_MIC].ac97 = x97;
+	/* spdif */
 	if (x97->ext_id & AC97_EI_SPDIF) {
-		if (chip->device_type != DEVICE_ALI)
+		switch (chip->device_type) {
+		case DEVICE_INTEL_ICH4:
 			chip->ichd[ICHD_SPBAR].ac97 = x97;
-		else
+			break;
+		case DEVICE_NFORCE:
+			chip->ichd[NVD_SPBAR].ac97 = x97;
+			break;
+		case DEVICE_ALI:
 			chip->ichd[ALID_AC97SPDIFOUT].ac97 = x97;
+			break;
+		}
 	}
 	/* make sure, that we have DACs at right slot for rev2.2 */
 	if (ac97_is_rev22(x97))
@@ -1666,6 +1604,11 @@
 				if (chip->ichd[ICHD_MIC].ac97 == NULL)
 					chip->ichd[ICHD_MIC].ac97 = x97;
 			}
+			if ((x97->ext_id & AC97_EI_SPDIF) &&
+			    chip->device_type == DEVICE_NFORCE) {
+				if (chip->ichd[NVD_SPBAR].ac97 == NULL)
+					chip->ichd[NVD_SPBAR].ac97 = x97;
+			}
 			break;
 		}
 	}
@@ -1786,8 +1729,11 @@
 	
 	/* put logic to right state */
 	/* first clear status bits */
+	status = ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT;
+	if (chip->device_type == DEVICE_NFORCE)
+		status |= ICH_NVSPINT;
 	cnt = igetdword(chip, ICHREG(GLOB_STA));
-	iputdword(chip, ICHREG(GLOB_STA), cnt & (ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT));
+	iputdword(chip, ICHREG(GLOB_STA), cnt & status);
 
 	/* ACLink on, 2 channels */
 	cnt = igetdword(chip, ICHREG(GLOB_CNT));
@@ -1852,6 +1798,13 @@
 		/* unmute the output on SIS7012 */
 		iputword(chip, 0x4c, igetword(chip, 0x4c) | 1);
 	}
+	if (chip->device_type == DEVICE_NFORCE) {
+		/* enable SPDIF interrupt */
+		unsigned int val;
+		pci_read_config_dword(chip->pci, 0x4c, &val);
+		val |= 0x1000000;
+		pci_write_config_dword(chip->pci, 0x4c, val);
+	}
       	return 0;
 }
 
@@ -1927,6 +1880,13 @@
 	/* reset channels */
 	for (i = 0; i < chip->bdbars_count; i++)
 		iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
+	if (chip->device_type == DEVICE_NFORCE) {
+		/* stop the spdif interrupt */
+		unsigned int val;
+		pci_read_config_dword(chip->pci, 0x4c, &val);
+		val &= ~0x1000000;
+		pci_write_config_dword(chip->pci, 0x4c, val);
+	}
 	/* --- */
 	synchronize_irq(chip->irq);
       __hw_end:
@@ -1957,15 +1917,15 @@
 static void intel8x0_suspend(intel8x0_t *chip)
 {
 	snd_card_t *card = chip->card;
+	int i;
 
 	if (chip->in_suspend ||
 	    card->power_state == SNDRV_CTL_POWER_D3hot)
 		return;
 
 	chip->in_suspend = 1;
-	snd_pcm_suspend_all(chip->pcm);
-	if (chip->pcm_mic)
-		snd_pcm_suspend_all(chip->pcm_mic);
+	for (i = 0; i < chip->pcm_devs; i++)
+		snd_pcm_suspend_all(chip->pcm[i]);
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 }
 
@@ -2050,7 +2010,7 @@
 	if (chip->ac97[0]->clock != 48000)
 		return; /* specified in module option */
 
-	subs = chip->pcm->streams[0].substream;
+	subs = chip->pcm[0]->streams[0].substream;
 	if (! subs || subs->dma_buffer.bytes < INTEL8X0_TESTBUF_SIZE) {
 		snd_printk("no playback buffer allocated - aborting measure ac97 clock\n");
 		return;
@@ -2087,10 +2047,7 @@
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	/* check the position */
 	pos = ichdev->fragsize1;
-	if (chip->device_type == DEVICE_SIS)
-		pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
-	else
-		pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << 1;
+	pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << chip->pcm_pos_shift;
 	pos += ichdev->position;
 	do_gettimeofday(&stop_time);
 	/* stop */
@@ -2164,6 +2121,11 @@
 	return snd_intel8x0_free(chip);
 }
 
+struct ich_reg_info {
+	unsigned int int_sta_mask;
+	unsigned int offset;
+};
+
 static int __devinit snd_intel8x0_create(snd_card_t * card,
 					 struct pci_dev *pci,
 					 unsigned long device_type,
@@ -2177,16 +2139,37 @@
 	static snd_device_ops_t ops = {
 		.dev_free =	snd_intel8x0_dev_free,
 	};
-	static u32 intel_int_sta_masks[6] = {
-		ICH_PIINT, ICH_POINT, ICH_MCINT, ICH_M2INT, ICH_P2INT, ICH_SPINT
+
+	static unsigned int bdbars[] = {
+		3, /* DEVICE_INTEL */
+		6, /* DEVICE_INTEL_ICH4 */
+		3, /* DEVICE_SIS */
+		6, /* DEVICE_ALI */
+		4, /* DEVICE_NFORCE */
+	};
+	static struct ich_reg_info intel_regs[6] = {
+		{ ICH_PIINT, 0 },
+		{ ICH_POINT, 0x10 },
+		{ ICH_MCINT, 0x20 },
+		{ ICH_M2INT, 0x40 },
+		{ ICH_P2INT, 0x50 },
+		{ ICH_SPINT, 0x60 },
 	};
-	static u32 ali_int_sta_masks[6] = {
-		ALI_INT_PCMIN, ALI_INT_PCMOUT, ALI_INT_MICIN,
-		ALI_INT_CODECSPDIFOUT, ALI_INT_SPDIFIN, ALI_INT_SPDIFOUT
+	static struct ich_reg_info nforce_regs[4] = {
+		{ ICH_PIINT, 0 },
+		{ ICH_POINT, 0x10 },
+		{ ICH_MCINT, 0x20 },
+		{ ICH_NVSPINT, 0x70 },
 	};
-	static u32 ali_reg_offsets[6] = {
-		0x40, 0x50, 0x60, 0x70, 0xa0, 0xb0
+	static struct ich_reg_info ali_regs[6] = {
+		{ ALI_INT_PCMIN, 0x40 },
+		{ ALI_INT_PCMOUT, 0x50 },
+		{ ALI_INT_MICIN, 0x60 },
+		{ ALI_INT_CODECSPDIFOUT, 0x70 },
+		{ ALI_INT_SPDIFIN, 0xa0 },
+		{ ALI_INT_SPDIFOUT, 0xb0 },
 	};
+	struct ich_reg_info *tbl;
 
 	*r_intel8x0 = NULL;
 
@@ -2205,25 +2188,36 @@
 	snd_intel8x0_proc_init(chip);
 	sprintf(chip->ac97_name, "%s - AC'97", card->shortname);
 	sprintf(chip->ctrl_name, "%s - Controller", card->shortname);
+	if (device_type == DEVICE_ALI) {
+		/* ALI5455 has no ac97 region */
+		chip->bmaddr = pci_resource_start(pci, 0);
+		if ((chip->res_bm = request_region(chip->bmaddr, 256, chip->ctrl_name)) == NULL) {
+			snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 64 - 1);
+			snd_intel8x0_free(chip);
+			return -EBUSY;
+		}
+		goto port_inited;
+	}
+
 	if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) {	/* ICH4 and Nforce */
 		chip->mmio = 1;
 		chip->addr = pci_resource_start(pci, 2);
 		if ((chip->res = request_mem_region(chip->addr, 512, chip->ac97_name)) == NULL) {
-			snd_intel8x0_free(chip);
 			snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", chip->addr, chip->addr + 512 - 1);
+			snd_intel8x0_free(chip);
 			return -EBUSY;
 		}
 		chip->remap_addr = (unsigned long) ioremap_nocache(chip->addr, 512);
 		if (chip->remap_addr == 0) {
-			snd_intel8x0_free(chip);
 			snd_printk("AC'97 space ioremap problem\n");
+			snd_intel8x0_free(chip);
 			return -EIO;
 		}
 	} else {
 		chip->addr = pci_resource_start(pci, 0);
 		if ((chip->res = request_region(chip->addr, 256, chip->ac97_name)) == NULL) {
-			snd_intel8x0_free(chip);
 			snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->addr, chip->addr + 256 - 1);
+			snd_intel8x0_free(chip);
 			return -EBUSY;
 		}
 	}
@@ -2231,63 +2225,69 @@
 		chip->bm_mmio = 1;
 		chip->bmaddr = pci_resource_start(pci, 3);
 		if ((chip->res_bm = request_mem_region(chip->bmaddr, 256, chip->ctrl_name)) == NULL) {
-			snd_intel8x0_free(chip);
 			snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 512 - 1);
+			snd_intel8x0_free(chip);
 			return -EBUSY;
 		}
 		chip->remap_bmaddr = (unsigned long) ioremap_nocache(chip->bmaddr, 256);
 		if (chip->remap_bmaddr == 0) {
-			snd_intel8x0_free(chip);
 			snd_printk("Controller space ioremap problem\n");
+			snd_intel8x0_free(chip);
 			return -EIO;
 		}
 	} else {
 		chip->bmaddr = pci_resource_start(pci, 1);
 		if ((chip->res_bm = request_region(chip->bmaddr, 64, chip->ctrl_name)) == NULL) {
-			snd_intel8x0_free(chip);
 			snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 64 - 1);
+			snd_intel8x0_free(chip);
 			return -EBUSY;
 		}
 	}
+
+ port_inited:
 	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);
+		snd_intel8x0_free(chip);
 		return -EBUSY;
 	}
 	chip->irq = pci->irq;
 	pci_set_master(pci);
 	synchronize_irq(chip->irq);
 
+	chip->bdbars_count = bdbars[device_type];
+
 	/* initialize offsets */
-	for (i = 0; i <= ICHD_LAST; i++) {
+	switch (device_type) {
+	case DEVICE_NFORCE:
+		tbl = nforce_regs;
+		break;
+	case DEVICE_ALI:
+		tbl = ali_regs;
+		break;
+	default:
+		tbl = intel_regs;
+		break;
+	}
+	for (i = 0; i < chip->bdbars_count; i++) {
 		ichdev = &chip->ichd[i];
 		ichdev->ichd = i;
-		ichdev->reg_offset = i * 0x10 + (i >= 0x30 ? 0x10 : 0);
-		ichdev->roff_sr = ICH_REG_OFF_SR;
-		ichdev->roff_picb = ICH_REG_OFF_PICB;
-		ichdev->int_sta_mask = device_type == DEVICE_ALI ? ali_int_sta_masks[i] : intel_int_sta_masks[i];
-	}
-	switch (device_type) {
-	case DEVICE_SIS:
-		for (i = 0; i <= ICHD_LAST; i++) {
-			ichdev = &chip->ichd[i];
+		ichdev->reg_offset = tbl[i].offset;
+		ichdev->int_sta_mask = tbl[i].int_sta_mask;
+		if (device_type == DEVICE_SIS) {
+			/* SiS 7012 swaps the registers */
 			ichdev->roff_sr = ICH_REG_OFF_PICB;
 			ichdev->roff_picb = ICH_REG_OFF_SR;
+		} else {
+			ichdev->roff_sr = ICH_REG_OFF_SR;
+			ichdev->roff_picb = ICH_REG_OFF_PICB;
 		}
-		break;
-	case DEVICE_ALI:
-		for (i = 0; i <= ALID_LAST; i++) {
-			ichdev = &chip->ichd[i];
-			ichdev->reg_offset = ali_reg_offsets[i];
-			ichdev->ali_slot = i + 1;	/* is this right for last three devices? --jk */
-		}
+		ichdev->ali_slot = i + 1;	/* is this right for last three devices? --jk */
 	}
+	/* SIS7012 handles the pcm data in bytes, others are in words */
+	chip->pcm_pos_shift = (device_type == DEVICE_SIS) ? 0 : 1;
 
 	/* allocate buffer descriptor lists */
 	/* the start of each lists must be aligned to 8 bytes */
-	chip->bdbars_count = 3;
-	if (device_type == DEVICE_INTEL_ICH4 || device_type == DEVICE_ALI)
-		chip->bdbars_count = 6;
 	chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr);
 	if (chip->bdbars == NULL) {
 		snd_intel8x0_free(chip);
@@ -2295,14 +2295,6 @@
 	}
 	/* 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
 	int_sta_masks = 0;
 	for (i = 0; i < chip->bdbars_count; i++) {
 		ichdev = &chip->ichd[i];
@@ -2344,9 +2336,9 @@
 	{ PCI_DEVICE_ID_INTEL_ICH4, "Intel 82801DB-ICH4" },
 	{ PCI_DEVICE_ID_INTEL_ICH5, "Intel ICH5" },
 	{ PCI_DEVICE_ID_SI_7012, "SiS SI7012" },
-	{ PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia NForce" },
-	{ PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO, "NVidia NForce2" },
-	{ PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO, "NVidia NForce3" },
+	{ PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia nForce" },
+	{ PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO, "NVidia nForce2" },
+	{ PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO, "NVidia nForce3" },
 	{ 0x746d, "AMD AMD8111" },
 	{ 0x7445, "AMD AMD768" },
 	{ 0x5455, "ALi M5455" },
@@ -2359,7 +2351,7 @@
 	static int dev;
 	snd_card_t *card;
 	intel8x0_t *chip;
-	int pcm_dev = 0, err;
+	int err;
 	struct shortname_table *name;
 
 	if (dev >= SNDRV_CARDS)
@@ -2373,7 +2365,15 @@
 	if (card == NULL)
 		return -ENOMEM;
 
-	strcpy(card->driver, "ICH");
+	switch (pci_id->driver_data) {
+	case DEVICE_NFORCE:
+		strcpy(card->driver, "NFORCE");
+		break;
+	default:
+		strcpy(card->driver, "ICH");
+		break;
+	}
+
 	strcpy(card->shortname, "Intel ICH");
 	for (name = shortnames; name->id; name++) {
 		if (pci->device == name->id) {
@@ -2391,53 +2391,10 @@
 		snd_card_free(card);
 		return err;
 	}
-	if ((err = snd_intel8x0_pcm(chip, pcm_dev++, NULL)) < 0) {
+	if ((err = snd_intel8x0_pcm(chip)) < 0) {
 		snd_card_free(card);
 		return err;
 	}
-	/* activate MIC PCM only when associated AC'97 codec */
-	if (chip->ichd[ICHD_MIC].ac97) {
-		if ((err = snd_intel8x0_pcm_mic(chip, pcm_dev++, NULL)) < 0) {
-			snd_card_free(card);
-			return err;
-		}
-	}
-	if (chip->device_type == DEVICE_ALI) {
-		if ((err = snd_intel8x0_ali_spdif(chip, pcm_dev++, NULL)) < 0) {
-			snd_card_free(card);
-			return err;
-		}
-	}
-	/* activate AC'97 S/PDIF only when associated AC'97 codec */
-	if (chip->bdbars_count > 3) {
-		err = 0;
-		if (chip->device_type == DEVICE_ALI) {
-			if (chip->ichd[ALID_AC97SPDIFOUT].ac97)
-				err = snd_intel8x0_ali_ac97spdif(chip, pcm_dev++, NULL);
-		} else {
-			if (chip->ichd[ICHD_SPBAR].ac97)
-				err = snd_intel8x0_pcm_spdif(chip, pcm_dev++, NULL);
-		}
-		if (err < 0) {
-			snd_card_free(card);
-			return err;
-		}
-		if (chip->device_type != DEVICE_ALI) {
-			/* activate MIC2 only when associated AC'97 codec */
-			if (chip->ichd[ICHD_MIC2].ac97)
-				if ((err = snd_intel8x0_pcm_mic2(chip, pcm_dev++, NULL)) < 0) {
-					snd_card_free(card);
-					return err;
-				}
-			/* activate PCM2IN only when associated AC'97 codec */
-			if (chip->ichd[ICHD_PCM2IN].ac97)
-				if ((err = snd_intel8x0_pcm_capture2(chip, pcm_dev++, NULL)) < 0) {
-					snd_card_free(card);
-					return err;
-				}
-		}
-	}
-	
 	
 	if (mpu_port[dev] == 0x300 || mpu_port[dev] == 0x330) {
 		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_INTEL8X0,
diff -urN linux-2.5.70-bk6/sound/pci/korg1212/korg1212.c linux-2.5.70-bk7/sound/pci/korg1212/korg1212.c
--- linux-2.5.70-bk6/sound/pci/korg1212/korg1212.c	2003-05-26 18:00:20.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/korg1212/korg1212.c	2003-06-02 04:39:13.000000000 -0700
@@ -1136,8 +1136,7 @@
 static irqreturn_t 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 IRQ_NONE);
+        korg1212_t *korg1212 = snd_magic_cast(korg1212_t, dev_id, return IRQ_NONE);
 
 	if(irq != korg1212->irq)
 		return IRQ_NONE;
diff -urN linux-2.5.70-bk6/sound/pci/maestro3.c linux-2.5.70-bk7/sound/pci/maestro3.c
--- linux-2.5.70-bk6/sound/pci/maestro3.c	2003-05-26 18:00:41.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/maestro3.c	2003-06-02 04:39:13.000000000 -0700
@@ -947,8 +947,14 @@
 		.amp_gpio = -1,
 		.irda_workaround = 1,
 	},
-	/* FIXME: Inspiron 8100 id should probably be here, too
-	 * (8200 irrelevant: has intel8x0 with CS4205) */
+	/* Dell Inspiron 8100 */
+	{
+		.name = "Dell Inspiron 8100",
+		.vendor = 0x1028,
+		.device = 0x00e6,
+		.amp_gpio = -1,
+		.irda_workaround = 1,
+	},
 	/* NEC LM800J/7 */
 	{
 		.name = "NEC LM800J/7",
@@ -1968,7 +1974,7 @@
 #endif
 }
 
-static int snd_m3_mixer(m3_t *chip)
+static int __devinit snd_m3_mixer(m3_t *chip)
 {
 	ac97_t ac97;
 	int err;
@@ -1994,7 +2000,7 @@
  * DSP Code images
  */
 
-static u16 assp_kernel_image[] = {
+static u16 assp_kernel_image[] __devinitdata = {
     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, 
@@ -2081,7 +2087,7 @@
  * Mini sample rate converter code image
  * that is to be loaded at 0x400 on the DSP.
  */
-static u16 assp_minisrc_image[] = {
+static u16 assp_minisrc_image[] __devinitdata = {
 
     0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412, 
     0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41, 
@@ -2124,12 +2130,12 @@
  */
 
 #define MINISRC_LPF_LEN 10
-static u16 minisrc_lpf[MINISRC_LPF_LEN] = {
+static u16 minisrc_lpf[MINISRC_LPF_LEN] __devinitdata = {
 	0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C,
 	0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F
 };
 
-static void snd_m3_assp_init(m3_t *chip)
+static void __devinit snd_m3_assp_init(m3_t *chip)
 {
 	unsigned int i;
 
@@ -2218,7 +2224,7 @@
 }
 
 
-static int snd_m3_assp_client_init(m3_t *chip, m3_dma_t *s, int index)
+static int __devinit 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 +
@@ -2387,6 +2393,8 @@
 	snd_card_t *card = chip->card;
 	int i, index;
 
+	if (chip->suspend_mem == NULL)
+		return;
 	if (card->power_state == SNDRV_CTL_POWER_D3hot)
 		return;
 
@@ -2416,6 +2424,8 @@
 	snd_card_t *card = chip->card;
 	int i, index;
 
+	if (chip->suspend_mem == NULL)
+		return;
 	if (card->power_state == SNDRV_CTL_POWER_D0)
 		return;
 
@@ -2585,8 +2595,8 @@
 	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);
+		snd_m3_free(chip);
 		return -EBUSY;
 	}
 	
@@ -2598,14 +2608,14 @@
 
 	snd_m3_ac97_reset(chip, 0);
 
+	snd_m3_assp_init(chip);
+	snd_m3_amp_enable(chip, 1);
+    
 	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;
@@ -2622,8 +2632,8 @@
     
 	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);
+		snd_m3_free(chip);
 		return -ENOMEM;
 	}
 	chip->irq = pci->irq;
@@ -2632,8 +2642,10 @@
 	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;
+	else {
+		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) {
diff -urN linux-2.5.70-bk6/sound/pci/nm256/nm256.c linux-2.5.70-bk7/sound/pci/nm256/nm256.c
--- linux-2.5.70-bk6/sound/pci/nm256/nm256.c	2003-05-26 18:00:56.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/nm256/nm256.c	2003-06-02 04:39:13.000000000 -0700
@@ -230,8 +230,9 @@
 	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 */
+	unsigned int coeffs_current: 1;	/* coeff. table is loaded? */
+	unsigned int use_cache: 1;	/* use one big coef. table */
+	unsigned int latitude_workaround: 1; /* Dell Latitude LS workaround needed */
 
 	int mixer_base;			/* register offset of ac97 mixer */
 	int mixer_status_offset;	/* offset of mixer status reg. */
@@ -1042,7 +1043,7 @@
 static irqreturn_t
 snd_nm256_interrupt_zx(int irq, void *dev_id, struct pt_regs *dummy)
 {
-	nm256_t *chip = snd_magic_cast(nm256_t, dev_id, return);
+	nm256_t *chip = snd_magic_cast(nm256_t, dev_id, return IRQ_NONE);
 	u32 status;
 	u8 cbyte;
 
@@ -1181,9 +1182,10 @@
 	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
+	if (chip->latitude_workaround) {
+		/* Dell latitude LS will lock up by this */
+		snd_nm256_writeb(chip, 0x6cc, 0x87);
+	}
 	snd_nm256_writeb(chip, 0x6cc, 0x80);
 	snd_nm256_writeb(chip, 0x6cc, 0x0);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
@@ -1194,7 +1196,7 @@
 snd_nm256_mixer(nm256_t *chip)
 {
 	ac97_t ac97;
-	int i;
+	int i, err;
 	/* looks like nm256 hangs up when unexpected registers are touched... */
 	static int mixer_regs[] = {
 		AC97_MASTER, AC97_HEADPHONE, AC97_MASTER_MONO,
@@ -1210,11 +1212,19 @@
 	ac97.reset = snd_nm256_ac97_reset;
 	ac97.write = snd_nm256_ac97_write;
 	ac97.read = snd_nm256_ac97_read;
+	ac97.scaps = AC97_SCAP_AUDIO; /* we support audio! */
 	ac97.limited_regs = 1;
 	for (i = 0; mixer_regs[i] >= 0; i++)
 		set_bit(mixer_regs[i], ac97.reg_accessed);
 	ac97.private_data = chip;
-	return snd_ac97_mixer(chip->card, &ac97, &chip->ac97);
+	err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97);
+	if (err < 0)
+		return err;
+	if (! (chip->ac97->id & (0xf0000000))) {
+		/* looks like an invalid id */
+		sprintf(chip->card->mixername, "%s AC97", chip->card->driver);
+	}
+	return 0;
 }
 
 /* 
@@ -1394,6 +1404,7 @@
 		.dev_free =	snd_nm256_dev_free,
 	};
 	u32 addr;
+	u16 subsystem_vendor, subsystem_device;
 
 	*chip_ret = NULL;
 
@@ -1530,6 +1541,15 @@
 
 	chip->coeffs_current = 0;
 
+	/* check workarounds */
+	chip->latitude_workaround = 1;
+	pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device);
+	if (subsystem_vendor == 0x104d && subsystem_device == 0x8041) {
+		/* this workaround will cause lock-up after suspend/resume on Sony PCG-F305 */
+		chip->latitude_workaround = 0;
+	}
+
 	snd_nm256_init_chip(chip);
 
 	if ((err = snd_nm256_pcm(chip, 0)) < 0)
diff -urN linux-2.5.70-bk6/sound/pci/rme9652/hammerfall_mem.c linux-2.5.70-bk7/sound/pci/rme9652/hammerfall_mem.c
--- linux-2.5.70-bk6/sound/pci/rme9652/hammerfall_mem.c	2003-05-26 18:00:24.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/rme9652/hammerfall_mem.c	2003-06-02 04:39:13.000000000 -0700
@@ -25,7 +25,7 @@
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
-    $Id: hammerfall_mem.c,v 1.8 2003/02/25 13:35:44 perex Exp $
+    $Id: hammerfall_mem.c,v 1.9 2003/05/31 11:33:57 perex Exp $
 
 
     Tue Oct 17 2000  Jaroslav Kysela <perex@suse.cz>
@@ -98,15 +98,7 @@
 {
 	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) {
 		struct page *page = virt_to_page(res);
 		struct page *last_page = page + (size + PAGE_SIZE - 1) / PAGE_SIZE;
@@ -127,19 +119,7 @@
 	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 "hammerfall_free_pages: dmaaddr != ptr\n");
-			return;
-		}
-		free_pages((unsigned long)ptr, pg);
-	}
-#endif
 }
 
 void *snd_hammerfall_get_buffer (struct pci_dev *pcidev, dma_addr_t *dmaaddr)
diff -urN linux-2.5.70-bk6/sound/pci/rme9652/hdsp.c linux-2.5.70-bk7/sound/pci/rme9652/hdsp.c
--- linux-2.5.70-bk6/sound/pci/rme9652/hdsp.c	2003-05-26 18:00:37.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/rme9652/hdsp.c	2003-06-02 04:39:13.000000000 -0700
@@ -3211,15 +3211,16 @@
 	else
 		runtime->status->hw_ptr = 0;
 	if (other) {
-		snd_pcm_substream_t *s = substream;
+		struct list_head *pos;
+		snd_pcm_substream_t *s;
 		snd_pcm_runtime_t *oruntime = other->runtime;
-		do {
-			s = s->link_next;
+		snd_pcm_group_for_each(pos, substream) {
+			s = snd_pcm_group_substream_entry(pos);
 			if (s == other) {
 				oruntime->status->hw_ptr = runtime->status->hw_ptr;
 				break;
 			}
-		} while (s != substream);
+		}
 	}
 	return 0;
 }
@@ -3382,9 +3383,10 @@
 		other = hdsp->playback_substream;
 
 	if (other) {
-		snd_pcm_substream_t *s = substream;
-		do {
-			s = s->link_next;
+		struct list_head *pos;
+		snd_pcm_substream_t *s;
+		snd_pcm_group_for_each(pos, substream) {
+			s = snd_pcm_group_substream_entry(pos);
 			if (s == other) {
 				snd_pcm_trigger_done(s, substream);
 				if (cmd == SNDRV_PCM_TRIGGER_START)
@@ -3393,7 +3395,7 @@
 					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)
@@ -3526,7 +3528,7 @@
 		snd_interval_t t = {
 			.min = hdsp->ds_channels,
 			.max = hdsp->ds_channels,
-			integer: 1,
+			.integer = 1,
 		};
 		return snd_interval_refine(c, &t);
 	} else if (r->max < 64000) {
@@ -3550,7 +3552,7 @@
 		snd_interval_t t = {
 			.min = 32000,
 			.max = 48000,
-			integer: 1,
+			.integer = 1,
 		};
 		return snd_interval_refine(r, &t);
 	} else if (c->max <= hdsp->ds_channels) {
diff -urN linux-2.5.70-bk6/sound/pci/rme9652/rme9652.c linux-2.5.70-bk7/sound/pci/rme9652/rme9652.c
--- linux-2.5.70-bk6/sound/pci/rme9652/rme9652.c	2003-05-26 18:00:39.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/rme9652/rme9652.c	2003-06-02 04:39:13.000000000 -0700
@@ -2057,15 +2057,16 @@
 	else
 		runtime->status->hw_ptr = 0;
 	if (other) {
-		snd_pcm_substream_t *s = substream;
+		struct list_head *pos;
+		snd_pcm_substream_t *s;
 		snd_pcm_runtime_t *oruntime = other->runtime;
-		do {
-			s = s->link_next;
+		snd_pcm_group_for_each(pos, substream) {
+			s = snd_pcm_group_substream_entry(pos);
 			if (s == other) {
 				oruntime->status->hw_ptr = runtime->status->hw_ptr;
 				break;
 			}
-		} while (s != substream);
+		}
 	}
 	return 0;
 }
@@ -2204,9 +2205,10 @@
 		other = rme9652->playback_substream;
 
 	if (other) {
-		snd_pcm_substream_t *s = substream;
-		do {
-			s = s->link_next;
+		struct list_head *pos;
+		snd_pcm_substream_t *s;
+		snd_pcm_group_for_each(pos, substream) {
+			s = snd_pcm_group_substream_entry(pos);
 			if (s == other) {
 				snd_pcm_trigger_done(s, substream);
 				if (cmd == SNDRV_PCM_TRIGGER_START)
@@ -2215,7 +2217,7 @@
 					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)
diff -urN linux-2.5.70-bk6/sound/pci/sonicvibes.c linux-2.5.70-bk7/sound/pci/sonicvibes.c
--- linux-2.5.70-bk6/sound/pci/sonicvibes.c	2003-05-26 18:00:44.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/sonicvibes.c	2003-06-02 04:39:13.000000000 -0700
@@ -598,8 +598,7 @@
 
 static irqreturn_t snd_sonicvibes_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, dev_id,
-						return IRQ_NONE);
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, dev_id, return IRQ_NONE);
 	unsigned char status;
 
 	status = inb(SV_REG(sonic, STATUS));
@@ -655,7 +654,6 @@
 		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);
 	}
-
 	return IRQ_HANDLED;
 }
 
diff -urN linux-2.5.70-bk6/sound/pci/trident/trident_main.c linux-2.5.70-bk7/sound/pci/trident/trident_main.c
--- linux-2.5.70-bk6/sound/pci/trident/trident_main.c	2003-05-26 18:00:42.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/trident/trident_main.c	2003-06-02 04:39:13.000000000 -0700
@@ -1502,6 +1502,7 @@
 				    
 {
 	trident_t *trident = snd_pcm_substream_chip(substream);
+	struct list_head *pos;
 	snd_pcm_substream_t *s;
 	unsigned int what, whati, capture_flag, spdif_flag;
 	snd_trident_voice_t *voice, *evoice;
@@ -1522,10 +1523,10 @@
 		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 {
+	snd_pcm_group_for_each(pos, substream) {
+		s = snd_pcm_group_substream_entry(pos);
 		if ((trident_t *) _snd_pcm_chip(s->pcm) == trident) {
 			voice = (snd_trident_voice_t *) s->runtime->private_data;
 			evoice = voice->extra;
@@ -1550,8 +1551,7 @@
 			if (voice->spdif)
 				spdif_flag = 1;
 		}
-		s = s->link_next;
-	} while (s != substream);
+	}
 	if (spdif_flag) {
 		if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
 			outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
@@ -3683,7 +3683,7 @@
                 the method try & fail so it is possible that it won't
                 work on all computers. [jaroslav]
 
-   Returns:     Whether IRQ was handled or not.
+   Returns:     None.
   
   ---------------------------------------------------------------------------*/
 
@@ -3781,7 +3781,6 @@
 		}
 	}
 	// outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(trident, T4D_MISCINT));
-
 	return IRQ_HANDLED;
 }
 
diff -urN linux-2.5.70-bk6/sound/pci/trident/trident_synth.c linux-2.5.70-bk7/sound/pci/trident/trident_synth.c
--- linux-2.5.70-bk6/sound/pci/trident/trident_synth.c	2003-05-26 18:00:38.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/trident/trident_synth.c	2003-06-02 04:39:13.000000000 -0700
@@ -904,9 +904,6 @@
 	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,
 						   16, 0,
diff -urN linux-2.5.70-bk6/sound/pci/via82xx.c linux-2.5.70-bk7/sound/pci/via82xx.c
--- linux-2.5.70-bk6/sound/pci/via82xx.c	2003-05-26 18:00:47.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/via82xx.c	2003-06-02 04:39:13.000000000 -0700
@@ -74,6 +74,7 @@
 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
 static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
 static int ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};
+static int dxs_support[SNDRV_CARDS];
 
 MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
 MODULE_PARM_DESC(index, "Index value for VIA 82xx bridge.");
@@ -90,6 +91,9 @@
 MODULE_PARM(ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
 MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");
 MODULE_PARM_SYNTAX(ac97_clock, SNDRV_ENABLED ",default:48000");
+MODULE_PARM(dxs_support, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(dxs_support, "Support for DXS channels (0 = auto, 1 = enable, 2 = disable, 3 = 48k only)");
+MODULE_PARM_SYNTAX(dxs_support, SNDRV_ENABLED ",allows:{{0,3}},dialog:list");
 
 
 /* pci ids */
@@ -435,6 +439,7 @@
 	unsigned int playback_devno, multi_devno, capture_devno;
 	viadev_t devs[VIA_MAX_DEVS];
 	struct via_rate_lock rates[2]; /* playback and capture */
+	unsigned int dxs_fixed: 1;	/* DXS channel accepts only 48kHz */
 
 	snd_rawmidi_t *rmidi;
 
@@ -876,6 +881,8 @@
 		return rate_changed;
 	if (rate_changed) {
 		snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+		snd_ac97_set_rate(chip->ac97, AC97_PCM_SURR_DAC_RATE, runtime->rate);
+		snd_ac97_set_rate(chip->ac97, AC97_PCM_LFE_DAC_RATE, runtime->rate);
 		snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate);
 	}
 	if (chip->chip_type == TYPE_VIA8233A)
@@ -1000,6 +1007,19 @@
 	int err;
 	unsigned long flags;
 	struct via_rate_lock *ratep;
+	struct ratetbl {
+		int rate;
+		unsigned int bit;
+	} ratebits[] = {
+		{8000, SNDRV_PCM_RATE_8000},
+		{11025, SNDRV_PCM_RATE_11025},
+		{16000, SNDRV_PCM_RATE_16000},
+		{22050, SNDRV_PCM_RATE_22050},
+		{32000, SNDRV_PCM_RATE_32000},
+		{44100, SNDRV_PCM_RATE_44100},
+		{48000, SNDRV_PCM_RATE_48000},
+	};
+	int i;
 
 	runtime->hw = snd_via82xx_hw;
 	
@@ -1007,14 +1027,33 @@
 	ratep = &chip->rates[viadev->direction];
 	spin_lock_irqsave(&ratep->lock, flags);
 	ratep->used++;
-	if (! ratep->rate) {
+	if (chip->dxs_fixed) {
+		runtime->hw.rates = SNDRV_PCM_RATE_48000;
+		runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+	} else if (! ratep->rate) {
 		int idx = viadev->direction ? AC97_RATES_ADC : AC97_RATES_FRONT_DAC;
 		runtime->hw.rates = chip->ac97->rates[idx];
-		if (runtime->hw.rates & SNDRV_PCM_RATE_8000)
-			runtime->hw.rate_min = 8000;
+		for (i = 0; i < (int)ARRAY_SIZE(ratebits); i++) {
+			if (runtime->hw.rates & ratebits[i].bit) {
+				runtime->hw.rate_min = ratebits[i].rate;
+				break;
+			}
+		}
+		for (i = ARRAY_SIZE(ratebits) - 1; i >= 0; i--) {
+			if (runtime->hw.rates & ratebits[i].bit) {
+				runtime->hw.rate_max = ratebits[i].rate;
+				break;
+			}
+		}
 	} else {
 		/* a fixed rate */
 		runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
+		for (i = 0; i < (int)ARRAY_SIZE(ratebits); i++) {
+			if (ratep->rate == ratebits[i].rate) {
+				runtime->hw.rates = ratebits[i].bit;
+				break;
+			}
+		}
 		runtime->hw.rate_max = runtime->hw.rate_min = ratep->rate;
 	}
 	spin_unlock_irqrestore(&ratep->lock, flags);
@@ -1038,8 +1077,11 @@
 {
 	via82xx_t *chip = snd_pcm_substream_chip(substream);
 	viadev_t *viadev = &chip->devs[chip->playback_devno + substream->number];
+	int err;
 
-	return snd_via82xx_pcm_open(chip, viadev, substream);
+	if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0)
+		return err;
+	return 0;
 }
 
 /*
@@ -1639,6 +1681,28 @@
 
 
 /*
+ * proc interface
+ */
+static void snd_via82xx_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, entry->private_data, return);
+	int i;
+	
+	snd_iprintf(buffer, "%s\n\n", chip->card->longname);
+	for (i = 0; i < 0xa0; i += 4) {
+		snd_iprintf(buffer, "%02x: %08x", i, inl(chip->port + i));
+	}
+}
+
+static void __devinit snd_via82xx_proc_init(via82xx_t *chip)
+{
+	snd_info_entry_t *entry;
+
+	if (! snd_card_proc_new(chip->card, "via82xx", &entry))
+		snd_info_set_text_ops(entry, chip, snd_via82xx_proc_read);
+}
+
+/*
  *
  */
 
@@ -1736,6 +1800,19 @@
 		outl(0, VIAREG(chip, GPI_INTR));
 	}
 
+	if (chip->chip_type != TYPE_VIA686) {
+		/* Workaround for Award BIOS bug:
+		 * DXS channels don't work properly with VRA if MC97 is disabled.
+		 */
+		struct pci_dev *pci;
+		pci = pci_find_device(0x1106, 0x3068, NULL); /* MC97 */
+		if (pci) {
+			unsigned char data;
+			pci_read_config_byte(pci, 0x44, &data);
+			pci_write_config_byte(pci, 0x44, data | 0x40);
+		}
+	}
+
 	return 0;
 }
 
@@ -1816,7 +1893,7 @@
 	if (request_irq(pci->irq, snd_via82xx_interrupt, SA_INTERRUPT|SA_SHIRQ,
 			card->driver, (void *)chip)) {
 		snd_via82xx_free(chip);
-		snd_printk("unable to grab IRQ %d\n", chip->irq);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
 		return -EBUSY;
 	}
 	chip->irq = pci->irq;
@@ -1896,6 +1973,13 @@
 				break;
 			}
 		}
+		/* force to use VIA8233 or 8233A model according to
+		 * dxs_support module option
+		 */
+		if (dxs_support[dev] == 1)
+			chip_type = TYPE_VIA8233;
+		else if (dxs_support[dev] == 2)
+			chip_type = TYPE_VIA8233A;
 		if (chip_type == TYPE_VIA8233A)
 			strcpy(card->driver, "VIA8233A");
 		else
@@ -1909,7 +1993,6 @@
 		
 	if ((err = snd_via82xx_create(card, pci, chip_type, revision, ac97_clock[dev], &chip)) < 0)
 		goto __error;
-
 	if ((err = snd_via82xx_mixer_new(chip)) < 0)
 		goto __error;
 
@@ -1924,6 +2007,8 @@
 		} else {
 			if ((err = snd_via8233_pcm_new(chip)) < 0)
 				goto __error;
+			if (dxs_support[dev] == 3)
+				chip->dxs_fixed = 1;
 		}
 		if ((err = snd_via8233_init_misc(chip, dev)) < 0)
 			goto __error;
@@ -1935,6 +2020,8 @@
 	sprintf(card->longname, "%s at 0x%lx, irq %d",
 		card->shortname, chip->port, chip->irq);
 
+	snd_via82xx_proc_init(chip);
+
 	if ((err = snd_card_register(card)) < 0) {
 		snd_card_free(card);
 		return err;
diff -urN linux-2.5.70-bk6/sound/pci/vx222/Makefile linux-2.5.70-bk7/sound/pci/vx222/Makefile
--- linux-2.5.70-bk6/sound/pci/vx222/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/pci/vx222/Makefile	2003-06-02 04:39:13.000000000 -0700
@@ -0,0 +1,8 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-vx222-objs := vx222.o vx222_ops.o
+
+obj-$(CONFIG_SND_VX222) += snd-vx222.o
diff -urN linux-2.5.70-bk6/sound/pci/vx222/vx222.c linux-2.5.70-bk7/sound/pci/vx222/vx222.c
--- linux-2.5.70-bk6/sound/pci/vx222/vx222.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/pci/vx222/vx222.c	2003-06-02 04:39:13.000000000 -0700
@@ -0,0 +1,311 @@
+/*
+ * Driver for Digigram VX222 V2/Mic PCI soundcards
+ *
+ * Copyright (c) 2002 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 <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include "vx222.h"
+
+#define chip_t vx_core_t
+
+#define CARD_NAME "VX222"
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("Digigram VX222 V2/Mic");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Digigram," CARD_NAME "}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int mic[SNDRV_CARDS]; /* microphone */
+static int ibl[SNDRV_CARDS]; /* microphone */
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(mic, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(mic, "Enable Microphone.");
+MODULE_PARM_SYNTAX(mic, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC);
+MODULE_PARM(ibl, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(ibl, "Capture IBL size.");
+MODULE_PARM_SYNTAX(ibl, SNDRV_ENABLED);
+
+/*
+ */
+
+enum {
+	VX_PCI_VX222_OLD,
+	VX_PCI_VX222_NEW
+};
+
+static struct pci_device_id snd_vx222_ids[] __devinitdata = {
+	{ 0x10b5, 0x9050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VX_PCI_VX222_OLD, },   /* PLX */
+	{ 0x10b5, 0x9030, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VX_PCI_VX222_NEW, },   /* PLX */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_vx222_ids);
+
+
+/*
+ */
+
+static struct snd_vx_hardware vx222_old_hw = {
+
+	.name = "VX222/Old",
+	.type = VX_TYPE_BOARD,
+	/* hw specs */
+	.num_codecs = 1,
+	.num_ins = 1,
+	.num_outs = 1,
+	.output_level_max = VX_ANALOG_OUT_LEVEL_MAX,
+};
+
+static struct snd_vx_hardware vx222_v2_hw = {
+
+	.name = "VX222/v2",
+	.type = VX_TYPE_V2,
+	/* hw specs */
+	.num_codecs = 1,
+	.num_ins = 1,
+	.num_outs = 1,
+	.output_level_max = VX2_AKM_LEVEL_MAX,
+};
+
+static struct snd_vx_hardware vx222_mic_hw = {
+
+	.name = "VX222/Mic",
+	.type = VX_TYPE_MIC,
+	/* hw specs */
+	.num_codecs = 1,
+	.num_ins = 1,
+	.num_outs = 1,
+	.output_level_max = VX2_AKM_LEVEL_MAX,
+};
+
+
+/*
+ */
+static int snd_vx222_free(vx_core_t *chip)
+{
+	int i;
+	struct snd_vx222 *vx = (struct snd_vx222 *)chip;
+
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void*)chip);
+	for (i = 0; i < 2; i++) {
+		if (vx->port_res[i]) {
+			release_resource(vx->port_res[i]);
+			kfree_nocheck(vx->port_res[i]);
+		}
+	}
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_vx222_dev_free(snd_device_t *device)
+{
+	vx_core_t *chip = snd_magic_cast(vx_core_t, device->device_data, return -ENXIO);
+	return snd_vx222_free(chip);
+}
+
+
+static int __devinit snd_vx222_create(snd_card_t *card, struct pci_dev *pci,
+				      struct snd_vx_hardware *hw,
+				      struct snd_vx222 **rchip)
+{
+	vx_core_t *chip;
+	struct snd_vx222 *vx;
+	int i, err;
+	static snd_device_ops_t ops = {
+		.dev_free =	snd_vx222_dev_free,
+	};
+	struct snd_vx_ops *vx_ops;
+
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	pci_set_master(pci);
+
+	vx_ops = hw->type == VX_TYPE_BOARD ? &vx222_old_ops : &vx222_ops;
+	chip = snd_vx_create(card, hw, vx_ops,
+			     sizeof(struct snd_vx222) - sizeof(vx_core_t));
+	if (! chip)
+		return -ENOMEM;
+	vx = (struct snd_vx222 *)chip;
+
+	for (i = 0; i < 2; i++) {
+		if (!(pci_resource_flags(pci, i + 1) & IORESOURCE_IO)) {
+			snd_printk(KERN_ERR "invalid i/o resource %d\n", i + 1);
+			snd_vx222_free(chip);
+			return -ENOMEM;
+		}
+		vx->port[i] = pci_resource_start(pci, i + 1);
+		if ((vx->port_res[i] = request_region(vx->port[i], 0x60,
+						      CARD_NAME)) == NULL) {
+			snd_printk(KERN_ERR "unable to grab port 0x%lx\n", vx->port[i]);
+			snd_vx222_free(chip);
+			return -EBUSY;
+		}
+	}
+
+	if (request_irq(pci->irq, snd_vx_irq_handler, SA_INTERRUPT|SA_SHIRQ,
+			CARD_NAME, (void *) chip)) {
+		snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+		snd_vx222_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_vx222_free(chip);
+		return err;
+	}
+
+	*rchip = vx;
+	return 0;
+}
+
+
+static int __devinit snd_vx222_probe(struct pci_dev *pci,
+				     const struct pci_device_id *pci_id)
+{
+	static int dev;
+	snd_card_t *card;
+	struct snd_vx_hardware *hw;
+	struct snd_vx222 *vx;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	switch ((int)pci_id->driver_data) {
+	case VX_PCI_VX222_OLD:
+		hw = &vx222_old_hw;
+		break;
+	case VX_PCI_VX222_NEW:
+	default:
+		if (mic[dev])
+			hw = &vx222_mic_hw;
+		else
+			hw = &vx222_v2_hw;
+		break;
+	}
+	if ((err = snd_vx222_create(card, pci, hw, &vx)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	vx->core.ibl.size = ibl[dev];
+
+	sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %i",
+		card->shortname, vx->port[0], vx->port[1], vx->core.irq);
+	snd_printdd("%s at 0x%lx & 0x%lx, irq %i\n",
+		    card->shortname, vx->port[0], vx->port[1], vx->core.irq);
+
+	if ((err = snd_vx_hwdep_new(&vx->core)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	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_vx222_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	.name = "Digigram VX222",
+	.id_table = snd_vx222_ids,
+	.probe = snd_vx222_probe,
+	.remove = __devexit_p(snd_vx222_remove),
+};
+
+static int __init alsa_card_vx222_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "Digigram VX222 soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_vx222_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_vx222_init)
+module_exit(alsa_card_vx222_exit)
+
+#ifndef MODULE
+
+/* format is: snd-vx222=enable,index,id */
+
+static int __init alsa_card_vx222_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&enable[nr_dev]) == 2 &&
+	       get_option(&str,&index[nr_dev]) == 2 &&
+	       get_id(&str,&id[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-vx222=", alsa_card_vx222_setup);
+
+#endif /* ifndef MODULE */
diff -urN linux-2.5.70-bk6/sound/pci/vx222/vx222.h linux-2.5.70-bk7/sound/pci/vx222/vx222.h
--- linux-2.5.70-bk6/sound/pci/vx222/vx222.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/pci/vx222/vx222.h	2003-06-02 04:39:13.000000000 -0700
@@ -0,0 +1,114 @@
+/*
+ * Driver for Digigram VX222 PCI soundcards
+ *
+ * Copyright (c) 2002 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 __VX222_H
+#define __VX222_H
+
+#include <sound/vx_core.h>
+
+struct snd_vx222 {
+
+	vx_core_t core;
+
+	/* h/w config; for PLX and for DSP */
+	unsigned long port[2];
+	struct resource *port_res[2];
+
+	unsigned int regCDSP;	/* current CDSP register */
+	unsigned int regCFG;	/* current CFG register */
+	unsigned int regSELMIC;	/* current SELMIC reg. (for VX222 Mic) */
+
+	int input_level[2];	/* input level for vx222 mic */
+	int mic_level;		/* mic level for vx222 mic */
+};
+
+/* we use a lookup table with 148 values, see vx_mixer.c */
+#define VX2_AKM_LEVEL_MAX	0x93
+
+extern struct snd_vx_ops vx222_ops;
+extern struct snd_vx_ops vx222_old_ops;
+
+/* Offset of registers with base equal to portDSP. */
+#define VX_RESET_DMA_REGISTER_OFFSET    0x00000008
+
+/* Constants used to access the INTCSR register. */
+#define VX_INTCSR_VALUE                 0x00000001
+#define VX_PCI_INTERRUPT_MASK           0x00000040
+
+/* Constants used to access the CDSP register (0x20). */
+#define VX_CDSP_TEST1_MASK              0x00000080
+#define VX_CDSP_TOR1_MASK               0x00000040
+#define VX_CDSP_TOR2_MASK               0x00000020
+#define VX_CDSP_RESERVED0_0_MASK        0x00000010
+#define VX_CDSP_CODEC_RESET_MASK        0x00000008
+#define VX_CDSP_VALID_IRQ_MASK          0x00000004
+#define VX_CDSP_TEST0_MASK              0x00000002
+#define VX_CDSP_DSP_RESET_MASK          0x00000001
+
+#define VX_CDSP_GPIO_OUT_MASK           0x00000060
+#define VX_GPIO_OUT_BIT_OFFSET          5               // transform output to bit 0 and 1
+
+/* Constants used to access the CFG register (0x24). */
+#define VX_CFG_SYNCDSP_MASK             0x00000080
+#define VX_CFG_RESERVED0_0_MASK         0x00000040
+#define VX_CFG_RESERVED1_0_MASK         0x00000020
+#define VX_CFG_RESERVED2_0_MASK         0x00000010
+#define VX_CFG_DATAIN_SEL_MASK          0x00000008     // 0 (ana), 1 (UER)
+#define VX_CFG_RESERVED3_0_MASK         0x00000004
+#define VX_CFG_RESERVED4_0_MASK         0x00000002
+#define VX_CFG_CLOCKIN_SEL_MASK         0x00000001     // 0 (internal), 1 (AES/EBU)
+
+/* Constants used to access the STATUS register (0x30). */
+#define VX_STATUS_DATA_XICOR_MASK       0x00000080
+#define VX_STATUS_VAL_TEST1_MASK        0x00000040
+#define VX_STATUS_VAL_TEST0_MASK        0x00000020
+#define VX_STATUS_RESERVED0_MASK        0x00000010
+#define VX_STATUS_VAL_TOR1_MASK         0x00000008
+#define VX_STATUS_VAL_TOR0_MASK         0x00000004
+#define VX_STATUS_LEVEL_IN_MASK         0x00000002    // 6 dBu (0), 22 dBu (1)
+#define VX_STATUS_MEMIRQ_MASK           0x00000001
+
+#define VX_STATUS_GPIO_IN_MASK          0x0000000C
+#define VX_GPIO_IN_BIT_OFFSET           0             // leave input as bit 2 and 3
+
+/* Constants used to access the MICRO INPUT SELECT register (0x40). */
+#define MICRO_SELECT_INPUT_NORM        0x00
+#define MICRO_SELECT_INPUT_MUTE        0x01
+#define MICRO_SELECT_INPUT_LIMIT       0x02
+#define MICRO_SELECT_INPUT_MASK        0x03
+
+#define MICRO_SELECT_PREAMPLI_G_0      0x00
+#define MICRO_SELECT_PREAMPLI_G_1      0x04
+#define MICRO_SELECT_PREAMPLI_G_2      0x08
+#define MICRO_SELECT_PREAMPLI_G_3      0x0C
+#define MICRO_SELECT_PREAMPLI_MASK     0x0C
+#define MICRO_SELECT_PREAMPLI_OFFSET   2
+
+#define MICRO_SELECT_RAISE_COMPR       0x10
+
+#define MICRO_SELECT_NOISE_T_52DB      0x00
+#define MICRO_SELECT_NOISE_T_42DB      0x20
+#define MICRO_SELECT_NOISE_T_32DB      0x40
+#define MICRO_SELECT_NOISE_T_MASK      0x60
+
+#define MICRO_SELECT_PHANTOM_ALIM      0x80
+
+
+#endif /* __VX222_H */
diff -urN linux-2.5.70-bk6/sound/pci/vx222/vx222_ops.c linux-2.5.70-bk7/sound/pci/vx222/vx222_ops.c
--- linux-2.5.70-bk6/sound/pci/vx222/vx222_ops.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/pci/vx222/vx222_ops.c	2003-06-02 04:39:13.000000000 -0700
@@ -0,0 +1,1011 @@
+/*
+ * Driver for Digigram VX222 V2/Mic soundcards
+ *
+ * VX222-specific low-level routines
+ *
+ * Copyright (c) 2002 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/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <asm/io.h>
+#include "vx222.h"
+
+#define chip_t vx_core_t
+
+
+static int vx2_reg_offset[VX_REG_MAX] = {
+	[VX_ICR]    = 0x00,
+	[VX_CVR]    = 0x04,
+	[VX_ISR]    = 0x08,
+	[VX_IVR]    = 0x0c,
+	[VX_RXH]    = 0x14,
+	[VX_RXM]    = 0x18,
+	[VX_RXL]    = 0x1c,
+	[VX_DMA]    = 0x10,
+	[VX_CDSP]   = 0x20,
+	[VX_CFG]    = 0x24,
+	[VX_RUER]   = 0x28,
+	[VX_DATA]   = 0x2c,
+	[VX_STATUS] = 0x30,
+	[VX_LOFREQ] = 0x34,
+	[VX_HIFREQ] = 0x38,
+	[VX_CSUER]  = 0x3c,
+	[VX_SELMIC] = 0x40,
+	[VX_COMPOT] = 0x44, // Write: POTENTIOMETER ; Read: COMPRESSION LEVEL activate
+	[VX_SCOMPR] = 0x48, // Read: COMPRESSION THRESHOLD activate
+	[VX_GLIMIT] = 0x4c, // Read: LEVEL LIMITATION activate
+	[VX_INTCSR] = 0x4c, // VX_INTCSR_REGISTER_OFFSET
+	[VX_CNTRL]  = 0x50,		// VX_CNTRL_REGISTER_OFFSET
+	[VX_GPIOC]  = 0x54,		// VX_GPIOC (new with PLX9030)
+};
+
+static int vx2_reg_index[VX_REG_MAX] = {
+	[VX_ICR]	= 1,
+	[VX_CVR]	= 1,
+	[VX_ISR]	= 1,
+	[VX_IVR]	= 1,
+	[VX_RXH]	= 1,
+	[VX_RXM]	= 1,
+	[VX_RXL]	= 1,
+	[VX_DMA]	= 1,
+	[VX_CDSP]	= 1,
+	[VX_CFG]	= 1,
+	[VX_RUER]	= 1,
+	[VX_DATA]	= 1,
+	[VX_STATUS]	= 1,
+	[VX_LOFREQ]	= 1,
+	[VX_HIFREQ]	= 1,
+	[VX_CSUER]	= 1,
+	[VX_SELMIC]	= 1,
+	[VX_COMPOT]	= 1,
+	[VX_SCOMPR]	= 1,
+	[VX_GLIMIT]	= 1,
+	[VX_INTCSR]	= 0,	/* on the PLX */
+	[VX_CNTRL]	= 0,	/* on the PLX */
+	[VX_GPIOC]	= 0,	/* on the PLX */
+};
+
+inline static unsigned long vx2_reg_addr(vx_core_t *_chip, int reg)
+{
+	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+	return chip->port[vx2_reg_index[reg]] + vx2_reg_offset[reg];
+}
+
+/**
+ * snd_vx_inb - read a byte from the register
+ * @offset: register enum
+ */
+static unsigned char vx2_inb(vx_core_t *chip, int offset)
+{
+	return inb(vx2_reg_addr(chip, offset));
+}
+
+/**
+ * snd_vx_outb - write a byte on the register
+ * @offset: the register offset
+ * @val: the value to write
+ */
+static void vx2_outb(vx_core_t *chip, int offset, unsigned char val)
+{
+	outb(val, vx2_reg_addr(chip, offset));
+	//printk("outb: %x -> %x\n", val, vx2_reg_addr(chip, offset));
+}
+
+/**
+ * snd_vx_inl - read a 32bit word from the register
+ * @offset: register enum
+ */
+static unsigned int vx2_inl(vx_core_t *chip, int offset)
+{
+	return inl(vx2_reg_addr(chip, offset));
+}
+
+/**
+ * snd_vx_outl - write a 32bit word on the register
+ * @offset: the register enum
+ * @val: the value to write
+ */
+static void vx2_outl(vx_core_t *chip, int offset, unsigned int val)
+{
+	// printk("outl: %x -> %x\n", val, vx2_reg_addr(chip, offset));
+	outl(val, vx2_reg_addr(chip, offset));
+}
+
+/*
+ * redefine macros to call directly
+ */
+#undef vx_inb
+#define vx_inb(chip,reg)	vx2_inb((vx_core_t*)(chip), VX_##reg)
+#undef vx_outb
+#define vx_outb(chip,reg,val)	vx2_outb((vx_core_t*)(chip), VX_##reg, val)
+#undef vx_inl
+#define vx_inl(chip,reg)	vx2_inl((vx_core_t*)(chip), VX_##reg)
+#undef vx_outl
+#define vx_outl(chip,reg,val)	vx2_outl((vx_core_t*)(chip), VX_##reg, val)
+
+
+/*
+ * vx_reset_dsp - reset the DSP
+ */
+
+#define XX_DSP_RESET_WAIT_TIME		2	/* ms */
+
+static void vx2_reset_dsp(vx_core_t *_chip)
+{
+	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+
+	/* set the reset dsp bit to 0 */
+	vx_outl(chip, CDSP, chip->regCDSP & ~VX_CDSP_DSP_RESET_MASK);
+
+	snd_vx_delay(_chip, XX_DSP_RESET_WAIT_TIME);
+
+	chip->regCDSP |= VX_CDSP_DSP_RESET_MASK;
+	/* set the reset dsp bit to 1 */
+	vx_outl(chip, CDSP, chip->regCDSP);
+}
+
+
+static int vx2_test_xilinx(vx_core_t *_chip)
+{
+	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+	unsigned int data;
+
+	snd_printdd("testing xilinx...\n");
+	/* This test uses several write/read sequences on TEST0 and TEST1 bits
+	 * to figure out whever or not the xilinx was correctly loaded
+	 */
+
+	/* We write 1 on CDSP.TEST0. We should get 0 on STATUS.TEST0. */
+	vx_outl(chip, CDSP, chip->regCDSP | VX_CDSP_TEST0_MASK);
+	vx_inl(chip, ISR);
+	data = vx_inl(chip, STATUS);
+	if ((data & VX_STATUS_VAL_TEST0_MASK) == VX_STATUS_VAL_TEST0_MASK) {
+		snd_printdd("bad!\n");
+		return -ENODEV;
+	}
+
+	/* We write 0 on CDSP.TEST0. We should get 1 on STATUS.TEST0. */
+	vx_outl(chip, CDSP, chip->regCDSP & ~VX_CDSP_TEST0_MASK);
+	vx_inl(chip, ISR);
+	data = vx_inl(chip, STATUS);
+	if (! (data & VX_STATUS_VAL_TEST0_MASK)) {
+		snd_printdd("bad! #2\n");
+		return -ENODEV;
+	}
+
+	if (_chip->type == VX_TYPE_BOARD) {
+		/* not implemented on VX_2_BOARDS */
+		/* We write 1 on CDSP.TEST1. We should get 0 on STATUS.TEST1. */
+		vx_outl(chip, CDSP, chip->regCDSP | VX_CDSP_TEST1_MASK);
+		vx_inl(chip, ISR);
+		data = vx_inl(chip, STATUS);
+		if ((data & VX_STATUS_VAL_TEST1_MASK) == VX_STATUS_VAL_TEST1_MASK) {
+			snd_printdd("bad! #3\n");
+			return -ENODEV;
+		}
+
+		/* We write 0 on CDSP.TEST1. We should get 1 on STATUS.TEST1. */
+		vx_outl(chip, CDSP, chip->regCDSP & ~VX_CDSP_TEST1_MASK);
+		vx_inl(chip, ISR);
+		data = vx_inl(chip, STATUS);
+		if (! (data & VX_STATUS_VAL_TEST1_MASK)) {
+			snd_printdd("bad! #4\n");
+			return -ENODEV;
+		}
+	}
+	snd_printdd("ok, xilinx fine.\n");
+	return 0;
+}
+
+
+/**
+ * vx_setup_pseudo_dma - set up the pseudo dma read/write mode.
+ * @do_write: 0 = read, 1 = set up for DMA write
+ */
+static void vx2_setup_pseudo_dma(vx_core_t *chip, int do_write)
+{
+	/* Interrupt mode and HREQ pin enabled for host transmit data transfers
+	 * (in case of the use of the pseudo-dma facility).
+	 */
+	vx_outl(chip, ICR, do_write ? ICR_TREQ : ICR_RREQ);
+
+	/* Reset the pseudo-dma register (in case of the use of the
+	 * pseudo-dma facility).
+	 */
+	vx_outl(chip, RESET_DMA, 0);
+}
+
+/*
+ * vx_release_pseudo_dma - disable the pseudo-DMA mode
+ */
+inline static void vx2_release_pseudo_dma(vx_core_t *chip)
+{
+	/* HREQ pin disabled. */
+	vx_outl(chip, ICR, 0);
+}
+
+
+
+/* pseudo-dma write */
+static void vx2_dma_write(vx_core_t *chip, snd_pcm_runtime_t *runtime,
+			  vx_pipe_t *pipe, int count)
+{
+	unsigned long port = vx2_reg_addr(chip, VX_DMA);
+	int offset = pipe->hw_ptr;
+	u32 *addr = (u32 *)(runtime->dma_area + offset);
+
+	snd_assert(count % 4 == 0, return);
+
+	vx2_setup_pseudo_dma(chip, 1);
+
+	/* Transfer using pseudo-dma.
+	 */
+	if (offset + count > pipe->buffer_bytes) {
+		int length = pipe->buffer_bytes - offset;
+		count -= length;
+		length >>= 2; /* in 32bit words */
+		/* Transfer using pseudo-dma. */
+		while (length-- > 0) {
+			outl(cpu_to_le32(*addr), port);
+			addr++;
+		}
+		addr = (u32 *)runtime->dma_area;
+		pipe->hw_ptr = 0;
+	}
+	pipe->hw_ptr += count;
+	count >>= 2; /* in 32bit words */
+	/* Transfer using pseudo-dma. */
+	while (count-- > 0) {
+		outl(cpu_to_le32(*addr), port);
+		addr++;
+	}
+
+	vx2_release_pseudo_dma(chip);
+}
+
+
+/* pseudo dma read */
+static void vx2_dma_read(vx_core_t *chip, snd_pcm_runtime_t *runtime,
+			 vx_pipe_t *pipe, int count)
+{
+	int offset = pipe->hw_ptr;
+	u32 *addr = (u32 *)(runtime->dma_area + offset);
+	unsigned long port = vx2_reg_addr(chip, VX_DMA);
+
+	snd_assert(count % 4 == 0, return);
+
+	vx2_setup_pseudo_dma(chip, 0);
+	/* Transfer using pseudo-dma.
+	 */
+	if (offset + count > pipe->buffer_bytes) {
+		int length = pipe->buffer_bytes - offset;
+		count -= length;
+		length >>= 2; /* in 32bit words */
+		/* Transfer using pseudo-dma. */
+		while (length-- > 0)
+			*addr++ = le32_to_cpu(inl(port));
+		addr = (u32 *)runtime->dma_area;
+		pipe->hw_ptr = 0;
+	}
+	pipe->hw_ptr += count;
+	count >>= 2; /* in 32bit words */
+	/* Transfer using pseudo-dma. */
+	while (count-- > 0)
+		*addr++ = le32_to_cpu(inl(port));
+
+	vx2_release_pseudo_dma(chip);
+}
+
+#define VX_XILINX_RESET_MASK        0x40000000
+#define VX_USERBIT0_MASK            0x00000004
+#define VX_USERBIT1_MASK            0x00000020
+#define VX_CNTRL_REGISTER_VALUE     0x00172012
+
+/*
+ * transfer counts bits to PLX
+ */
+static int put_xilinx_data(vx_core_t *chip, unsigned int port, unsigned int counts, unsigned char data)
+{
+	unsigned int i;
+
+	for (i = 0; i < counts; i++) {
+		unsigned int val;
+
+		/* set the clock bit to 0. */
+		val = VX_CNTRL_REGISTER_VALUE & ~VX_USERBIT0_MASK;
+		vx2_outl(chip, port, val);
+		vx2_inl(chip, port);
+		udelay(1);
+
+		if (data & (1 << i))
+			val |= VX_USERBIT1_MASK;
+		else
+			val &= ~VX_USERBIT1_MASK;
+		vx2_outl(chip, port, val);
+		vx2_inl(chip, port);
+
+		/* set the clock bit to 1. */
+		val |= VX_USERBIT0_MASK;
+		vx2_outl(chip, port, val);
+		vx2_inl(chip, port);
+		udelay(1);
+	}
+	return 0;
+}
+
+/*
+ * load the xilinx image
+ */
+static int vx2_load_xilinx_binary(vx_core_t *chip, const snd_hwdep_dsp_image_t *xilinx)
+{
+	unsigned int i;
+	unsigned int port;
+	unsigned char *image, data;
+
+	/* XILINX reset (wait at least 1 milisecond between reset on and off). */
+	vx_outl(chip, CNTRL, VX_CNTRL_REGISTER_VALUE | VX_XILINX_RESET_MASK);
+	vx_inl(chip, CNTRL);
+	snd_vx_delay(chip, 10);
+	vx_outl(chip, CNTRL, VX_CNTRL_REGISTER_VALUE);
+	vx_inl(chip, CNTRL);
+	snd_vx_delay(chip, 10);
+
+	if (chip->type == VX_TYPE_BOARD)
+		port = VX_CNTRL;
+	else
+		port = VX_GPIOC; /* VX222 V2 and VX222_MIC_BOARD with new PLX9030 use this register */
+
+	image = xilinx->image;
+	for (i = 0; i < xilinx->length; i++, image++) {
+		__get_user(data, image);
+		if (put_xilinx_data(chip, port, 8, data) < 0)
+			return -EINVAL;
+		/* don't take too much time in this loop... */
+		if (need_resched()) {
+			if (current->state != TASK_RUNNING)
+				set_current_state(TASK_RUNNING);
+			schedule();
+		}
+	}
+	put_xilinx_data(chip, port, 4, 0xff); /* end signature */
+
+	snd_vx_delay(chip, 200);
+
+	/* test after loading (is buggy with VX222) */
+	if (chip->type != VX_TYPE_BOARD) {
+		/* Test if load successful: test bit 8 of register GPIOC (VX222: use CNTRL) ! */
+		i = vx_inl(chip, GPIOC);
+		if (i & 0x0100)
+			return 0;
+		snd_printk(KERN_ERR "vx222: xilinx test failed after load, GPIOC=0x%x\n", i);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+	
+/*
+ * load the boot/dsp images
+ */
+static int vx2_load_dsp(vx_core_t *vx, const snd_hwdep_dsp_image_t *dsp)
+{
+	int err;
+
+	if (*dsp->name)
+		snd_printdd("loading dsp [%d] %s, size = %d\n", dsp->index, dsp->name, dsp->length);
+	switch (dsp->index) {
+	case 0:
+		/* xilinx image */
+		if ((err = vx2_load_xilinx_binary(vx, dsp)) < 0)
+			return err;
+		if ((err = vx2_test_xilinx(vx)) < 0)
+			return err;
+		return 0;
+	case 1:
+		/* DSP boot */
+		return snd_vx_dsp_boot(vx, dsp);
+	case 2:
+		/* DSP image */
+		return snd_vx_dsp_load(vx, dsp);
+	default:
+		snd_BUG();
+		return -EINVAL;
+	}
+}
+
+
+/*
+ * vx_test_and_ack - test and acknowledge interrupt
+ *
+ * called from irq hander, too
+ *
+ * spinlock held!
+ */
+static int vx2_test_and_ack(vx_core_t *chip)
+{
+	/* not booted yet? */
+	if (! (chip->chip_status & VX_STAT_XILINX_LOADED))
+		return -ENXIO;
+
+	if (! (vx_inl(chip, STATUS) & VX_STATUS_MEMIRQ_MASK))
+		return -EIO;
+	
+	/* ok, interrupts generated, now ack it */
+	/* set ACQUIT bit up and down */
+	vx_outl(chip, STATUS, 0);
+	/* useless read just to spend some time and maintain
+	 * the ACQUIT signal up for a while ( a bus cycle )
+	 */
+	vx_inl(chip, STATUS);
+	/* ack */
+	vx_outl(chip, STATUS, VX_STATUS_MEMIRQ_MASK);
+	/* useless read just to spend some time and maintain
+	 * the ACQUIT signal up for a while ( a bus cycle ) */
+	vx_inl(chip, STATUS);
+	/* clear */
+	vx_outl(chip, STATUS, 0);
+
+	return 0;
+}
+
+
+/*
+ * vx_validate_irq - enable/disable IRQ
+ */
+static void vx2_validate_irq(vx_core_t *_chip, int enable)
+{
+	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+
+	/* Set the interrupt enable bit to 1 in CDSP register */
+	if (enable) {
+		/* Set the PCI interrupt enable bit to 1.*/
+		vx_outl(chip, INTCSR, VX_INTCSR_VALUE|VX_PCI_INTERRUPT_MASK);
+		chip->regCDSP |= VX_CDSP_VALID_IRQ_MASK;
+	} else {
+		/* Set the PCI interrupt enable bit to 0. */
+		vx_outl(chip, INTCSR, VX_INTCSR_VALUE&~VX_PCI_INTERRUPT_MASK);
+		chip->regCDSP &= ~VX_CDSP_VALID_IRQ_MASK;
+	}
+	vx_outl(chip, CDSP, chip->regCDSP);
+}
+
+
+/*
+ * write an AKM codec data (24bit)
+ */
+static void vx2_write_codec_reg(vx_core_t *chip, unsigned int data)
+{
+	unsigned int i;
+
+	vx_inl(chip, HIFREQ);
+
+	/* We have to send 24 bits (3 x 8 bits). Start with most signif. Bit */
+	for (i = 0; i < 24; i++, data <<= 1)
+		vx_outl(chip, DATA, ((data & 0x800000) ? VX_DATA_CODEC_MASK : 0));
+	/* Terminate access to codec registers */
+	vx_inl(chip, RUER);
+}
+
+
+#define AKM_CODEC_POWER_CONTROL_CMD 0xA007
+#define AKM_CODEC_RESET_ON_CMD      0xA100
+#define AKM_CODEC_RESET_OFF_CMD     0xA103
+#define AKM_CODEC_CLOCK_FORMAT_CMD  0xA240
+#define AKM_CODEC_MUTE_CMD          0xA38D
+#define AKM_CODEC_UNMUTE_CMD        0xA30D
+#define AKM_CODEC_LEFT_LEVEL_CMD    0xA400
+#define AKM_CODEC_RIGHT_LEVEL_CMD   0xA500
+
+static const u8 vx2_akm_gains_lut[VX2_AKM_LEVEL_MAX+1] = {
+    0x7f,       // [000] =  +0.000 dB  ->  AKM(0x7f) =  +0.000 dB  error(+0.000 dB)
+    0x7d,       // [001] =  -0.500 dB  ->  AKM(0x7d) =  -0.572 dB  error(-0.072 dB)
+    0x7c,       // [002] =  -1.000 dB  ->  AKM(0x7c) =  -0.873 dB  error(+0.127 dB)
+    0x7a,       // [003] =  -1.500 dB  ->  AKM(0x7a) =  -1.508 dB  error(-0.008 dB)
+    0x79,       // [004] =  -2.000 dB  ->  AKM(0x79) =  -1.844 dB  error(+0.156 dB)
+    0x77,       // [005] =  -2.500 dB  ->  AKM(0x77) =  -2.557 dB  error(-0.057 dB)
+    0x76,       // [006] =  -3.000 dB  ->  AKM(0x76) =  -2.937 dB  error(+0.063 dB)
+    0x75,       // [007] =  -3.500 dB  ->  AKM(0x75) =  -3.334 dB  error(+0.166 dB)
+    0x73,       // [008] =  -4.000 dB  ->  AKM(0x73) =  -4.188 dB  error(-0.188 dB)
+    0x72,       // [009] =  -4.500 dB  ->  AKM(0x72) =  -4.648 dB  error(-0.148 dB)
+    0x71,       // [010] =  -5.000 dB  ->  AKM(0x71) =  -5.134 dB  error(-0.134 dB)
+    0x70,       // [011] =  -5.500 dB  ->  AKM(0x70) =  -5.649 dB  error(-0.149 dB)
+    0x6f,       // [012] =  -6.000 dB  ->  AKM(0x6f) =  -6.056 dB  error(-0.056 dB)
+    0x6d,       // [013] =  -6.500 dB  ->  AKM(0x6d) =  -6.631 dB  error(-0.131 dB)
+    0x6c,       // [014] =  -7.000 dB  ->  AKM(0x6c) =  -6.933 dB  error(+0.067 dB)
+    0x6a,       // [015] =  -7.500 dB  ->  AKM(0x6a) =  -7.571 dB  error(-0.071 dB)
+    0x69,       // [016] =  -8.000 dB  ->  AKM(0x69) =  -7.909 dB  error(+0.091 dB)
+    0x67,       // [017] =  -8.500 dB  ->  AKM(0x67) =  -8.626 dB  error(-0.126 dB)
+    0x66,       // [018] =  -9.000 dB  ->  AKM(0x66) =  -9.008 dB  error(-0.008 dB)
+    0x65,       // [019] =  -9.500 dB  ->  AKM(0x65) =  -9.407 dB  error(+0.093 dB)
+    0x64,       // [020] = -10.000 dB  ->  AKM(0x64) =  -9.826 dB  error(+0.174 dB)
+    0x62,       // [021] = -10.500 dB  ->  AKM(0x62) = -10.730 dB  error(-0.230 dB)
+    0x61,       // [022] = -11.000 dB  ->  AKM(0x61) = -11.219 dB  error(-0.219 dB)
+    0x60,       // [023] = -11.500 dB  ->  AKM(0x60) = -11.738 dB  error(-0.238 dB)
+    0x5f,       // [024] = -12.000 dB  ->  AKM(0x5f) = -12.149 dB  error(-0.149 dB)
+    0x5e,       // [025] = -12.500 dB  ->  AKM(0x5e) = -12.434 dB  error(+0.066 dB)
+    0x5c,       // [026] = -13.000 dB  ->  AKM(0x5c) = -13.033 dB  error(-0.033 dB)
+    0x5b,       // [027] = -13.500 dB  ->  AKM(0x5b) = -13.350 dB  error(+0.150 dB)
+    0x59,       // [028] = -14.000 dB  ->  AKM(0x59) = -14.018 dB  error(-0.018 dB)
+    0x58,       // [029] = -14.500 dB  ->  AKM(0x58) = -14.373 dB  error(+0.127 dB)
+    0x56,       // [030] = -15.000 dB  ->  AKM(0x56) = -15.130 dB  error(-0.130 dB)
+    0x55,       // [031] = -15.500 dB  ->  AKM(0x55) = -15.534 dB  error(-0.034 dB)
+    0x54,       // [032] = -16.000 dB  ->  AKM(0x54) = -15.958 dB  error(+0.042 dB)
+    0x53,       // [033] = -16.500 dB  ->  AKM(0x53) = -16.404 dB  error(+0.096 dB)
+    0x52,       // [034] = -17.000 dB  ->  AKM(0x52) = -16.874 dB  error(+0.126 dB)
+    0x51,       // [035] = -17.500 dB  ->  AKM(0x51) = -17.371 dB  error(+0.129 dB)
+    0x50,       // [036] = -18.000 dB  ->  AKM(0x50) = -17.898 dB  error(+0.102 dB)
+    0x4e,       // [037] = -18.500 dB  ->  AKM(0x4e) = -18.605 dB  error(-0.105 dB)
+    0x4d,       // [038] = -19.000 dB  ->  AKM(0x4d) = -18.905 dB  error(+0.095 dB)
+    0x4b,       // [039] = -19.500 dB  ->  AKM(0x4b) = -19.538 dB  error(-0.038 dB)
+    0x4a,       // [040] = -20.000 dB  ->  AKM(0x4a) = -19.872 dB  error(+0.128 dB)
+    0x48,       // [041] = -20.500 dB  ->  AKM(0x48) = -20.583 dB  error(-0.083 dB)
+    0x47,       // [042] = -21.000 dB  ->  AKM(0x47) = -20.961 dB  error(+0.039 dB)
+    0x46,       // [043] = -21.500 dB  ->  AKM(0x46) = -21.356 dB  error(+0.144 dB)
+    0x44,       // [044] = -22.000 dB  ->  AKM(0x44) = -22.206 dB  error(-0.206 dB)
+    0x43,       // [045] = -22.500 dB  ->  AKM(0x43) = -22.664 dB  error(-0.164 dB)
+    0x42,       // [046] = -23.000 dB  ->  AKM(0x42) = -23.147 dB  error(-0.147 dB)
+    0x41,       // [047] = -23.500 dB  ->  AKM(0x41) = -23.659 dB  error(-0.159 dB)
+    0x40,       // [048] = -24.000 dB  ->  AKM(0x40) = -24.203 dB  error(-0.203 dB)
+    0x3f,       // [049] = -24.500 dB  ->  AKM(0x3f) = -24.635 dB  error(-0.135 dB)
+    0x3e,       // [050] = -25.000 dB  ->  AKM(0x3e) = -24.935 dB  error(+0.065 dB)
+    0x3c,       // [051] = -25.500 dB  ->  AKM(0x3c) = -25.569 dB  error(-0.069 dB)
+    0x3b,       // [052] = -26.000 dB  ->  AKM(0x3b) = -25.904 dB  error(+0.096 dB)
+    0x39,       // [053] = -26.500 dB  ->  AKM(0x39) = -26.615 dB  error(-0.115 dB)
+    0x38,       // [054] = -27.000 dB  ->  AKM(0x38) = -26.994 dB  error(+0.006 dB)
+    0x37,       // [055] = -27.500 dB  ->  AKM(0x37) = -27.390 dB  error(+0.110 dB)
+    0x36,       // [056] = -28.000 dB  ->  AKM(0x36) = -27.804 dB  error(+0.196 dB)
+    0x34,       // [057] = -28.500 dB  ->  AKM(0x34) = -28.699 dB  error(-0.199 dB)
+    0x33,       // [058] = -29.000 dB  ->  AKM(0x33) = -29.183 dB  error(-0.183 dB)
+    0x32,       // [059] = -29.500 dB  ->  AKM(0x32) = -29.696 dB  error(-0.196 dB)
+    0x31,       // [060] = -30.000 dB  ->  AKM(0x31) = -30.241 dB  error(-0.241 dB)
+    0x31,       // [061] = -30.500 dB  ->  AKM(0x31) = -30.241 dB  error(+0.259 dB)
+    0x30,       // [062] = -31.000 dB  ->  AKM(0x30) = -30.823 dB  error(+0.177 dB)
+    0x2e,       // [063] = -31.500 dB  ->  AKM(0x2e) = -31.610 dB  error(-0.110 dB)
+    0x2d,       // [064] = -32.000 dB  ->  AKM(0x2d) = -31.945 dB  error(+0.055 dB)
+    0x2b,       // [065] = -32.500 dB  ->  AKM(0x2b) = -32.659 dB  error(-0.159 dB)
+    0x2a,       // [066] = -33.000 dB  ->  AKM(0x2a) = -33.038 dB  error(-0.038 dB)
+    0x29,       // [067] = -33.500 dB  ->  AKM(0x29) = -33.435 dB  error(+0.065 dB)
+    0x28,       // [068] = -34.000 dB  ->  AKM(0x28) = -33.852 dB  error(+0.148 dB)
+    0x27,       // [069] = -34.500 dB  ->  AKM(0x27) = -34.289 dB  error(+0.211 dB)
+    0x25,       // [070] = -35.000 dB  ->  AKM(0x25) = -35.235 dB  error(-0.235 dB)
+    0x24,       // [071] = -35.500 dB  ->  AKM(0x24) = -35.750 dB  error(-0.250 dB)
+    0x24,       // [072] = -36.000 dB  ->  AKM(0x24) = -35.750 dB  error(+0.250 dB)
+    0x23,       // [073] = -36.500 dB  ->  AKM(0x23) = -36.297 dB  error(+0.203 dB)
+    0x22,       // [074] = -37.000 dB  ->  AKM(0x22) = -36.881 dB  error(+0.119 dB)
+    0x21,       // [075] = -37.500 dB  ->  AKM(0x21) = -37.508 dB  error(-0.008 dB)
+    0x20,       // [076] = -38.000 dB  ->  AKM(0x20) = -38.183 dB  error(-0.183 dB)
+    0x1f,       // [077] = -38.500 dB  ->  AKM(0x1f) = -38.726 dB  error(-0.226 dB)
+    0x1e,       // [078] = -39.000 dB  ->  AKM(0x1e) = -39.108 dB  error(-0.108 dB)
+    0x1d,       // [079] = -39.500 dB  ->  AKM(0x1d) = -39.507 dB  error(-0.007 dB)
+    0x1c,       // [080] = -40.000 dB  ->  AKM(0x1c) = -39.926 dB  error(+0.074 dB)
+    0x1b,       // [081] = -40.500 dB  ->  AKM(0x1b) = -40.366 dB  error(+0.134 dB)
+    0x1a,       // [082] = -41.000 dB  ->  AKM(0x1a) = -40.829 dB  error(+0.171 dB)
+    0x19,       // [083] = -41.500 dB  ->  AKM(0x19) = -41.318 dB  error(+0.182 dB)
+    0x18,       // [084] = -42.000 dB  ->  AKM(0x18) = -41.837 dB  error(+0.163 dB)
+    0x17,       // [085] = -42.500 dB  ->  AKM(0x17) = -42.389 dB  error(+0.111 dB)
+    0x16,       // [086] = -43.000 dB  ->  AKM(0x16) = -42.978 dB  error(+0.022 dB)
+    0x15,       // [087] = -43.500 dB  ->  AKM(0x15) = -43.610 dB  error(-0.110 dB)
+    0x14,       // [088] = -44.000 dB  ->  AKM(0x14) = -44.291 dB  error(-0.291 dB)
+    0x14,       // [089] = -44.500 dB  ->  AKM(0x14) = -44.291 dB  error(+0.209 dB)
+    0x13,       // [090] = -45.000 dB  ->  AKM(0x13) = -45.031 dB  error(-0.031 dB)
+    0x12,       // [091] = -45.500 dB  ->  AKM(0x12) = -45.840 dB  error(-0.340 dB)
+    0x12,       // [092] = -46.000 dB  ->  AKM(0x12) = -45.840 dB  error(+0.160 dB)
+    0x11,       // [093] = -46.500 dB  ->  AKM(0x11) = -46.731 dB  error(-0.231 dB)
+    0x11,       // [094] = -47.000 dB  ->  AKM(0x11) = -46.731 dB  error(+0.269 dB)
+    0x10,       // [095] = -47.500 dB  ->  AKM(0x10) = -47.725 dB  error(-0.225 dB)
+    0x10,       // [096] = -48.000 dB  ->  AKM(0x10) = -47.725 dB  error(+0.275 dB)
+    0x0f,       // [097] = -48.500 dB  ->  AKM(0x0f) = -48.553 dB  error(-0.053 dB)
+    0x0e,       // [098] = -49.000 dB  ->  AKM(0x0e) = -49.152 dB  error(-0.152 dB)
+    0x0d,       // [099] = -49.500 dB  ->  AKM(0x0d) = -49.796 dB  error(-0.296 dB)
+    0x0d,       // [100] = -50.000 dB  ->  AKM(0x0d) = -49.796 dB  error(+0.204 dB)
+    0x0c,       // [101] = -50.500 dB  ->  AKM(0x0c) = -50.491 dB  error(+0.009 dB)
+    0x0b,       // [102] = -51.000 dB  ->  AKM(0x0b) = -51.247 dB  error(-0.247 dB)
+    0x0b,       // [103] = -51.500 dB  ->  AKM(0x0b) = -51.247 dB  error(+0.253 dB)
+    0x0a,       // [104] = -52.000 dB  ->  AKM(0x0a) = -52.075 dB  error(-0.075 dB)
+    0x0a,       // [105] = -52.500 dB  ->  AKM(0x0a) = -52.075 dB  error(+0.425 dB)
+    0x09,       // [106] = -53.000 dB  ->  AKM(0x09) = -52.990 dB  error(+0.010 dB)
+    0x09,       // [107] = -53.500 dB  ->  AKM(0x09) = -52.990 dB  error(+0.510 dB)
+    0x08,       // [108] = -54.000 dB  ->  AKM(0x08) = -54.013 dB  error(-0.013 dB)
+    0x08,       // [109] = -54.500 dB  ->  AKM(0x08) = -54.013 dB  error(+0.487 dB)
+    0x07,       // [110] = -55.000 dB  ->  AKM(0x07) = -55.173 dB  error(-0.173 dB)
+    0x07,       // [111] = -55.500 dB  ->  AKM(0x07) = -55.173 dB  error(+0.327 dB)
+    0x06,       // [112] = -56.000 dB  ->  AKM(0x06) = -56.512 dB  error(-0.512 dB)
+    0x06,       // [113] = -56.500 dB  ->  AKM(0x06) = -56.512 dB  error(-0.012 dB)
+    0x06,       // [114] = -57.000 dB  ->  AKM(0x06) = -56.512 dB  error(+0.488 dB)
+    0x05,       // [115] = -57.500 dB  ->  AKM(0x05) = -58.095 dB  error(-0.595 dB)
+    0x05,       // [116] = -58.000 dB  ->  AKM(0x05) = -58.095 dB  error(-0.095 dB)
+    0x05,       // [117] = -58.500 dB  ->  AKM(0x05) = -58.095 dB  error(+0.405 dB)
+    0x05,       // [118] = -59.000 dB  ->  AKM(0x05) = -58.095 dB  error(+0.905 dB)
+    0x04,       // [119] = -59.500 dB  ->  AKM(0x04) = -60.034 dB  error(-0.534 dB)
+    0x04,       // [120] = -60.000 dB  ->  AKM(0x04) = -60.034 dB  error(-0.034 dB)
+    0x04,       // [121] = -60.500 dB  ->  AKM(0x04) = -60.034 dB  error(+0.466 dB)
+    0x04,       // [122] = -61.000 dB  ->  AKM(0x04) = -60.034 dB  error(+0.966 dB)
+    0x03,       // [123] = -61.500 dB  ->  AKM(0x03) = -62.532 dB  error(-1.032 dB)
+    0x03,       // [124] = -62.000 dB  ->  AKM(0x03) = -62.532 dB  error(-0.532 dB)
+    0x03,       // [125] = -62.500 dB  ->  AKM(0x03) = -62.532 dB  error(-0.032 dB)
+    0x03,       // [126] = -63.000 dB  ->  AKM(0x03) = -62.532 dB  error(+0.468 dB)
+    0x03,       // [127] = -63.500 dB  ->  AKM(0x03) = -62.532 dB  error(+0.968 dB)
+    0x03,       // [128] = -64.000 dB  ->  AKM(0x03) = -62.532 dB  error(+1.468 dB)
+    0x02,       // [129] = -64.500 dB  ->  AKM(0x02) = -66.054 dB  error(-1.554 dB)
+    0x02,       // [130] = -65.000 dB  ->  AKM(0x02) = -66.054 dB  error(-1.054 dB)
+    0x02,       // [131] = -65.500 dB  ->  AKM(0x02) = -66.054 dB  error(-0.554 dB)
+    0x02,       // [132] = -66.000 dB  ->  AKM(0x02) = -66.054 dB  error(-0.054 dB)
+    0x02,       // [133] = -66.500 dB  ->  AKM(0x02) = -66.054 dB  error(+0.446 dB)
+    0x02,       // [134] = -67.000 dB  ->  AKM(0x02) = -66.054 dB  error(+0.946 dB)
+    0x02,       // [135] = -67.500 dB  ->  AKM(0x02) = -66.054 dB  error(+1.446 dB)
+    0x02,       // [136] = -68.000 dB  ->  AKM(0x02) = -66.054 dB  error(+1.946 dB)
+    0x02,       // [137] = -68.500 dB  ->  AKM(0x02) = -66.054 dB  error(+2.446 dB)
+    0x02,       // [138] = -69.000 dB  ->  AKM(0x02) = -66.054 dB  error(+2.946 dB)
+    0x01,       // [139] = -69.500 dB  ->  AKM(0x01) = -72.075 dB  error(-2.575 dB)
+    0x01,       // [140] = -70.000 dB  ->  AKM(0x01) = -72.075 dB  error(-2.075 dB)
+    0x01,       // [141] = -70.500 dB  ->  AKM(0x01) = -72.075 dB  error(-1.575 dB)
+    0x01,       // [142] = -71.000 dB  ->  AKM(0x01) = -72.075 dB  error(-1.075 dB)
+    0x01,       // [143] = -71.500 dB  ->  AKM(0x01) = -72.075 dB  error(-0.575 dB)
+    0x01,       // [144] = -72.000 dB  ->  AKM(0x01) = -72.075 dB  error(-0.075 dB)
+    0x01,       // [145] = -72.500 dB  ->  AKM(0x01) = -72.075 dB  error(+0.425 dB)
+    0x01,       // [146] = -73.000 dB  ->  AKM(0x01) = -72.075 dB  error(+0.925 dB)
+    0x00};      // [147] = -73.500 dB  ->  AKM(0x00) =  mute       error(+infini)
+
+/*
+ * pseudo-codec write entry
+ */
+static void vx2_write_akm(vx_core_t *chip, int reg, unsigned int data)
+{
+	unsigned int val;
+
+	if (reg == XX_CODEC_DAC_CONTROL_REGISTER) {
+		vx2_write_codec_reg(chip, data ? AKM_CODEC_MUTE_CMD : AKM_CODEC_UNMUTE_CMD);
+		return;
+	}
+
+	/* `data' is a value between 0x0 and VX2_AKM_LEVEL_MAX = 0x093, in the case of the AKM codecs, we need
+	   a look up table, as there is no linear matching between the driver codec values
+	   and the real dBu value
+	*/
+	snd_assert(data < sizeof(vx2_akm_gains_lut), return);
+
+	switch (reg) {
+	case XX_CODEC_LEVEL_LEFT_REGISTER:
+		val = AKM_CODEC_LEFT_LEVEL_CMD;
+		break;
+	case XX_CODEC_LEVEL_RIGHT_REGISTER:
+		val = AKM_CODEC_RIGHT_LEVEL_CMD;
+		break;
+	default:
+		snd_BUG();
+		return;
+	}
+	val |= vx2_akm_gains_lut[data];
+
+	vx2_write_codec_reg(chip, val);
+}
+
+
+/*
+ * write codec bit for old VX222 board
+ */
+static void vx2_old_write_codec_bit(vx_core_t *chip, int codec, unsigned int data)
+{
+	int i;
+
+	/* activate access to codec registers */
+	vx_inl(chip, HIFREQ);
+
+	for (i = 0; i < 24; i++, data <<= 1)
+		vx_outl(chip, DATA, ((data & 0x800000) ? VX_DATA_CODEC_MASK : 0));
+
+	/* Terminate access to codec registers */
+	vx_inl(chip, RUER);
+}
+
+
+/*
+ * reset codec bit
+ */
+static void vx2_reset_codec(vx_core_t *_chip)
+{
+	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+
+	/* Set the reset CODEC bit to 0. */
+	vx_outl(chip, CDSP, chip->regCDSP &~ VX_CDSP_CODEC_RESET_MASK);
+	vx_inl(chip, CDSP);
+	snd_vx_delay(_chip, 10);
+	/* Set the reset CODEC bit to 1. */
+	chip->regCDSP |= VX_CDSP_CODEC_RESET_MASK;
+	vx_outl(chip, CDSP, chip->regCDSP);
+	vx_inl(chip, CDSP);
+	if (_chip->type == VX_TYPE_BOARD) {
+		snd_vx_delay(_chip, 1);
+		return;
+	}
+
+	snd_vx_delay(_chip, 5);  /* additionnel wait time for AKM's */
+
+	vx2_write_codec_reg(_chip, AKM_CODEC_POWER_CONTROL_CMD); /* DAC power up, ADC power up, Vref power down */
+	
+	vx2_write_codec_reg(_chip, AKM_CODEC_CLOCK_FORMAT_CMD); /* default */
+	vx2_write_codec_reg(_chip, AKM_CODEC_MUTE_CMD); /* Mute = ON ,Deemphasis = OFF */
+	vx2_write_codec_reg(_chip, AKM_CODEC_RESET_OFF_CMD); /* DAC and ADC normal operation */
+
+	if (_chip->type == VX_TYPE_MIC) {
+		/* set up the micro input selector */
+		chip->regSELMIC =  MICRO_SELECT_INPUT_NORM |
+			MICRO_SELECT_PREAMPLI_G_0 |
+			MICRO_SELECT_NOISE_T_52DB;
+
+		/* reset phantom power supply */
+		chip->regSELMIC &= ~MICRO_SELECT_PHANTOM_ALIM;
+
+		vx_outl(_chip, SELMIC, chip->regSELMIC);
+	}
+}
+
+
+/*
+ * change the audio source
+ */
+static void vx2_change_audio_source(vx_core_t *_chip, int src)
+{
+	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+
+	switch (src) {
+	case VX_AUDIO_SRC_DIGITAL:
+		chip->regCFG |= VX_CFG_DATAIN_SEL_MASK;
+		break;
+	default:
+		chip->regCFG &= ~VX_CFG_DATAIN_SEL_MASK;
+		break;
+	}
+	vx_outl(chip, CFG, chip->regCFG);
+}
+
+
+/*
+ * set the clock source
+ */
+static void vx2_set_clock_source(vx_core_t *_chip, int source)
+{
+	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+
+	if (source == INTERNAL_QUARTZ)
+		chip->regCFG &= ~VX_CFG_CLOCKIN_SEL_MASK;
+	else
+		chip->regCFG |= VX_CFG_CLOCKIN_SEL_MASK;
+	vx_outl(chip, CFG, chip->regCFG);
+}
+
+/*
+ * reset the board
+ */
+static void vx2_reset_board(vx_core_t *_chip, int cold_reset)
+{
+	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+
+	/* initialize the register values */
+	chip->regCDSP = VX_CDSP_CODEC_RESET_MASK | VX_CDSP_DSP_RESET_MASK ;
+	chip->regCFG = 0;
+}
+
+
+
+/*
+ * input level controls for VX222 Mic
+ */
+
+/* Micro level is specified to be adjustable from -96dB to 63 dB (board coded 0x00 ... 318),
+ * 318 = 210 + 36 + 36 + 36   (210 = +9dB variable) (3 * 36 = 3 steps of 18dB pre ampli)
+ * as we will mute if less than -110dB, so let's simply use line input coded levels and add constant offset !
+ */
+#define V2_MICRO_LEVEL_RANGE        (318 - 255)
+
+static void vx2_set_input_level(struct snd_vx222 *chip)
+{
+	int i, miclevel, preamp;
+	unsigned int data;
+
+	miclevel = chip->mic_level;
+	miclevel += V2_MICRO_LEVEL_RANGE; /* add 318 - 0xff */
+	preamp = 0;
+        while (miclevel > 210) { /* limitation to +9dB of 3310 real gain */
+		preamp++;	/* raise pre ampli + 18dB */
+		miclevel -= (18 * 2);   /* lower level 18 dB (*2 because of 0.5 dB steps !) */
+        }
+	snd_assert(preamp < 4, return);
+
+	/* set pre-amp level */
+	chip->regSELMIC &= ~MICRO_SELECT_PREAMPLI_MASK;
+	chip->regSELMIC |= (preamp << MICRO_SELECT_PREAMPLI_OFFSET) & MICRO_SELECT_PREAMPLI_MASK;
+	vx_outl(chip, SELMIC, chip->regSELMIC);
+
+	data = (unsigned int)miclevel << 16 |
+		(unsigned int)chip->input_level[1] << 8 |
+		(unsigned int)chip->input_level[0];
+	vx_inl(chip, DATA); /* Activate input level programming */
+
+	/* We have to send 32 bits (4 x 8 bits) */
+	for (i = 0; i < 32; i++, data <<= 1)
+		vx_outl(chip, DATA, ((data & 0x80000000) ? VX_DATA_CODEC_MASK : 0));
+
+	vx_inl(chip, RUER); /* Terminate input level programming */
+}
+
+
+#define MIC_LEVEL_MAX	0xff
+
+/*
+ * controls API for input levels
+ */
+
+/* input levels */
+static int vx_input_level_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 = MIC_LEVEL_MAX;
+	return 0;
+}
+
+static int vx_input_level_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *_chip = snd_kcontrol_chip(kcontrol);
+	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+	down(&_chip->mixer_mutex);
+	ucontrol->value.integer.value[0] = chip->input_level[0];
+	ucontrol->value.integer.value[1] = chip->input_level[1];
+	up(&_chip->mixer_mutex);
+	return 0;
+}
+
+static int vx_input_level_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *_chip = snd_kcontrol_chip(kcontrol);
+	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+	down(&_chip->mixer_mutex);
+	if (chip->input_level[0] != ucontrol->value.integer.value[0] ||
+	    chip->input_level[1] != ucontrol->value.integer.value[1]) {
+		chip->input_level[0] = ucontrol->value.integer.value[0];
+		chip->input_level[1] = ucontrol->value.integer.value[1];
+		vx2_set_input_level(chip);
+		up(&_chip->mixer_mutex);
+		return 1;
+	}
+	up(&_chip->mixer_mutex);
+	return 0;
+}
+
+/* mic level */
+static int vx_mic_level_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 = MIC_LEVEL_MAX;
+	return 0;
+}
+
+static int vx_mic_level_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *_chip = snd_kcontrol_chip(kcontrol);
+	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+	ucontrol->value.integer.value[0] = chip->mic_level;
+	return 0;
+}
+
+static int vx_mic_level_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *_chip = snd_kcontrol_chip(kcontrol);
+	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+	down(&_chip->mixer_mutex);
+	if (chip->mic_level != ucontrol->value.integer.value[0]) {
+		chip->mic_level = ucontrol->value.integer.value[0];
+		vx2_set_input_level(chip);
+		up(&_chip->mixer_mutex);
+		return 1;
+	}
+	up(&_chip->mixer_mutex);
+	return 0;
+}
+
+static snd_kcontrol_new_t vx_control_input_level = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Capture Volume",
+	.info =		vx_input_level_info,
+	.get =		vx_input_level_get,
+	.put =		vx_input_level_put,
+};
+
+static snd_kcontrol_new_t vx_control_mic_level = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Mic Capture Volume",
+	.info =		vx_mic_level_info,
+	.get =		vx_mic_level_get,
+	.put =		vx_mic_level_put,
+};
+
+/*
+ * FIXME: compressor/limiter implementation is missing yet...
+ */
+
+static int vx2_add_mic_controls(vx_core_t *_chip)
+{
+	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+	int err;
+
+	if (_chip->type != VX_TYPE_MIC)
+		return 0;
+
+	/* mute input levels */
+	chip->input_level[0] = chip->input_level[1] = 0;
+	chip->mic_level = 0;
+	vx2_set_input_level(chip);
+
+	/* controls */
+	if ((err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_input_level, chip))) < 0)
+		return err;
+	if ((err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_mic_level, chip))) < 0)
+		return err;
+
+	return 0;
+}
+
+
+/*
+ * callbacks
+ */
+struct snd_vx_ops vx222_ops = {
+	.in8 = vx2_inb,
+	.in32 = vx2_inl,
+	.out8 = vx2_outb,
+	.out32 = vx2_outl,
+	.test_and_ack = vx2_test_and_ack,
+	.validate_irq = vx2_validate_irq,
+	.akm_write = vx2_write_akm,
+	.reset_codec = vx2_reset_codec,
+	.change_audio_source = vx2_change_audio_source,
+	.set_clock_source = vx2_set_clock_source,
+	.load_dsp = vx2_load_dsp,
+	.reset_dsp = vx2_reset_dsp,
+	.reset_board = vx2_reset_board,
+	.dma_write = vx2_dma_write,
+	.dma_read = vx2_dma_read,
+	.add_controls = vx2_add_mic_controls,
+};
+
+/* for old VX222 board */
+struct snd_vx_ops vx222_old_ops = {
+	.in8 = vx2_inb,
+	.in32 = vx2_inl,
+	.out8 = vx2_outb,
+	.out32 = vx2_outl,
+	.test_and_ack = vx2_test_and_ack,
+	.validate_irq = vx2_validate_irq,
+	.write_codec = vx2_old_write_codec_bit,
+	.reset_codec = vx2_reset_codec,
+	.change_audio_source = vx2_change_audio_source,
+	.set_clock_source = vx2_set_clock_source,
+	.load_dsp = vx2_load_dsp,
+	.reset_dsp = vx2_reset_dsp,
+	.reset_board = vx2_reset_board,
+	.dma_write = vx2_dma_write,
+	.dma_read = vx2_dma_read,
+};
+
diff -urN linux-2.5.70-bk6/sound/pci/ymfpci/ymfpci.c linux-2.5.70-bk7/sound/pci/ymfpci/ymfpci.c
--- linux-2.5.70-bk6/sound/pci/ymfpci/ymfpci.c	2003-05-26 18:00:38.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ymfpci/ymfpci.c	2003-06-02 04:39:13.000000000 -0700
@@ -84,6 +84,8 @@
 {
 	static int dev;
 	snd_card_t *card;
+	struct resource *fm_res = NULL;
+	struct resource *mpu_res = NULL;
 	ymfpci_t *chip;
 	opl3_t *opl3;
 	char *str;
@@ -116,24 +118,18 @@
 
 	if (pci_id->device >= 0x0010) { /* YMF 744/754 */
 		if (fm_port[dev] < 0) {
-			// fm_port[dev] = pci_resource_start(pci, 1);
-			u16 addr;
-			pci_read_config_word(pci, PCIR_DSXG_FMBASE, &addr);
-			fm_port[dev] = addr;
+			fm_port[dev] = pci_resource_start(pci, 1);
 		}
 		if (fm_port[dev] >= 0 &&
-		    (chip->fm_res = request_region(fm_port[dev], 4, "YMFPCI OPL3")) != NULL) {
+		    (fm_res = request_region(fm_port[dev], 4, "YMFPCI OPL3")) != NULL) {
 			legacy_ctrl |= YMFPCI_LEGACY_FMEN;
 			pci_write_config_word(pci, PCIR_DSXG_FMBASE, fm_port[dev]);
 		}
 		if (mpu_port[dev] < 0) {
-			// mpu_port[dev] = pci_resource_start(pci, 1) + 0x20;
-			u16 addr;
-			pci_read_config_word(pci, PCIR_DSXG_MPU401BASE, &addr);
-			mpu_port[dev] = addr;
+			mpu_port[dev] = pci_resource_start(pci, 1) + 0x20;
 		}
 		if (mpu_port[dev] >= 0 &&
-		    (chip->mpu_res = request_region(mpu_port[dev], 2, "YMFPCI MPU401")) != NULL) {
+		    (mpu_res = request_region(mpu_port[dev], 2, "YMFPCI MPU401")) != NULL) {
 			legacy_ctrl |= YMFPCI_LEGACY_MEN;
 			pci_write_config_word(pci, PCIR_DSXG_MPU401BASE, mpu_port[dev]);
 		}
@@ -146,7 +142,7 @@
 		default: fm_port[dev] = -1; break;
 		}
 		if (fm_port[dev] > 0 &&
-		    (chip->fm_res = request_region(fm_port[dev], 4, "YMFPCI OPL3")) != NULL) {
+		    (fm_res = request_region(fm_port[dev], 4, "YMFPCI OPL3")) != NULL) {
 			legacy_ctrl |= YMFPCI_LEGACY_FMEN;
 		} else {
 			legacy_ctrl2 &= ~YMFPCI_LEGACY2_FMIO;
@@ -160,15 +156,15 @@
 		default: mpu_port[dev] = -1; break;
 		}
 		if (mpu_port[dev] > 0 &&
-		    (chip->mpu_res = request_region(mpu_port[dev], 2, "YMFPCI MPU401")) != NULL) {
+		    (mpu_res = request_region(mpu_port[dev], 2, "YMFPCI MPU401")) != NULL) {
 			legacy_ctrl |= YMFPCI_LEGACY_MEN;
 		} else {
 			legacy_ctrl2 &= ~YMFPCI_LEGACY2_MPUIO;
 			mpu_port[dev] = -1;
 		}
 	}
-	if (chip->mpu_res) {
-		legacy_ctrl |= YMFPCI_LEGACY_MIEN; /* FIXME: do we need this? */
+	if (mpu_res) {
+		legacy_ctrl |= YMFPCI_LEGACY_MIEN;
 		legacy_ctrl2 |= YMFPCI_LEGACY2_IMOD;
 	}
 	pci_read_config_word(pci, PCIR_DSXG_LEGACY, &old_legacy_ctrl);
@@ -178,8 +174,18 @@
 				     old_legacy_ctrl,
 			 	     &chip)) < 0) {
 		snd_card_free(card);
+		if (mpu_res) {
+			release_resource(mpu_res);
+			kfree_nocheck(mpu_res);
+		}
+		if (fm_res) {
+			release_resource(fm_res);
+			kfree_nocheck(fm_res);
+		}
 		return err;
 	}
+	chip->fm_res = fm_res;
+	chip->mpu_res = mpu_res;
 	if ((err = snd_ymfpci_pcm(chip, 0, NULL)) < 0) {
 		snd_card_free(card);
 		return err;
@@ -230,7 +236,7 @@
 	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->reg_area_phys,
 		chip->irq);
 
 	if ((err = snd_card_register(card)) < 0) {
diff -urN linux-2.5.70-bk6/sound/pci/ymfpci/ymfpci_main.c linux-2.5.70-bk7/sound/pci/ymfpci/ymfpci_main.c
--- linux-2.5.70-bk6/sound/pci/ymfpci/ymfpci_main.c	2003-05-26 18:00:56.000000000 -0700
+++ linux-2.5.70-bk7/sound/pci/ymfpci/ymfpci_main.c	2003-06-02 04:39:13.000000000 -0700
@@ -779,15 +779,14 @@
 		}
 	}
 
-	status = snd_ymfpci_readl(chip, YDSXGR_INTFLAG);
+	status = snd_ymfpci_readw(chip, YDSXGR_INTFLAG);
 	if (status & 1) {
 		/* timer handler */
-		snd_ymfpci_writel(chip, YDSXGR_INTFLAG, ~0);
 	}
+	snd_ymfpci_writew(chip, YDSXGR_INTFLAG, status);
 
 	if (chip->rawmidi)
 		snd_mpu401_uart_interrupt(irq, chip->rawmidi->private_data, regs);
-
 	return IRQ_HANDLED;
 }
 
@@ -2253,8 +2252,10 @@
 		return -EIO;
 	}
 
-	if ((err = snd_ymfpci_ac3_init(chip)) < 0)
+	if ((err = snd_ymfpci_ac3_init(chip)) < 0) {
+		snd_ymfpci_free(chip);
 		return err;
+	}
 
 #ifdef CONFIG_PM
 	chip->saved_regs = vmalloc(YDSXGR_NUM_SAVED_REGS * sizeof(u32));
diff -urN linux-2.5.70-bk6/sound/pcmcia/Kconfig linux-2.5.70-bk7/sound/pcmcia/Kconfig
--- linux-2.5.70-bk6/sound/pcmcia/Kconfig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/pcmcia/Kconfig	2003-06-02 04:39:13.000000000 -0700
@@ -0,0 +1,18 @@
+# ALSA PCMCIA drivers
+
+menu "PCMCIA devices"
+	depends on SND!=n && PCMCIA
+
+config SND_VXPOCKET
+	tristate "Digigram VXpocket"
+	depends on SND && PCMCIA
+	help
+	  Say 'Y' or 'M' to include support for Digigram VXpocket soundcard.
+
+config SND_VXP440
+	tristate "Digigram VXpocket 440"
+	depends on SND && PCMCIA
+	help
+	  Say 'Y' or 'M' to include support for Digigram VXpocket 440 soundcard.
+
+endmenu
diff -urN linux-2.5.70-bk6/sound/pcmcia/Makefile linux-2.5.70-bk7/sound/pcmcia/Makefile
--- linux-2.5.70-bk6/sound/pcmcia/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/pcmcia/Makefile	2003-06-02 04:39:13.000000000 -0700
@@ -0,0 +1,8 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+obj-$(CONFIG_SND) += vx/
+
+
diff -urN linux-2.5.70-bk6/sound/pcmcia/vx/Makefile linux-2.5.70-bk7/sound/pcmcia/vx/Makefile
--- linux-2.5.70-bk6/sound/pcmcia/vx/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/pcmcia/vx/Makefile	2003-06-02 04:39:13.000000000 -0700
@@ -0,0 +1,10 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-vxpocket-objs := vxpocket.o vx_entry.o vxp_ops.o vxp_mixer.o
+snd-vxp440-objs := vxpocket.o vx_entry.o vxp_ops.o vxp_mixer.o
+
+obj-$(CONFIG_SND_VXPOCKET) += snd-vxpocket.o
+obj-$(CONFIG_SND_VXP440) += snd-vxp440.o
diff -urN linux-2.5.70-bk6/sound/pcmcia/vx/vx_entry.c linux-2.5.70-bk7/sound/pcmcia/vx/vx_entry.c
--- linux-2.5.70-bk6/sound/pcmcia/vx/vx_entry.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/pcmcia/vx/vx_entry.c	2003-06-02 04:39:13.000000000 -0700
@@ -0,0 +1,385 @@
+/*
+ * Driver for Digigram VXpocket soundcards
+ *
+ * PCMCIA entry part
+ *
+ * Copyright (c) 2002 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 <sound/core.h>
+#include "vxpocket.h"
+#include <pcmcia/ciscode.h>
+#include <pcmcia/cisreg.h>
+
+
+/*
+ * prototypes
+ */
+static void vxpocket_config(dev_link_t *link);
+static int vxpocket_event(event_t event, int priority, event_callback_args_t *args);
+
+
+static void vxpocket_release(u_long arg)
+{
+	dev_link_t *link = (dev_link_t *)arg;
+	
+	if (link->state & DEV_CONFIG) {
+		/* release cs resources */
+		CardServices(ReleaseConfiguration, link->handle);
+		CardServices(ReleaseIO, link->handle, &link->io);
+		CardServices(ReleaseIRQ, link->handle, &link->irq);
+		link->state &= ~DEV_CONFIG;
+	}
+}
+
+/*
+ * destructor
+ */
+static int snd_vxpocket_free(vx_core_t *chip)
+{
+	struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
+	struct snd_vxp_entry *hw;
+	dev_link_t *link = &vxp->link;
+
+	vxpocket_release((u_long)link);
+
+	/* Break the link with Card Services */
+	if (link->handle)
+		CardServices(DeregisterClient, link->handle);
+
+	hw = vxp->hw_entry;
+	if (hw)
+		hw->card_list[vxp->index] = NULL;
+	chip->card = NULL;
+
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_vxpocket_dev_free(snd_device_t *device)
+{
+	vx_core_t *chip = snd_magic_cast(vx_core_t, device->device_data, return -ENXIO);
+	return snd_vxpocket_free(chip);
+}
+
+/*
+ * snd_vxpocket_attach - attach callback for cs
+ * @hw: the hardware information
+ */
+dev_link_t *snd_vxpocket_attach(struct snd_vxp_entry *hw)
+{
+	client_reg_t client_reg;	/* Register with cardmgr */
+	dev_link_t *link;		/* Info for cardmgr */
+	int i, ret;
+	vx_core_t *chip;
+	struct snd_vxpocket *vxp;
+	snd_card_t *card;
+	static snd_device_ops_t ops = {
+		.dev_free =	snd_vxpocket_dev_free,
+	};
+
+	snd_printdd(KERN_DEBUG "vxpocket_attach called\n");
+	/* find an empty slot from the card list */
+	for (i = 0; i < SNDRV_CARDS; i++) {
+		if (! hw->card_list[i])
+			break;
+	}
+	if (i >= SNDRV_CARDS) {
+		snd_printk(KERN_ERR "vxpocket: too many cards found\n");
+		return NULL;
+	}
+	if (! hw->enable_table[i])
+		return NULL; /* disabled explicitly */
+
+	/* ok, create a card instance */
+	card = snd_card_new(hw->index_table[i], hw->id_table[i], THIS_MODULE, 0);
+	if (card == NULL) {
+		snd_printk(KERN_ERR "vxpocket: cannot create a card instance\n");
+		return NULL;
+	}
+
+	chip = snd_vx_create(card, hw->hardware, hw->ops,
+			     sizeof(struct snd_vxpocket) - sizeof(vx_core_t));
+	if (! chip)
+		return NULL;
+
+	if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops) < 0) {
+		snd_magic_kfree(chip);
+		snd_card_free(card);
+		return NULL;
+	}
+
+	vxp = (struct snd_vxpocket *)chip;
+	vxp->index = i;
+	vxp->hw_entry = hw;
+	chip->ibl.size = hw->ibl[i];
+	hw->card_list[i] = chip;
+
+	link = &vxp->link;
+	link->priv = chip;
+
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+	link->io.NumPorts1 = 16;
+
+	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+	// link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+
+	link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+	if (hw->irq_list[0] == -1)
+		link->irq.IRQInfo2 = *hw->irq_mask_p;
+	else
+		for (i = 0; i < 4; i++)
+			link->irq.IRQInfo2 |= 1 << hw->irq_list[i];
+	link->irq.Handler = &snd_vx_irq_handler;
+	link->irq.Instance = chip;
+
+	link->release.function = &vxpocket_release;
+	link->release.data = (u_long)link;
+
+	link->conf.Attributes = CONF_ENABLE_IRQ;
+	link->conf.Vcc = 50;
+	link->conf.IntType = INT_MEMORY_AND_IO;
+	link->conf.ConfigIndex = 1;
+	link->conf.Present = PRESENT_OPTION;
+
+	/* Chain drivers */
+	link->next = hw->dev_list;
+	hw->dev_list = link;
+
+	/* Register with Card Services */
+	client_reg.dev_info = hw->dev_info;
+	client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+	client_reg.EventMask = 
+		CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL
+#ifdef CONFIG_PM
+		| CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET
+		| CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME
+#endif
+		;
+	client_reg.event_handler = &vxpocket_event;
+	client_reg.Version = 0x0210;
+	client_reg.event_callback_args.client_data = link;
+
+	ret = CardServices(RegisterClient, &link->handle, &client_reg);
+	if (ret != CS_SUCCESS) {
+		cs_error(link->handle, RegisterClient, ret);
+		snd_vxpocket_detach(hw, link);
+		return NULL;
+	}
+
+	return link;
+}
+
+
+/**
+ * snd_vxpocket_assign_resources - initialize the hardware and card instance.
+ * @port: i/o port for the card
+ * @irq: irq number for the card
+ *
+ * this function assigns the specified port and irq, boot the card,
+ * create pcm and control instances, and initialize the rest hardware.
+ *
+ * returns 0 if successful, or a negative error code.
+ */
+static int snd_vxpocket_assign_resources(vx_core_t *chip, int port, int irq)
+{
+	int err;
+	snd_card_t *card = chip->card;
+	struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
+
+	snd_printdd(KERN_DEBUG "vxpocket assign resources: port = 0x%x, irq = %d\n", port, irq);
+	vxp->port = port;
+
+	sprintf(card->shortname, "Digigram %s", card->driver);
+	sprintf(card->longname, "%s at 0x%x, irq %i",
+		card->shortname, port, irq);
+
+	if ((err = snd_vx_hwdep_new(chip)) < 0)
+		return err;
+
+	chip->irq = irq;
+
+	if ((err = snd_card_register(chip->card)) < 0)
+		return err;
+
+	return 0;
+}
+
+
+/*
+ * snd_vxpocket_detach - detach callback for cs
+ * @hw: the hardware information
+ */
+void snd_vxpocket_detach(struct snd_vxp_entry *hw, dev_link_t *link)
+{
+	vx_core_t *chip = snd_magic_cast(vx_core_t, link->priv, return);
+
+	del_timer(&link->release);
+
+	snd_printdd(KERN_DEBUG "vxpocket_detach called\n");
+	/* Remove the interface data from the linked list */
+	if (hw) {
+		dev_link_t **linkp;
+		/* Locate device structure */
+		for (linkp = &hw->dev_list; *linkp; linkp = &(*linkp)->next)
+			if (*linkp == link)
+				break;
+		if (*linkp)
+			*linkp = link->next;
+	}
+	chip->chip_status |= VX_STAT_IS_STALE; /* to be sure */
+	snd_card_disconnect(chip->card);
+	snd_card_free_in_thread(chip->card);
+}
+
+/*
+ * snd_vxpocket_detach_all - detach all instances linked to the hw
+ */
+void snd_vxpocket_detach_all(struct snd_vxp_entry *hw)
+{
+	while (hw->dev_list != NULL)
+		snd_vxpocket_detach(hw, hw->dev_list);
+}
+
+/*
+ * configuration callback
+ */
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+static void vxpocket_config(dev_link_t *link)
+{
+	client_handle_t handle = link->handle;
+	vx_core_t *chip = snd_magic_cast(vx_core_t, link->priv, return);
+	struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
+	tuple_t tuple;
+	cisparse_t parse;
+	config_info_t conf;
+	u_short buf[32];
+	int last_fn, last_ret;
+
+	snd_printdd(KERN_DEBUG "vxpocket_config called\n");
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	tuple.Attributes = 0;
+	tuple.TupleData = (cisdata_t *)buf;
+	tuple.TupleDataMax = sizeof(buf);
+	tuple.TupleOffset = 0;
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	CS_CHECK(GetFirstTuple, handle, &tuple);
+	CS_CHECK(GetTupleData, handle, &tuple);
+	CS_CHECK(ParseTuple, handle, &tuple, &parse);
+	link->conf.ConfigBase = parse.config.base;
+	link->conf.ConfigIndex = 1;
+
+	CS_CHECK(GetConfigurationInfo, handle, &conf);
+	link->conf.Vcc = conf.Vcc;
+
+	/* Configure card */
+	link->state |= DEV_CONFIG;
+
+	CS_CHECK(RequestIO, handle, &link->io);
+	CS_CHECK(RequestIRQ, link->handle, &link->irq);
+	CS_CHECK(RequestConfiguration, link->handle, &link->conf);
+
+	if (snd_vxpocket_assign_resources(chip, link->io.BasePort1, link->irq.AssignedIRQ) < 0)
+		goto failed;
+
+	link->dev = &vxp->node;
+	link->state &= ~DEV_CONFIG_PENDING;
+	return;
+
+cs_failed:
+	cs_error(link->handle, last_fn, last_ret);
+failed:
+	CardServices(ReleaseConfiguration, link->handle);
+	CardServices(ReleaseIO, link->handle, &link->io);
+	CardServices(ReleaseIRQ, link->handle, &link->irq);
+}
+
+
+/*
+ * event callback
+ */
+static int vxpocket_event(event_t event, int priority, event_callback_args_t *args)
+{
+	dev_link_t *link = args->client_data;
+	vx_core_t *chip = link->priv;
+
+	switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+		snd_printdd(KERN_DEBUG "CARD_REMOVAL..\n");
+		link->state &= ~DEV_PRESENT;
+		if (link->state & DEV_CONFIG) {
+			mod_timer(&link->release, jiffies + HZ/20);
+			chip->chip_status |= VX_STAT_IS_STALE;
+		}
+		break;
+	case CS_EVENT_CARD_INSERTION:
+		snd_printdd(KERN_DEBUG "CARD_INSERTION..\n");
+		link->state |= DEV_PRESENT;
+		vxpocket_config(link);
+		break;
+#ifdef CONFIG_PM
+	case CS_EVENT_PM_SUSPEND:
+		snd_printdd(KERN_DEBUG "SUSPEND\n");
+		link->state |= DEV_SUSPEND;
+		if (chip) {
+			snd_printdd(KERN_DEBUG "snd_vx_suspend calling\n");
+			snd_vx_suspend(chip);
+		}
+		/* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+		snd_printdd(KERN_DEBUG "RESET_PHYSICAL\n");
+		if (link->state & DEV_CONFIG)
+			CardServices(ReleaseConfiguration, link->handle);
+		break;
+	case CS_EVENT_PM_RESUME:
+		snd_printdd(KERN_DEBUG "RESUME\n");
+		link->state &= ~DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_CARD_RESET:
+		snd_printdd(KERN_DEBUG "CARD_RESET\n");
+		if (DEV_OK(link)) {
+			//struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
+			snd_printdd(KERN_DEBUG "requestconfig...\n");
+			CardServices(RequestConfiguration, link->handle, &link->conf);
+			if (chip) {
+				snd_printdd(KERN_DEBUG "calling snd_vx_resume\n");
+				snd_vx_resume(chip);
+			}
+		}
+		snd_printdd(KERN_DEBUG "resume done!\n");
+		break;
+#endif
+	}
+	return 0;
+}
+
+/* we link this module statically with the card modules
+ * due to the pcmcia symbol problems...
+ */
+#if 0
+/*
+ * exported stuffs
+ */
+EXPORT_SYMBOL(snd_vxpocket_ops);
+EXPORT_SYMBOL(snd_vxpocket_attach);
+EXPORT_SYMBOL(snd_vxpocket_detach);
+EXPORT_SYMBOL(snd_vxpocket_detach_all);
+#endif
diff -urN linux-2.5.70-bk6/sound/pcmcia/vx/vxp440.c linux-2.5.70-bk7/sound/pcmcia/vx/vxp440.c
--- linux-2.5.70-bk6/sound/pcmcia/vx/vxp440.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/pcmcia/vx/vxp440.c	2003-06-02 04:39:13.000000000 -0700
@@ -0,0 +1,14 @@
+#define COMPILE_VXP440
+
+/*
+ add the following as /etc/pcmcia/vxp440.conf:
+
+  device "snd-vxp440"
+    class "audio" module "snd-vxp440"
+
+  card "Digigram VX-POCKET440"
+    manfid 0x01f1, 0x0100
+    bind "snd-vxp440"
+*/
+
+#include "vxpocket.c"
diff -urN linux-2.5.70-bk6/sound/pcmcia/vx/vxp_mixer.c linux-2.5.70-bk7/sound/pcmcia/vx/vxp_mixer.c
--- linux-2.5.70-bk6/sound/pcmcia/vx/vxp_mixer.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/pcmcia/vx/vxp_mixer.c	2003-06-02 04:39:13.000000000 -0700
@@ -0,0 +1,150 @@
+/*
+ * Driver for Digigram VXpocket soundcards
+ *
+ * VX-pocket mixer
+ *
+ * Copyright (c) 2002 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 <sound/core.h>
+#include <sound/control.h>
+#include "vxpocket.h"
+
+#define chip_t vx_core_t
+
+#define MIC_LEVEL_MIN	0
+#define MIC_LEVEL_MAX	8
+
+/*
+ * mic level control (for VXPocket)
+ */
+static int vx_mic_level_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 = MIC_LEVEL_MAX;
+	return 0;
+}
+
+static int vx_mic_level_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *_chip = snd_kcontrol_chip(kcontrol);
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+	ucontrol->value.integer.value[0] = chip->mic_level;
+	return 0;
+}
+
+static int vx_mic_level_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *_chip = snd_kcontrol_chip(kcontrol);
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+	down(&_chip->mixer_mutex);
+	if (chip->mic_level != ucontrol->value.integer.value[0]) {
+		vx_set_mic_level(_chip, ucontrol->value.integer.value[0]);
+		chip->mic_level = ucontrol->value.integer.value[0];
+		up(&_chip->mixer_mutex);
+		return 1;
+	}
+	up(&_chip->mixer_mutex);
+	return 0;
+}
+
+static snd_kcontrol_new_t vx_control_mic_level = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Mic Capture Volume",
+	.info =		vx_mic_level_info,
+	.get =		vx_mic_level_get,
+	.put =		vx_mic_level_put,
+};
+
+/*
+ * mic boost level control (for VXP440)
+ */
+static int vx_mic_boost_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 vx_mic_boost_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *_chip = snd_kcontrol_chip(kcontrol);
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+	ucontrol->value.integer.value[0] = chip->mic_level;
+	return 0;
+}
+
+static int vx_mic_boost_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	vx_core_t *_chip = snd_kcontrol_chip(kcontrol);
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+	down(&_chip->mixer_mutex);
+	if (chip->mic_level != ucontrol->value.integer.value[0]) {
+		vx_set_mic_boost(_chip, ucontrol->value.integer.value[0]);
+		chip->mic_level = ucontrol->value.integer.value[0];
+		up(&_chip->mixer_mutex);
+		return 1;
+	}
+	up(&_chip->mixer_mutex);
+	return 0;
+}
+
+static snd_kcontrol_new_t vx_control_mic_boost = {
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Mic Boost",
+	.info =		vx_mic_boost_info,
+	.get =		vx_mic_boost_get,
+	.put =		vx_mic_boost_put,
+};
+
+
+int vxp_add_mic_controls(vx_core_t *_chip)
+{
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+	int err;
+
+	/* mute input levels */
+	chip->mic_level = 0;
+	switch (_chip->type) {
+	case VX_TYPE_VXPOCKET:
+		vx_set_mic_level(_chip, 0);
+		break;
+	case VX_TYPE_VXP440:
+		vx_set_mic_boost(_chip, 0);
+		break;
+	}
+
+	/* mic level */
+	switch (_chip->type) {
+	case VX_TYPE_VXPOCKET:
+		if ((err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_mic_level, chip))) < 0)
+			return err;
+		break;
+	case VX_TYPE_VXP440:
+		if ((err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_mic_boost, chip))) < 0)
+			return err;
+		break;
+	}
+
+	return 0;
+}
+
diff -urN linux-2.5.70-bk6/sound/pcmcia/vx/vxp_ops.c linux-2.5.70-bk7/sound/pcmcia/vx/vxp_ops.c
--- linux-2.5.70-bk6/sound/pcmcia/vx/vxp_ops.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/pcmcia/vx/vxp_ops.c	2003-06-02 04:39:13.000000000 -0700
@@ -0,0 +1,616 @@
+/*
+ * Driver for Digigram VXpocket soundcards
+ *
+ * lowlevel routines for VXpocket soundcards
+ *
+ * Copyright (c) 2002 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/delay.h>
+#include <sound/core.h>
+#include <asm/io.h>
+#include "vxpocket.h"
+
+#define chip_t vx_core_t
+
+
+static int vxp_reg_offset[VX_REG_MAX] = {
+	[VX_ICR]	= 0x00,		// ICR
+	[VX_CVR]	= 0x01,		// CVR
+	[VX_ISR]	= 0x02,		// ISR
+	[VX_IVR]	= 0x03,		// IVR
+	[VX_RXH]	= 0x05,		// RXH
+	[VX_RXM]	= 0x06,		// RXM
+	[VX_RXL]	= 0x07,		// RXL
+	[VX_DMA]	= 0x04,		// DMA
+	[VX_CDSP]	= 0x08,		// CDSP
+	[VX_LOFREQ]	= 0x09,		// LFREQ
+	[VX_HIFREQ]	= 0x0a,		// HFREQ
+	[VX_DATA]	= 0x0b,		// DATA
+	[VX_MICRO]	= 0x0c,		// MICRO
+	[VX_DIALOG]	= 0x0d,		// DIALOG
+	[VX_CSUER]	= 0x0e,		// CSUER
+	[VX_RUER]	= 0x0f,		// RUER
+};
+
+
+inline static unsigned long vxp_reg_addr(vx_core_t *_chip, int reg)
+{
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+	return chip->port + vxp_reg_offset[reg];
+}
+
+/*
+ * snd_vx_inb - read a byte from the register
+ * @offset: register offset
+ */
+static unsigned char vxp_inb(vx_core_t *chip, int offset)
+{
+	return inb(vxp_reg_addr(chip, offset));
+}
+
+/*
+ * snd_vx_outb - write a byte on the register
+ * @offset: the register offset
+ * @val: the value to write
+ */
+static void vxp_outb(vx_core_t *chip, int offset, unsigned char val)
+{
+	outb(val, vxp_reg_addr(chip, offset));
+}
+
+/*
+ * redefine macros to call directly
+ */
+#undef vx_inb
+#define vx_inb(chip,reg)	vxp_inb((vx_core_t*)(chip), VX_##reg)
+#undef vx_outb
+#define vx_outb(chip,reg,val)	vxp_outb((vx_core_t*)(chip), VX_##reg,val)
+
+
+/*
+ * vx_check_magic - check the magic word on xilinx
+ *
+ * returns zero if a magic word is detected, or a negative error code.
+ */
+static int vx_check_magic(vx_core_t *chip)
+{
+	unsigned long end_time = jiffies + HZ / 5;
+	int c;
+	do {
+		c = vx_inb(chip, CDSP);
+		if (c == CDSP_MAGIC)
+			return 0;
+		snd_vx_delay(chip, 10);
+	} while (time_after_eq(end_time, jiffies));
+	snd_printk(KERN_ERR "cannot find xilinx magic word (%x)\n", c);
+	return -EIO;
+}
+
+
+/*
+ * vx_reset_dsp - reset the DSP
+ */
+
+#define XX_DSP_RESET_WAIT_TIME		2	/* ms */
+
+static void vxp_reset_dsp(vx_core_t *_chip)
+{
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+
+	/* set the reset dsp bit to 1 */
+	vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_DSP_RESET_MASK);
+	vx_inb(chip, CDSP);
+	mdelay(XX_DSP_RESET_WAIT_TIME);
+	/* reset the bit */
+	chip->regCDSP &= ~VXP_CDSP_DSP_RESET_MASK;
+	vx_outb(chip, CDSP, chip->regCDSP);
+	vx_inb(chip, CDSP);
+	mdelay(XX_DSP_RESET_WAIT_TIME);
+}
+
+/*
+ * reset codec bit
+ */
+static void vxp_reset_codec(vx_core_t *_chip)
+{
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+
+	/* Set the reset CODEC bit to 1. */
+	vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_CODEC_RESET_MASK);
+	vx_inb(chip, CDSP);
+	snd_vx_delay(_chip, 10);
+	/* Set the reset CODEC bit to 0. */
+	chip->regCDSP &= ~VXP_CDSP_CODEC_RESET_MASK;
+	vx_outb(chip, CDSP, chip->regCDSP);
+	vx_inb(chip, CDSP);
+	snd_vx_delay(_chip, 1);
+}
+
+/*
+ * vx_load_xilinx_binary - load the xilinx binary image
+ * the binary image is the binary array converted from the bitstream file.
+ */
+static int vxp_load_xilinx_binary(vx_core_t *_chip, const snd_hwdep_dsp_image_t *xilinx)
+{
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+	unsigned int i;
+	int c;
+	int regCSUER, regRUER;
+	unsigned char *image, data;
+
+	/* Switch to programmation mode */
+	chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK;
+	vx_outb(chip, DIALOG, chip->regDIALOG);
+
+	/* Save register CSUER and RUER */
+	regCSUER = vx_inb(chip, CSUER);
+	regRUER = vx_inb(chip, RUER);
+
+	/* reset HF0 and HF1 */
+	vx_outb(chip, ICR, 0);
+
+	/* Wait for answer HF2 equal to 1 */
+	snd_printdd(KERN_DEBUG "check ISR_HF2\n");
+	if (vx_check_isr(_chip, ISR_HF2, ISR_HF2, 20) < 0)
+		goto _error;
+
+	/* set HF1 for loading xilinx binary */
+	vx_outb(chip, ICR, ICR_HF1);
+	image = xilinx->image;
+	for (i = 0; i < xilinx->length; i++, image++) {
+		__get_user(data, image);
+		if (vx_wait_isr_bit(_chip, ISR_TX_EMPTY) < 0)
+			goto _error;
+		vx_outb(chip, TXL, data);
+		/* wait for reading */
+		if (vx_wait_for_rx_full(_chip) < 0)
+			goto _error;
+		c = vx_inb(chip, RXL);
+		if (c != (int)data)
+			snd_printk(KERN_ERR "vxpocket: load xilinx mismatch at %d: 0x%x != 0x%x\n", i, c, (int)data);
+        }
+
+	/* reset HF1 */
+	vx_outb(chip, ICR, 0);
+
+	/* wait for HF3 */
+	if (vx_check_isr(_chip, ISR_HF3, ISR_HF3, 20) < 0)
+		goto _error;
+
+	/* read the number of bytes received */
+	if (vx_wait_for_rx_full(_chip) < 0)
+		goto _error;
+
+	c = (int)vx_inb(chip, RXH) << 16;
+	c |= (int)vx_inb(chip, RXM) << 8;
+	c |= vx_inb(chip, RXL);
+
+	snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%x\n", c, xilinx->length);
+
+	vx_outb(chip, ICR, ICR_HF0);
+
+	/* TEMPO 250ms : wait until Xilinx is downloaded */
+	snd_vx_delay(_chip, 300);
+
+	/* test magical word */
+	if (vx_check_magic(_chip) < 0)
+		goto _error;
+
+	/* Restore register 0x0E and 0x0F (thus replacing COR and FCSR) */
+	vx_outb(chip, CSUER, regCSUER);
+	vx_outb(chip, RUER, regRUER);
+
+	/* Reset the Xilinx's signal enabling IO access */
+	chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK;
+	vx_outb(chip, DIALOG, chip->regDIALOG);
+	vx_inb(chip, DIALOG);
+	snd_vx_delay(_chip, 10);
+	chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK;
+	vx_outb(chip, DIALOG, chip->regDIALOG);
+	vx_inb(chip, DIALOG);
+
+	/* Reset of the Codec */
+	vxp_reset_codec(_chip);
+	vx_reset_dsp(_chip);
+
+	return 0;
+
+ _error:
+	vx_outb(chip, CSUER, regCSUER);
+	vx_outb(chip, RUER, regRUER);
+	chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK;
+	vx_outb(chip, DIALOG, chip->regDIALOG);
+	return -EIO;
+}
+
+
+/*
+ * vxp_load_dsp - load_dsp callback
+ */
+static int vxp_load_dsp(vx_core_t *vx, const snd_hwdep_dsp_image_t *dsp)
+{
+	int err;
+
+	if (*dsp->name)
+		snd_printdd("loading dsp [%d] %s, size = %d\n", dsp->index, dsp->name, dsp->length);
+
+	switch (dsp->index) {
+	case 0:
+		/* xilinx boot */
+		if ((err = vx_check_magic(vx)) < 0)
+			return err;
+		if ((err = snd_vx_load_boot_image(vx, dsp)) < 0)
+			return err;
+		return 0;
+	case 1:
+		/* xilinx image */
+		return vxp_load_xilinx_binary(vx, dsp);
+	case 2:
+		/* DSP boot */
+		return snd_vx_dsp_boot(vx, dsp);
+	case 3:
+		/* DSP image */
+		return snd_vx_dsp_load(vx, dsp);
+	default:
+		snd_BUG();
+		return -EINVAL;
+	}
+}
+		
+
+/*
+ * vx_test_and_ack - test and acknowledge interrupt
+ *
+ * called from irq hander, too
+ *
+ * spinlock held!
+ */
+static int vxp_test_and_ack(vx_core_t *_chip)
+{
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+
+	/* not booted yet? */
+	if (! (_chip->chip_status & VX_STAT_XILINX_LOADED))
+		return -ENXIO;
+
+	if (! (vx_inb(chip, DIALOG) & VXP_DLG_MEMIRQ_MASK))
+		return -EIO;
+	
+	/* ok, interrupts generated, now ack it */
+	/* set ACQUIT bit up and down */
+	vx_outb(chip, DIALOG, chip->regDIALOG | VXP_DLG_ACK_MEMIRQ_MASK);
+	/* useless read just to spend some time and maintain
+	 * the ACQUIT signal up for a while ( a bus cycle )
+	 */
+	vx_inb(chip, DIALOG);
+	vx_outb(chip, DIALOG, chip->regDIALOG & ~VXP_DLG_ACK_MEMIRQ_MASK);
+
+	return 0;
+}
+
+
+/*
+ * vx_validate_irq - enable/disable IRQ
+ */
+static void vxp_validate_irq(vx_core_t *_chip, int enable)
+{
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+
+	/* Set the interrupt enable bit to 1 in CDSP register */
+	if (enable)
+		chip->regCDSP |= VXP_CDSP_VALID_IRQ_MASK;
+	else
+		chip->regCDSP &= ~VXP_CDSP_VALID_IRQ_MASK;
+	vx_outb(chip, CDSP, chip->regCDSP);
+}
+
+/*
+ * vx_setup_pseudo_dma - set up the pseudo dma read/write mode.
+ * @do_write: 0 = read, 1 = set up for DMA write
+ */
+static void vx_setup_pseudo_dma(vx_core_t *_chip, int do_write)
+{
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+
+	/* Interrupt mode and HREQ pin enabled for host transmit / receive data transfers */
+	vx_outb(chip, ICR, do_write ? ICR_TREQ : ICR_RREQ);
+	/* Reset the pseudo-dma register */
+	vx_inb(chip, ISR);
+	vx_outb(chip, ISR, 0);
+
+	/* Select DMA in read/write transfer mode and in 16-bit accesses */
+	chip->regDIALOG |= VXP_DLG_DMA16_SEL_MASK;
+	chip->regDIALOG |= do_write ? VXP_DLG_DMAWRITE_SEL_MASK : VXP_DLG_DMAREAD_SEL_MASK;
+	vx_outb(chip, DIALOG, chip->regDIALOG);
+
+}
+
+/*
+ * vx_release_pseudo_dma - disable the pseudo-DMA mode
+ */
+static void vx_release_pseudo_dma(vx_core_t *_chip)
+{
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+
+	/* Disable DMA and 16-bit accesses */
+	chip->regDIALOG &= ~(VXP_DLG_DMAWRITE_SEL_MASK|
+			     VXP_DLG_DMAREAD_SEL_MASK|
+			     VXP_DLG_DMA16_SEL_MASK);
+	vx_outb(chip, DIALOG, chip->regDIALOG);
+	/* HREQ pin disabled. */
+	vx_outb(chip, ICR, 0);
+}
+
+/*
+ * vx_pseudo_dma_write - write bulk data on pseudo-DMA mode
+ * @count: data length to transfer in bytes
+ *
+ * data size must be aligned to 6 bytes to ensure the 24bit alignment on DSP.
+ * NB: call with a certain lock!
+ */
+static void vxp_dma_write(vx_core_t *chip, snd_pcm_runtime_t *runtime,
+			  vx_pipe_t *pipe, int count)
+{
+	long port = vxp_reg_addr(chip, VX_DMA);
+	int offset = pipe->hw_ptr;
+	unsigned short *addr = (unsigned short *)(runtime->dma_area + offset);
+
+	vx_setup_pseudo_dma(chip, 1);
+	if (offset + count > pipe->buffer_bytes) {
+		int length = pipe->buffer_bytes - offset;
+		count -= length;
+		length >>= 1; /* in 16bit words */
+		/* Transfer using pseudo-dma. */
+		while (length-- > 0) {
+			outw(cpu_to_le16(*addr), port);
+			addr++;
+		}
+		addr = (unsigned short *)runtime->dma_area;
+		pipe->hw_ptr = 0;
+	}
+	pipe->hw_ptr += count;
+	count >>= 1; /* in 16bit words */
+	/* Transfer using pseudo-dma. */
+	while (count-- > 0) {
+		outw(cpu_to_le16(*addr), port);
+		addr++;
+	}
+	vx_release_pseudo_dma(chip);
+}
+
+
+/*
+ * vx_pseudo_dma_read - read bulk data on pseudo DMA mode
+ * @offset: buffer offset in bytes
+ * @count: data length to transfer in bytes
+ *
+ * the read length must be aligned to 6 bytes, as well as write.
+ * NB: call with a certain lock!
+ */
+static void vxp_dma_read(vx_core_t *chip, snd_pcm_runtime_t *runtime,
+			 vx_pipe_t *pipe, int count)
+{
+	struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip;
+	long port = vxp_reg_addr(chip, VX_DMA);
+	int offset = pipe->hw_ptr;
+	unsigned short *addr = (unsigned short *)(runtime->dma_area + offset);
+
+	snd_assert(count % 2 == 0, return);
+	vx_setup_pseudo_dma(chip, 0);
+	if (offset + count > pipe->buffer_bytes) {
+		int length = pipe->buffer_bytes - offset;
+		count -= length;
+		length >>= 1; /* in 16bit words */
+		/* Transfer using pseudo-dma. */
+		while (length-- > 0)
+			*addr++ = le16_to_cpu(inw(port));
+		addr = (unsigned short *)runtime->dma_area;
+		pipe->hw_ptr = 0;
+	}
+	pipe->hw_ptr += count;
+	count >>= 1; /* in 16bit words */
+	/* Transfer using pseudo-dma. */
+	while (count-- > 1)
+		*addr++ = le16_to_cpu(inw(port));
+	/* Disable DMA */
+	pchip->regDIALOG &= ~VXP_DLG_DMAREAD_SEL_MASK;
+	vx_outb(chip, DIALOG, pchip->regDIALOG);
+	/* Read the last word (16 bits) */
+	*addr = le16_to_cpu(inw(port));
+	/* Disable 16-bit accesses */
+	pchip->regDIALOG &= ~VXP_DLG_DMA16_SEL_MASK;
+	vx_outb(chip, DIALOG, pchip->regDIALOG);
+	/* HREQ pin disabled. */
+	vx_outb(chip, ICR, 0);
+}
+
+
+/*
+ * write a codec data (24bit)
+ */
+static void vxp_write_codec_reg(vx_core_t *chip, int codec, unsigned int data)
+{
+	int i;
+
+	/* Activate access to the corresponding codec register */
+	if (! codec)
+		vx_inb(chip, LOFREQ);
+	else
+		vx_inb(chip, CODEC2);
+		
+	/* We have to send 24 bits (3 x 8 bits). Start with most signif. Bit */
+	for (i = 0; i < 24; i++, data <<= 1)
+		vx_outb(chip, DATA, ((data & 0x800000) ? VX_DATA_CODEC_MASK : 0));
+	
+	/* Terminate access to codec registers */
+	vx_inb(chip, HIFREQ);
+}
+
+
+/*
+ * vx_set_mic_boost - set mic boost level (on vxp440 only)
+ * @boost: 0 = 20dB, 1 = +38dB
+ */
+void vx_set_mic_boost(vx_core_t *chip, int boost)
+{
+	struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip;
+	unsigned long flags;
+
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	if (pchip->regCDSP & P24_CDSP_MICS_SEL_MASK) {
+		if (boost) {
+			/* boost: 38 dB */
+			pchip->regCDSP &= ~P24_CDSP_MIC20_SEL_MASK;
+			pchip->regCDSP |=  P24_CDSP_MIC38_SEL_MASK;
+		} else {
+			/* minimum value: 20 dB */
+			pchip->regCDSP |=  P24_CDSP_MIC20_SEL_MASK;
+			pchip->regCDSP &= ~P24_CDSP_MIC38_SEL_MASK;
+                }
+		vx_outb(chip, CDSP, pchip->regCDSP);
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+/*
+ * remap the linear value (0-8) to the actual value (0-15)
+ */
+static int vx_compute_mic_level(int level)
+{
+	switch (level) {
+	case 5: level = 6 ; break;
+	case 6: level = 8 ; break;
+	case 7: level = 11; break;
+	case 8: level = 15; break;
+	default: break ;
+	}
+	return level;
+}
+
+/*
+ * vx_set_mic_level - set mic level (on vxpocket only)
+ * @level: the mic level = 0 - 8 (max)
+ */
+void vx_set_mic_level(vx_core_t *chip, int level)
+{
+	struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip;
+	unsigned long flags;
+
+	if (chip->chip_status & VX_STAT_IS_STALE)
+		return;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	if (pchip->regCDSP & VXP_CDSP_MIC_SEL_MASK) {
+		level = vx_compute_mic_level(level);
+		vx_outb(chip, MICRO, level);
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+/*
+ * change the input audio source
+ */
+static void vxp_change_audio_source(vx_core_t *_chip, int src)
+{
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+
+	switch (src) {
+	case VX_AUDIO_SRC_DIGITAL:
+		chip->regCDSP |= VXP_CDSP_DATAIN_SEL_MASK;
+		vx_outb(chip, CDSP, chip->regCDSP);
+		break;
+	case VX_AUDIO_SRC_LINE:
+		chip->regCDSP &= ~VXP_CDSP_DATAIN_SEL_MASK;
+		if (_chip->type == VX_TYPE_VXP440)
+			chip->regCDSP &= ~P24_CDSP_MICS_SEL_MASK;
+		else
+			chip->regCDSP &= ~VXP_CDSP_MIC_SEL_MASK;
+		vx_outb(chip, CDSP, chip->regCDSP);
+		break;
+	case VX_AUDIO_SRC_MIC:
+		chip->regCDSP &= ~VXP_CDSP_DATAIN_SEL_MASK;
+		/* reset mic levels */
+		if (_chip->type == VX_TYPE_VXP440) {
+			chip->regCDSP &= ~P24_CDSP_MICS_SEL_MASK;
+			if (chip->mic_level)
+				chip->regCDSP |=  P24_CDSP_MIC38_SEL_MASK;
+			else
+				chip->regCDSP |= P24_CDSP_MIC20_SEL_MASK;
+			vx_outb(chip, CDSP, chip->regCDSP);
+		} else {
+			chip->regCDSP |= VXP_CDSP_MIC_SEL_MASK;
+			vx_outb(chip, CDSP, chip->regCDSP);
+			vx_outb(chip, MICRO, vx_compute_mic_level(chip->mic_level));
+		}
+		break;
+	}
+}
+
+/*
+ * change the clock source
+ * source = INTERNAL_QUARTZ or UER_SYNC
+ */
+static void vxp_set_clock_source(vx_core_t *_chip, int source)
+{
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+
+	if (source == INTERNAL_QUARTZ)
+		chip->regCDSP &= ~VXP_CDSP_CLOCKIN_SEL_MASK;
+	else
+		chip->regCDSP |= VXP_CDSP_CLOCKIN_SEL_MASK;
+	vx_outb(chip, CDSP, chip->regCDSP);
+}
+
+
+/*
+ * reset the board
+ */
+static void vxp_reset_board(vx_core_t *_chip, int cold_reset)
+{
+	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
+
+	chip->regCDSP = 0;
+	chip->regDIALOG = 0;
+}
+
+
+/*
+ * callbacks
+ */
+/* exported */
+struct snd_vx_ops snd_vxpocket_ops = {
+	.in8 = vxp_inb,
+	.out8 = vxp_outb,
+	.test_and_ack = vxp_test_and_ack,
+	.validate_irq = vxp_validate_irq,
+	.write_codec = vxp_write_codec_reg,
+	.reset_codec = vxp_reset_codec,
+	.change_audio_source = vxp_change_audio_source,
+	.set_clock_source = vxp_set_clock_source,
+	.load_dsp = vxp_load_dsp,
+	.add_controls = vxp_add_mic_controls,
+	.reset_dsp = vxp_reset_dsp,
+	.reset_board = vxp_reset_board,
+	.dma_write = vxp_dma_write,
+	.dma_read = vxp_dma_read,
+};
diff -urN linux-2.5.70-bk6/sound/pcmcia/vx/vxpocket.c linux-2.5.70-bk7/sound/pcmcia/vx/vxpocket.c
--- linux-2.5.70-bk6/sound/pcmcia/vx/vxpocket.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/pcmcia/vx/vxpocket.c	2003-06-02 04:39:13.000000000 -0700
@@ -0,0 +1,174 @@
+/*
+ * Driver for Digigram VXpocket V2/440 soundcards
+ *
+ * Copyright (c) 2002 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
+ */
+
+/*
+ please add the following as /etc/pcmcia/vxpocket.conf:
+ 
+  device "snd-vxpocket"
+     class "audio" module "snd-vxpocket"
+
+  card "Digigram VX-POCKET"
+    manfid 0x01f1, 0x0100
+    bind "snd-vxpocket"
+
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <pcmcia/version.h>
+#include "vxpocket.h"
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+/*
+ */
+
+#ifdef COMPILE_VXP440
+#define CARD_NAME	"VXPocket440"
+#else
+#define CARD_NAME	"VXPocket"
+#endif
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("Digigram " CARD_NAME);
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Digigram," CARD_NAME "}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable switches */
+static unsigned int irq_mask = 0xffff;
+static int irq_list[4] = { -1 };
+static int ibl[SNDRV_CARDS];
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM_DESC(irq_mask, "IRQ bitmask for " CARD_NAME " soundcard.");
+MODULE_PARM(irq_list, "1-4i");
+MODULE_PARM_DESC(irq_list, "List of Available interrupts for " CARD_NAME " soundcard.");
+MODULE_PARM(ibl, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(ibl, "Capture IBL size for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(ibl, SNDRV_ENABLED);
+ 
+
+/*
+ */
+
+#ifdef COMPILE_VXP440
+static dev_info_t dev_info = "snd-vxp440";
+
+/* 1 DSP, 1 sync UER, 1 sync World Clock (NIY) */
+/* SMPTE (NIY) */
+/* 2 stereo analog input (line/micro) */
+/* 2 stereo analog output */
+/* Only output levels can be modified */
+/* UER, but only for the first two inputs and outputs. */
+
+#define NUM_CODECS	2
+#define CARD_TYPE	VX_TYPE_VXP440
+
+#else
+static dev_info_t dev_info = "snd-vxpocket";
+
+/* 1 DSP, 1 sync UER */
+/* 1 programmable clock (NIY) */
+/* 1 stereo analog input (line/micro) */
+/* 1 stereo analog output */
+/* Only output levels can be modified */
+
+#define NUM_CODECS	1
+#define CARD_TYPE	VX_TYPE_VXPOCKET
+#endif
+
+
+static struct snd_vx_hardware vxp_hw = {
+	.name = CARD_NAME,
+	.type = CARD_TYPE,
+
+	/* hardware specs */
+	.num_codecs = NUM_CODECS,
+	.num_ins = NUM_CODECS,
+	.num_outs = NUM_CODECS,
+	.output_level_max = VX_ANALOG_OUT_LEVEL_MAX,
+};	
+
+static struct snd_vxp_entry hw_entry = {
+	.dev_info = &dev_info,
+
+	/* module parameters */
+	.index_table = index,
+	.id_table = id,
+	.enable_table = enable,
+	.irq_mask_p = &irq_mask,
+	.irq_list = irq_list,
+	.ibl = ibl,
+
+	/* h/w config */
+	.hardware = &vxp_hw,
+	.ops = &snd_vxpocket_ops,
+};
+
+/*
+ */
+static dev_link_t *vxp_attach(void)
+{
+	return snd_vxpocket_attach(&hw_entry);
+}
+
+static void vxp_detach(dev_link_t *link)
+{
+	snd_vxpocket_detach(&hw_entry, link);
+}
+
+
+/*
+ * Module entry points
+ */
+
+static int __init init_vxpocket(void)
+{
+	servinfo_t serv;
+
+	CardServices(GetCardServicesInfo, &serv);
+	if (serv.Revision != CS_RELEASE_CODE) {
+		printk(KERN_WARNING "init_vxpocket: Card Services release does not match (%x != %x)!\n", serv.Revision, CS_RELEASE_CODE);
+		return -1;
+	}
+	register_pccard_driver(&dev_info, vxp_attach, vxp_detach);
+	return 0;
+}
+
+static void __exit exit_vxpocket(void)
+{
+        unregister_pccard_driver(&dev_info);
+        snd_vxpocket_detach_all(&hw_entry);
+}
+
+module_init(init_vxpocket);
+module_exit(exit_vxpocket);
diff -urN linux-2.5.70-bk6/sound/pcmcia/vx/vxpocket.h linux-2.5.70-bk7/sound/pcmcia/vx/vxpocket.h
--- linux-2.5.70-bk6/sound/pcmcia/vx/vxpocket.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.70-bk7/sound/pcmcia/vx/vxpocket.h	2003-06-02 04:39:13.000000000 -0700
@@ -0,0 +1,121 @@
+/*
+ * Driver for Digigram VXpocket soundcards
+ *
+ * Copyright (c) 2002 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 __VXPOCKET_H
+#define __VXPOCKET_H
+
+#include <sound/vx_core.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+struct snd_vxp_entry {
+	dev_info_t *dev_info;
+
+	/* module parameters */
+	int *index_table;
+	char **id_table;
+	int *enable_table;
+	unsigned int *irq_mask_p;
+	int *irq_list;
+	int *ibl;
+
+	/* h/w config */
+	struct snd_vx_hardware *hardware;
+	struct snd_vx_ops *ops;
+
+	/* slots */
+	vx_core_t *card_list[SNDRV_CARDS];
+	dev_link_t *dev_list;		/* Linked list of devices */
+};
+
+struct snd_vxpocket {
+
+	vx_core_t core;
+
+	unsigned long port;
+
+	int mic_level;	/* analog mic level (or boost) */
+
+	unsigned int regCDSP;	/* current CDSP register */
+	unsigned int regDIALOG;	/* current DIALOG register */
+
+	int index;
+	struct snd_vxp_entry *hw_entry;
+
+	/* pcmcia stuff */
+	dev_link_t link;
+	dev_node_t node;
+};
+
+extern struct snd_vx_ops snd_vxpocket_ops;
+
+void vx_set_mic_boost(vx_core_t *chip, int boost);
+void vx_set_mic_level(vx_core_t *chip, int level);
+
+/*
+ * pcmcia stuff
+ */
+dev_link_t *snd_vxpocket_attach(struct snd_vxp_entry *hw);
+void snd_vxpocket_detach(struct snd_vxp_entry *hw, dev_link_t *link);
+void snd_vxpocket_detach_all(struct snd_vxp_entry *hw);
+
+int vxp_add_mic_controls(vx_core_t *chip);
+
+/* Constants used to access the CDSP register (0x08). */
+#define CDSP_MAGIC	0xA7	/* magic value (for read) */
+/* for write */
+#define VXP_CDSP_CLOCKIN_SEL_MASK	0x80	/* 0 (internal), 1 (AES/EBU) */
+#define VXP_CDSP_DATAIN_SEL_MASK	0x40	/* 0 (analog), 1 (UER) */
+#define VXP_CDSP_SMPTE_SEL_MASK		0x20
+#define VXP_CDSP_RESERVED_MASK		0x10
+#define VXP_CDSP_MIC_SEL_MASK		0x08
+#define VXP_CDSP_VALID_IRQ_MASK		0x04
+#define VXP_CDSP_CODEC_RESET_MASK	0x02
+#define VXP_CDSP_DSP_RESET_MASK		0x01
+/* VXPOCKET 240/440 */
+#define P24_CDSP_MICS_SEL_MASK		0x18
+#define P24_CDSP_MIC20_SEL_MASK		0x10
+#define P24_CDSP_MIC38_SEL_MASK		0x08
+
+/* Constants used to access the MEMIRQ register (0x0C). */
+#define P44_MEMIRQ_MASTER_SLAVE_SEL_MASK 0x08
+#define P44_MEMIRQ_SYNCED_ALONE_SEL_MASK 0x04
+#define P44_MEMIRQ_WCLK_OUT_IN_SEL_MASK  0x02 /* Not used */
+#define P44_MEMIRQ_WCLK_UER_SEL_MASK     0x01 /* Not used */
+
+/* Micro levels (0x0C) */
+
+/* Constants used to access the DIALOG register (0x0D). */
+#define VXP_DLG_XILINX_REPROG_MASK	0x80	/* W */
+#define VXP_DLG_DATA_XICOR_MASK		0x80	/* R */
+#define VXP_DLG_RESERVED4_0_MASK	0x40
+#define VXP_DLG_RESERVED2_0_MASK	0x20
+#define VXP_DLG_RESERVED1_0_MASK	0x10
+#define VXP_DLG_DMAWRITE_SEL_MASK	0x08	/* W */
+#define VXP_DLG_DMAREAD_SEL_MASK	0x04	/* W */
+#define VXP_DLG_MEMIRQ_MASK		0x02	/* R */
+#define VXP_DLG_DMA16_SEL_MASK		0x02	/* W */
+#define VXP_DLG_ACK_MEMIRQ_MASK		0x01	/* R/W */
+
+
+#endif /* __VXPOCKET_H */
diff -urN linux-2.5.70-bk6/sound/ppc/awacs.c linux-2.5.70-bk7/sound/ppc/awacs.c
--- linux-2.5.70-bk6/sound/ppc/awacs.c	2003-05-26 18:00:40.000000000 -0700
+++ linux-2.5.70-bk7/sound/ppc/awacs.c	2003-06-02 04:39:13.000000000 -0700
@@ -32,7 +32,7 @@
 #define chip_t pmac_t
 
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) || defined(CONFIG_ADB_CUDA)
+#ifdef CONFIG_ADB_CUDA
 #define PMAC_AMP_AVAIL
 #endif
 
@@ -43,11 +43,7 @@
 	unsigned char amp_tone[2];
 } awacs_amp_t;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
-#define CHECK_CUDA_AMP() (adb_hardware == ADB_VIACUDA)
-#else
 #define CHECK_CUDA_AMP() (sys_ctrler == SYS_CTRLER_CUDA)
-#endif
 
 #endif /* PMAC_AMP_AVAIL */
 
@@ -649,8 +645,22 @@
 }
 
 #ifdef CONFIG_PMAC_PBOOK
+static void snd_pmac_awacs_suspend(pmac_t *chip)
+{
+	snd_pmac_awacs_write_noreg(chip, 1, (chip->awacs_reg[1]
+					     | MASK_AMUTE | MASK_CMUTE));
+}
+
 static void snd_pmac_awacs_resume(pmac_t *chip)
 {
+	if (machine_is_compatible("PowerBook3,1")
+	    || machine_is_compatible("PowerBook3,2")) {
+		do_mdelay(100, 0);
+		snd_pmac_awacs_write_reg(chip, 1,
+			chip->awacs_reg[1] & ~MASK_PAROUT);
+		do_mdelay(300, 0);
+	}
+
 	awacs_restore_all_regs(chip, 0);
 	if (chip->model == PMAC_SCREAMER) {
 		/* reset power bits in reg 6 */
@@ -868,6 +878,7 @@
 	 */
 	chip->set_format = snd_pmac_awacs_set_format;
 #ifdef CONFIG_PMAC_PBOOK
+	chip->suspend = snd_pmac_awacs_suspend;
 	chip->resume = snd_pmac_awacs_resume;
 #endif
 #ifdef PMAC_SUPPORT_AUTOMUTE
diff -urN linux-2.5.70-bk6/sound/ppc/keywest.c linux-2.5.70-bk7/sound/ppc/keywest.c
--- linux-2.5.70-bk6/sound/ppc/keywest.c	2003-05-26 18:00:59.000000000 -0700
+++ linux-2.5.70-bk7/sound/ppc/keywest.c	2003-06-02 04:39:13.000000000 -0700
@@ -49,6 +49,10 @@
 };
 
 
+#ifndef i2c_device_name
+#define i2c_device_name(x)	((x)->dev.name)
+#endif
+
 static int keywest_attach_adapter(struct i2c_adapter *adapter)
 {
 	int err;
@@ -57,20 +61,21 @@
 	if (! keywest_ctx)
 		return -EINVAL;
 
-	if (strncmp(adapter->name, "mac-io", 6))
+	if (strncmp(i2c_device_name(adapter), "mac-io", 6))
 		return 0; /* ignored */
 
 	new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
 	if (! new_client)
 		return -ENOMEM;
 
+	memset(new_client, 0, sizeof(*new_client));
 	new_client->addr = keywest_ctx->addr;
-	new_client->data = keywest_ctx;
+	i2c_set_clientdata(new_client, keywest_ctx);
 	new_client->adapter = adapter;
 	new_client->driver = &keywest_driver;
 	new_client->flags = 0;
 
-	strcpy(new_client->name, keywest_ctx->name);
+	strcpy(i2c_device_name(new_client), keywest_ctx->name);
 
 	new_client->id = keywest_ctx->id++; /* Automatically unique */
 	keywest_ctx->client = new_client;
diff -urN linux-2.5.70-bk6/sound/ppc/pmac.c linux-2.5.70-bk7/sound/ppc/pmac.c
--- linux-2.5.70-bk6/sound/ppc/pmac.c	2003-05-26 18:01:03.000000000 -0700
+++ linux-2.5.70-bk7/sound/ppc/pmac.c	2003-06-02 04:39:13.000000000 -0700
@@ -36,17 +36,6 @@
 #include <asm/feature.h>
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
-#define pmu_suspend()	/**/
-#define pmu_resume()	/**/
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
-#define request_OF_resource(io,num,str)  1
-#define release_OF_resource(io,num) /**/
-#endif
-
-
 #define chip_t pmac_t
 
 
diff -urN linux-2.5.70-bk6/sound/ppc/pmac.h linux-2.5.70-bk7/sound/ppc/pmac.h
--- linux-2.5.70-bk6/sound/ppc/pmac.h	2003-05-26 18:00:42.000000000 -0700
+++ linux-2.5.70-bk7/sound/ppc/pmac.h	2003-06-02 04:39:13.000000000 -0700
@@ -27,11 +27,6 @@
 #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>
@@ -39,7 +34,6 @@
 #ifdef CONFIG_ADB_PMU
 #include <linux/pmu.h>
 #endif
-#endif
 #include <linux/nvram.h>
 #include <linux/tty.h>
 #include <linux/vt_kern.h>
diff -urN linux-2.5.70-bk6/sound/ppc/tumbler.c linux-2.5.70-bk7/sound/ppc/tumbler.c
--- linux-2.5.70-bk6/sound/ppc/tumbler.c	2003-05-26 18:00:20.000000000 -0700
+++ linux-2.5.70-bk7/sound/ppc/tumbler.c	2003-06-02 04:39:13.000000000 -0700
@@ -442,7 +442,7 @@
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 1;
 	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = info->max;
+	uinfo->value.integer.max = info->max - 1;
 	return 0;
 }
 
@@ -867,6 +867,8 @@
 {
 	pmac_tumbler_t *mix = chip->mixer_data;
 
+	write_audio_gpio(&mix->audio_reset, 0);
+	mdelay(200);
 	write_audio_gpio(&mix->audio_reset, 1);
 	mdelay(100);
 	write_audio_gpio(&mix->audio_reset, 0);
@@ -970,9 +972,6 @@
 	chip->mixer_data = mix;
 	chip->mixer_free = tumbler_cleanup;
 
-	if ((err = tumbler_init(chip)) < 0)
-		return err;
-
 	/* set up TAS */
 	tas_node = find_devices("deq");
 	if (tas_node == NULL)
@@ -1019,6 +1018,9 @@
 	if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0)
 		return err;
 
+	if ((err = tumbler_init(chip)) < 0)
+		return err;
+
 #ifdef CONFIG_PMAC_PBOOK
 	chip->resume = tumbler_resume;
 #endif
diff -urN linux-2.5.70-bk6/sound/usb/usbaudio.c linux-2.5.70-bk7/sound/usb/usbaudio.c
--- linux-2.5.70-bk6/sound/usb/usbaudio.c	2003-06-02 04:39:08.000000000 -0700
+++ linux-2.5.70-bk7/sound/usb/usbaudio.c	2003-06-02 04:39:13.000000000 -0700
@@ -111,12 +111,15 @@
 	struct list_head list;
 	snd_pcm_format_t format;	/* format type */
 	unsigned int channels;		/* # channels */
+	unsigned int nonaudio: 1;	/* non-audio (type II) */
+	unsigned int frame_size;	/* samples per frame for non-audio */
 	int iface;			/* interface number */
 	unsigned char altsetting;	/* corresponding alternate setting */
 	unsigned char altset_idx;	/* array index of altenate setting */
 	unsigned char attributes;	/* corresponding attributes of cs endpoint */
 	unsigned char endpoint;		/* endpoint */
 	unsigned char ep_attr;		/* endpoint attributes */
+	unsigned int maxpacksize;	/* max. packet size */
 	unsigned int rates;		/* rate bitmasks */
 	unsigned int rate_min, rate_max;	/* min/max rates */
 	unsigned int nr_rates;		/* number of rate table entries */
@@ -159,6 +162,7 @@
 	unsigned int curpacksize;	/* current packet size in bytes (for capture) */
 	unsigned int curframesize;	/* current packet size in frames (for capture) */
 	unsigned int fill_max: 1;	/* fill max packet size always */
+	unsigned int nonaudio: 1;	/* Type II format (MPEG, AC3) */
 
 	unsigned int running: 1;	/* running status */
 
@@ -401,10 +405,14 @@
 		    urb->iso_frame_desc[i].actual_length < 3)
 			continue;
 		f = combine_triple(cp);
+#if 0
 		if (f < subs->freqn - (subs->freqn>>3) || f > subs->freqmax) {
-			snd_printd(KERN_WARNING "requested frequency %u (nominal %u) out of range!\n", f, subs->freqn);
+			snd_printd(KERN_WARNING "requested frequency %d (%u,%03uHz) out of range (current nominal %d (%u,%03uHz))\n",
+				   f, f >> 14, (f & ((1 << 14) - 1) * 1000) / ((1 << 14) - 1),
+				   subs->freqn, subs->freqn >> 14, (subs->freqn & ((1 << 14) - 1) * 1000) / ((1 << 14) - 1));
 			continue;
 		}
+#endif
 		found = f;
 	}
 	if (found) {
@@ -459,6 +467,22 @@
 		subs->transfer_sched += counts;
 		if (subs->transfer_sched >= runtime->period_size) {
 			subs->transfer_sched -= runtime->period_size;
+			if (subs->nonaudio) {
+				if (subs->transfer_sched > 0) {
+					/* FIXME: fill-max mode is not supported yet */
+					offs -= subs->transfer_sched;
+					counts -= subs->transfer_sched;
+					urb->iso_frame_desc[i].length = counts * stride;
+					subs->transfer_sched = 0;
+				}
+				i++;
+				if (i < ctx->packets) {
+					/* add a transfer delimiter */
+					urb->iso_frame_desc[i].offset = offs * stride;
+					urb->iso_frame_desc[i].length = 0;
+					urb->number_of_packets++;
+				}
+			}
 			break;
  		}
 	}
@@ -865,6 +889,8 @@
 		u->subs = subs;
 		u->transfer = 0;
 		u->packets = npacks[i];
+		if (subs->nonaudio)
+			u->packets++; /* for transfer delimiter */
 		if (! is_playback) {
 			/* allocate a capture buffer per urb */
 			u->buf = kmalloc(maxsize * u->packets, GFP_KERNEL);
@@ -918,6 +944,7 @@
 static struct audioformat *find_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
 {
 	struct list_head *p;
+	struct audioformat *found = NULL;
 
 	list_for_each(p, &subs->fmt_list) {
 		struct audioformat *fp;
@@ -927,16 +954,20 @@
 			continue;
 		if (runtime->rate < fp->rate_min || runtime->rate > fp->rate_max)
 			continue;
-		if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
-			return fp;
-		else {
+		if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) {
+			if (! found || fp->maxpacksize > found->maxpacksize)
+				found = fp;
+		} else {
 			unsigned int i;
 			for (i = 0; i < fp->nr_rates; i++)
-				if (fp->rate_table[i] == runtime->rate)
-					return fp;
+				if (fp->rate_table[i] == runtime->rate) {
+					if (! found || fp->maxpacksize > found->maxpacksize)
+						found = fp;
+					break;
+				}
 		}
 	}
-	return NULL;
+	return found;
 }
 
 
@@ -1069,8 +1100,11 @@
 		 * QUIRK: plantronics headset has adaptive-in
 		 * although it's really not...
 		 */
-		if (dev->descriptor.idVendor == 0x047f &&
-		    dev->descriptor.idProduct == 0x0ca1)
+		if ((dev->descriptor.idVendor == 0x047f &&
+		     dev->descriptor.idProduct == 0x0ca1) ||
+		    /* Griffin iMic (note that there is an older model 77d:223) */
+		    (dev->descriptor.idVendor == 0x077d &&
+		     dev->descriptor.idProduct == 0x07af))
 			goto _ok;
 		/* check endpoint */
 		if (altsd->bNumEndpoints < 2 ||
@@ -1401,6 +1435,25 @@
 		if (rates[i])
 			rmaster = rates[i];
 	}
+	/* check whether channels match for all distinct rates */
+	memset(channels, 0, sizeof(channels));
+	list_for_each(p, &subs->fmt_list) {
+		struct audioformat *f;
+		f = list_entry(p, struct audioformat, list);
+		if (f->rates & SNDRV_PCM_RATE_CONTINUOUS)
+			continue;
+		for (i = 0; i < 32; i++) {
+			if (f->rates & (1 << i))
+				channels[i] |= (1 << f->channels);
+		}
+	}
+	cmaster = 0;
+	for (i = 0; i < 32; i++) {
+		if (cmaster != channels[i] && cmaster && channels[i])
+			return 1;
+		if (channels[i])
+			cmaster = channels[i];
+	}
 	return 0;
 }
 
@@ -1434,6 +1487,11 @@
 			runtime->hw.channels_min = fp->channels;
 		if (runtime->hw.channels_max < fp->channels)
 			runtime->hw.channels_max = fp->channels;
+		if (fp->nonaudio && fp->frame_size > 0) {
+			/* FIXME: there might be more than one audio formats... */
+			runtime->hw.period_bytes_min = runtime->hw.period_bytes_max =
+				fp->frame_size;
+		}
 	}
 
 	/* set the period time minimum 1ms */
@@ -1660,6 +1718,8 @@
 			}
 			snd_iprintf(buffer, "\n");
 		}
+		// snd_iprintf(buffer, "    Max Packet Size = %d\n", fp->maxpacksize);
+		// snd_iprintf(buffer, "    EP Attribute = 0x%x\n", fp->attributes);
 	}
 }
 
@@ -1738,6 +1798,7 @@
 	subs->formats |= 1ULL << fp->format;
 	subs->endpoint = fp->endpoint;
 	subs->num_formats++;
+	subs->nonaudio = fp->nonaudio;
 }
 
 
@@ -1800,6 +1861,13 @@
 		if (! subs->endpoint)
 			break;
 		if (subs->endpoint == fp->endpoint) {
+			if (fp->nonaudio) {
+				if (!subs->nonaudio || subs->formats != (1ULL << fp->format))
+					continue; /* non-linear formats are handled exclusively */
+			} else {
+				if (subs->nonaudio)
+					continue;
+			}
 			list_add_tail(&fp->list, &subs->fmt_list);
 			subs->num_formats++;
 			subs->formats |= 1ULL << fp->format;
@@ -1855,62 +1923,52 @@
 
 
 /*
- * parse the audio format type descriptor
+ * parse the audio format type I descriptor
  * and returns the corresponding pcm format
+ *
+ * @dev: usb device
+ * @fp: audioformat record
+ * @format: the format tag (wFormatTag)
+ * @fmt: the format type descriptor
  */
-static int parse_audio_format_type(struct usb_device *dev, int iface_no, int altno,
-				   int format, unsigned char *fmt)
+static int parse_audio_format_i_type(struct usb_device *dev, struct audioformat *fp,
+				     int format, unsigned char *fmt)
 {
-	int format_type = fmt[3];
 	int pcm_format;
+	int sample_width, sample_bytes;
 
-	/* FIXME: needed support for TYPE II and III */
-	if (format_type != USB_FORMAT_TYPE_I) {
-		snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
-			   dev->devnum, iface_no, altno, format_type);
-		return -1;
-	}
-	
 	/* FIXME: correct endianess and sign? */
 	pcm_format = -1;
+	sample_width = fmt[6];
+	sample_bytes = fmt[5];
 	switch (format) {
 	case 0: /* some devices don't define this correctly... */
-		snd_printd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
-			   dev->devnum, iface_no, altno);
+		snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
+			    dev->devnum, iface_no, altno);
 		/* fall-through */
 	case USB_AUDIO_FORMAT_PCM:
+		if (sample_width > sample_bytes * 8) {
+			snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n",
+				   dev->devnum, fp->iface, fp->altsetting,
+				   sample_width, sample_bytes);
+		}
 		/* check the format byte size */
-		switch (fmt[6]) {
-		case 8:
+		switch (fmt[5]) {
+		case 1:
 			pcm_format = SNDRV_PCM_FORMAT_U8;
 			break;
-		case 16:
+		case 2:
 			pcm_format = SNDRV_PCM_FORMAT_S16_LE;
 			break;
-		case 18:
-		case 20:
-			if (fmt[5] == 3)
-				pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
-			else
-				snd_printk(KERN_ERR "%d:%u:%d : non-supported sample bit %d in %d bytes\n",
-					   dev->devnum, iface_no, altno, fmt[6], fmt[5]);
-			break;
-		case 24:
-			if (fmt[5] == 4)
-				/* FIXME: correct?  or S32_LE? */
-				pcm_format = SNDRV_PCM_FORMAT_S24_LE;
-			else if (fmt[5] == 3)
-				pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
-			else
-				snd_printk(KERN_ERR "%d:%u:%d : non-supported sample bit %d in %d bytes\n",
-					   dev->devnum, iface_no, altno, format, fmt[5]);
+		case 3:
+			pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
 			break;
-		case 32:
+		case 4:
 			pcm_format = SNDRV_PCM_FORMAT_S32_LE;
 			break;
 		default:
 			snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
-				   dev->devnum, iface_no, altno, fmt[6], fmt[5]);
+				   dev->devnum, fp->iface, fp->altsetting, sample_width, sample_bytes);
 			break;
 		}
 		break;
@@ -1932,13 +1990,168 @@
 		break;
 	default:
 		snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n",
-			   dev->devnum, iface_no, altno, format);
+			   dev->devnum, fp->iface, fp->altsetting, format);
 		break;
 	}
 	return pcm_format;
 }
 
 
+/*
+ * parse the format descriptor and stores the possible sample rates
+ * on the audioformat table.
+ *
+ * @dev: usb device
+ * @fp: audioformat record
+ * @fmt: the format descriptor
+ * @offset: the start offset of descriptor pointing the rate type
+ *          (7 for type I and II, 8 for type II)
+ */
+static int parse_audio_format_rates(struct usb_device *dev, struct audioformat *fp,
+				    unsigned char *fmt, int offset)
+{
+	int nr_rates = fmt[offset];
+	if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) {
+		snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", 
+				   dev->devnum, fp->iface, fp->altsetting);
+		return -1;
+	}
+
+	if (nr_rates) {
+		/*
+		 * build the rate table and bitmap flags
+		 */
+		int r, idx, c;
+		/* this table corresponds to the SNDRV_PCM_RATE_XXX bit */
+		static unsigned int conv_rates[] = {
+			5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
+			64000, 88200, 96000, 176400, 192000
+		};
+		fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
+		if (fp->rate_table == NULL) {
+			snd_printk(KERN_ERR "cannot malloc\n");
+			return -1;
+		}
+
+		fp->nr_rates = nr_rates;
+		fp->rate_min = fp->rate_max = combine_triple(&fmt[8]);
+		for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) {
+			unsigned int rate = fp->rate_table[r] = combine_triple(&fmt[idx]);
+			if (rate < fp->rate_min)
+				fp->rate_min = rate;
+			else if (rate > fp->rate_max)
+				fp->rate_max = rate;
+			for (c = 0; c < (int)ARRAY_SIZE(conv_rates); c++) {
+				if (rate == conv_rates[c]) {
+					fp->rates |= (1 << c);
+					break;
+				}
+			}
+		}
+	} else {
+		/* continuous rates */
+		fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
+		fp->rate_min = combine_triple(&fmt[offset + 1]);
+		fp->rate_max = combine_triple(&fmt[offset + 4]);
+	}
+	return 0;
+}
+
+/*
+ * parse the format type I and III descriptors
+ */
+static int parse_audio_format_i(struct usb_device *dev, struct audioformat *fp,
+				int format, unsigned char *fmt)
+{
+	int pcm_format;
+
+	if (fmt[3] == USB_FORMAT_TYPE_III) {
+		/* FIXME: the format type is really IECxxx
+		 *        but we give normal PCM format to get the existing
+		 *        apps working...
+		 */
+		pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+	} else {
+		pcm_format = parse_audio_format_i_type(dev, fp, format, fmt);
+		if (pcm_format < 0)
+			return -1;
+	}
+	fp->format = pcm_format;
+	fp->channels = fmt[4];
+	if (fp->channels < 1) {
+		snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
+			   dev->devnum, fp->iface, fp->altsetting, fp->channels);
+		return -1;
+	}
+	return parse_audio_format_rates(dev, fp, fmt, 7);
+}
+
+/*
+ * prase the format type II descriptor
+ */
+static int parse_audio_format_ii(struct usb_device *dev, struct audioformat *fp,
+				 int format, unsigned char *fmt)
+{
+	int brate, framesize;
+	switch (format) {
+	case USB_AUDIO_FORMAT_AC3:
+		/* FIXME: there is no AC3 format defined yet */
+		// fp->format = SNDRV_PCM_FORMAT_AC3;
+		fp->format = SNDRV_PCM_FORMAT_U8; /* temporarily hack to receive byte streams */
+		break;
+	case USB_AUDIO_FORMAT_MPEG:
+		fp->format = SNDRV_PCM_FORMAT_MPEG;
+		break;
+	default:
+		snd_printd(KERN_INFO "%d:%u:%d : unknown format tag 0x%x is detected.  processed as MPEG.\n",
+			   dev->devnum, fp->iface, fp->altsetting, format);
+		fp->format = SNDRV_PCM_FORMAT_MPEG;
+		break;
+	}
+	fp->channels = 1;
+	fp->nonaudio = 1;
+	brate = combine_word(&fmt[4]); 	/* fmt[4,5] : wMaxBitRate (in kbps) */
+	framesize = combine_word(&fmt[6]); /* fmt[6,7]: wSamplesPerFrame */
+	snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
+	fp->frame_size = framesize;
+	return parse_audio_format_rates(dev, fp, fmt, 8); /* fmt[8..] sample rates */
+}
+
+static int parse_audio_format(struct usb_device *dev, struct audioformat *fp,
+			      int format, unsigned char *fmt, int stream)
+{
+	int err;
+
+	switch (fmt[3]) {
+	case USB_FORMAT_TYPE_I:
+	case USB_FORMAT_TYPE_III:
+		err = parse_audio_format_i(dev, fp, format, fmt);
+		break;
+	case USB_FORMAT_TYPE_II:
+		err = parse_audio_format_ii(dev, fp, format, fmt);
+		break;
+	default:
+		snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
+			   dev->devnum, fp->iface, fp->altsetting, fmt[3]);
+		return -1;
+	}
+	if (err < 0)
+		return err;
+#if 1
+	/* FIXME: temporary hack for extigy */
+	/* extigy apparently supports sample rates other than 48k
+	 * but not in ordinary way.  so we enable only 48k atm.
+	 */
+	if (dev->descriptor.idVendor == 0x041e && dev->descriptor.idProduct == 0x3000) {
+		if (fmt[3] == USB_FORMAT_TYPE_I &&
+		    stream == SNDRV_PCM_STREAM_PLAYBACK &&
+		    fp->rates != SNDRV_PCM_RATE_48000)
+			return -1; /* use 48k only */
+	}
+#endif
+	return 0;
+}	
+
 static int parse_audio_endpoints(snd_usb_audio_t *chip, int iface_no)
 {
 	struct usb_device *dev;
@@ -1947,7 +2160,7 @@
 	struct usb_host_interface *alts;
 	struct usb_interface_descriptor *altsd;
 	int i, altno, err, stream;
-	int channels, nr_rates, pcm_format, format;
+	int format;
 	struct audioformat *fp;
 	unsigned char *fmt, *csep;
 
@@ -2003,24 +2216,6 @@
 			continue;
 		}
 
-		pcm_format = parse_audio_format_type(dev, iface_no, altno, format, fmt);
-		if (pcm_format < 0)
-			continue;
-		
-		channels = fmt[4];
-		if (channels < 1) {
-			snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
-				   dev->devnum, iface_no, altno, channels);
-			continue;
-		}
-
-		nr_rates = fmt[7];
-		if (fmt[0] < 8 + 3 * (nr_rates ? nr_rates : 2)) {
-			snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", 
-				   dev->devnum, iface_no, altno);
-			continue;
-		}
-
 		csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT);
 		if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) {
 			snd_printk(KERN_ERR "%d:%u:%d : no or invalid class specific endpoint descriptor\n", 
@@ -2038,57 +2233,18 @@
 		fp->iface = iface_no;
 		fp->altsetting = altno;
 		fp->altset_idx = i;
-		fp->format = pcm_format;
 		fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
 		fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
-		fp->channels = channels;
+		fp->maxpacksize = get_endpoint(alts, 0)->wMaxPacketSize;
 		fp->attributes = csep[3];
 
-		if (nr_rates) {
-			/*
-			 * build the rate table and bitmap flags
-			 */
-			int r, idx, c;
-			/* this table corresponds to the SNDRV_PCM_RATE_XXX bit */
-			static unsigned int conv_rates[] = {
-				5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
-				64000, 88200, 96000, 176400, 192000
-			};
-			fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
-			if (fp->rate_table == NULL) {
-				snd_printk(KERN_ERR "cannot malloc\n");
-				kfree(fp);
-				break;
-			}
-
-			fp->nr_rates = nr_rates;
-			fp->rate_min = fp->rate_max = combine_triple(&fmt[8]);
-			for (r = 0, idx = 8; r < nr_rates; r++, idx += 3) {
-				unsigned int rate = fp->rate_table[r] = combine_triple(&fmt[idx]);
-				if (rate < fp->rate_min)
-					fp->rate_min = rate;
-				else if (rate > fp->rate_max)
-					fp->rate_max = rate;
-				for (c = 0; c < 13; c++) {
-					if (rate == conv_rates[c]) {
-						fp->rates |= (1 << c);
-						break;
-					}
-				}
-#if 0 // FIXME - we need to define constraint
-				if (c >= 13)
-					fp->rates |= SNDRV_PCM_RATE_KNOT; /* unconventional rate */
-#endif
-			}
-
-		} else {
-			/* continuous rates */
-			fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
-			fp->rate_min = combine_triple(&fmt[8]);
-			fp->rate_max = combine_triple(&fmt[11]);
+		if (parse_audio_format(dev, fp, format, fmt, stream) < 0) {
+			if (fp->rate_table)
+				kfree(fp->rate_table);
+			kfree(fp);
+			continue;
 		}
 
-		
 		snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint 0x%x\n", dev->devnum, iface_no, i, fp->endpoint);
 		err = add_audio_endpoint(chip, stream, fp);
 		if (err < 0) {
@@ -2279,26 +2435,25 @@
 #define EXTIGY_FIRMWARE_SIZE_OLD 794
 #define EXTIGY_FIRMWARE_SIZE_NEW 483
 
-static int snd_usb_boot_quirk(struct usb_device *dev,
-			      struct usb_interface *intf,
-			      const snd_usb_audio_quirk_t *quirk)
+static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf)
 {
 	struct usb_host_config *config = dev->actconfig;
+	int err;
 
-	/* FIXME: we need to handle composite type quirks later.. */
-	switch (quirk->type) {
-	case QUIRK_BOOT_EXTIGY:
-		snd_printdd(KERN_INFO "extigy_boot: boot length = %d\n", get_cfg_desc(config)->wTotalLength);
-		if (get_cfg_desc(config)->wTotalLength == EXTIGY_FIRMWARE_SIZE_OLD ||
-		    get_cfg_desc(config)->wTotalLength == EXTIGY_FIRMWARE_SIZE_NEW) {
-			/* Send message to force it to reconnect with full interface. */
-			usb_control_msg(dev, usb_sndctrlpipe(dev,0),
-					0x10, 0x43, 0x0001, 0x000a, NULL, 0, HZ);
-			usb_get_device_descriptor(dev);
-			usb_set_configuration(dev, get_cfg_desc(config)->bConfigurationValue);
-			return -ENODEV; /* quit this anyway */
-		}
-		return 0;
+	if (get_cfg_desc(config)->wTotalLength == EXTIGY_FIRMWARE_SIZE_OLD ||
+	    get_cfg_desc(config)->wTotalLength == EXTIGY_FIRMWARE_SIZE_NEW) {
+		snd_printdd("sending Extigy boot sequence...\n");
+		/* Send message to force it to reconnect with full interface. */
+		err = usb_control_msg(dev, usb_sndctrlpipe(dev,0),
+				      0x10, 0x43, 0x0001, 0x000a, NULL, 0, HZ);
+		if (err < 0) snd_printdd("error sending boot message: %d\n", err);
+		err = usb_get_device_descriptor(dev);
+		config = dev->actconfig;
+		if (err < 0) snd_printdd("error usb_get_device_descriptor: %d\n", err);
+		err = usb_set_configuration(dev, get_cfg_desc(config)->bConfigurationValue);
+		if (err < 0) snd_printdd("error usb_set_configuration: %d\n", err);
+		snd_printdd("extigy_boot: new boot length = %d\n", get_cfg_desc(config)->wTotalLength);
+		return -ENODEV; /* quit this anyway */
 	}
 	return 0;
 }
@@ -2311,9 +2466,6 @@
 				struct usb_interface *iface,
 				const snd_usb_audio_quirk_t *quirk)
 {
-	if (quirk->type & QUIRK_BOOT_MASK)
-		return 1; /* continue as the normal device */
-
 	switch (quirk->type) {
 	case QUIRK_MIDI_FIXED_ENDPOINT:
 	case QUIRK_MIDI_YAMAHA:
@@ -2414,7 +2566,7 @@
 		}
 	}
 	if (len > 0)
-		strlcat(card->longname, ' ', sizeof(card->longname));
+		strlcat(card->longname, " ", sizeof(card->longname));
 
 	len = strlen(card->longname);
 
@@ -2468,10 +2620,12 @@
 		goto __err_val;
 	}
 
-	if (quirk) {
-		/* try to check the boot quirk */
-		if (snd_usb_boot_quirk(dev, intf, quirk) < 0)
+	/* SB Extigy needs special boot-up sequence */
+	/* if more models come, this will go to the quirk list. */
+	if (dev->descriptor.idVendor == 0x041e && dev->descriptor.idProduct == 0x3000) {
+		if (snd_usb_extigy_boot_quirk(dev, intf) < 0)
 			goto __err_val;
+		config = dev->actconfig;
 	}
 
 	/*
diff -urN linux-2.5.70-bk6/sound/usb/usbaudio.h linux-2.5.70-bk7/sound/usb/usbaudio.h
--- linux-2.5.70-bk6/sound/usb/usbaudio.h	2003-05-26 18:00:41.000000000 -0700
+++ linux-2.5.70-bk7/sound/usb/usbaudio.h	2003-06-02 04:39:13.000000000 -0700
@@ -156,9 +156,6 @@
 #define QUIRK_AUDIO_FIXED_ENDPOINT	4
 #define QUIRK_STANDARD_INTERFACE	5
 
-#define QUIRK_BOOT_MASK			0x80
-#define QUIRK_BOOT_EXTIGY		(QUIRK_BOOT_MASK | 0)
-
 typedef struct snd_usb_audio_quirk snd_usb_audio_quirk_t;
 typedef struct snd_usb_midi_endpoint_info snd_usb_midi_endpoint_info_t;
 
diff -urN linux-2.5.70-bk6/sound/usb/usbmixer.c linux-2.5.70-bk7/sound/usb/usbmixer.c
--- linux-2.5.70-bk6/sound/usb/usbmixer.c	2003-05-26 18:00:27.000000000 -0700
+++ linux-2.5.70-bk7/sound/usb/usbmixer.c	2003-06-02 04:39:13.000000000 -0700
@@ -161,7 +161,7 @@
 		return 0;
 
 	for (p = state->map; p->id; p++) {
-		if (p->id == unitid &&
+		if (p->id == unitid && p->name &&
 		    (! control || ! p->control || control == p->control)) {
 			buflen--;
 			return strlcpy(buf, p->name, buflen);
@@ -170,6 +170,22 @@
 	return 0;
 }
 
+/* check whether the control should be ignored */
+static int check_ignored_ctl(mixer_build_t *state, int unitid, int control)
+{
+	const struct usbmix_name_map *p;
+
+	if (! state->map)
+		return 0;
+	for (p = state->map; p->id; p++) {
+		if (p->id == unitid && ! p->name &&
+		    (! control || ! p->control || control == p->control)) {
+			// printk("ignored control %d:%d\n", unitid, control);
+			return 1;
+		}
+	}
+	return 0;
+}
 
 /*
  * find an audio control unit with the given unit id
@@ -762,6 +778,9 @@
 		return;
 	}
 
+	if (check_ignored_ctl(state, unitid, control))
+		return;
+
 	cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL);
 	if (! cval) {
 		snd_printk(KERN_ERR "cannot malloc kcontrol\n");
@@ -928,6 +947,9 @@
 	snd_kcontrol_t *kctl;
 	usb_audio_term_t iterm;
 
+	if (check_ignored_ctl(state, unitid, 0))
+		return;
+
 	cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL);
 	if (! cval)
 		return;
@@ -1156,6 +1178,8 @@
 		/* FIXME: bitmap might be longer than 8bit */
 		if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1))))
 			continue;
+		if (check_ignored_ctl(state, unitid, valinfo->control))
+			continue;
 		cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL);
 		if (! cval) {
 			snd_printk(KERN_ERR "cannot malloc kcontrol\n");
@@ -1169,7 +1193,14 @@
 		cval->channels = 1;
 
 		/* get min/max values */
-		get_min_max(cval, valinfo->min_value);
+		if (type == USB_PROC_UPDOWN && cval->control == USB_PROC_UPDOWN_MODE_SEL) {
+			/* FIXME: hard-coded */
+			cval->min = 1;
+			cval->max = dsc[15];
+			cval->res = 1;
+			cval->initialized = 1;
+		} else
+			get_min_max(cval, valinfo->min_value);
 
 		kctl = snd_ctl_new1(&mixer_procunit_ctl, cval);
 		if (! kctl) {
@@ -1191,7 +1222,7 @@
 			if (! len)
 				strlcpy(kctl->id.name, name, sizeof(kctl->id.name));
 		}
-		strlcat(kctl->id.name, ' ', sizeof(kctl->id.name));
+		strlcat(kctl->id.name, " ", sizeof(kctl->id.name));
 		strlcat(kctl->id.name, valinfo->suffix, sizeof(kctl->id.name));
 
 		snd_printdd(KERN_INFO "[%d] PU [%s] ch = %d, val = %d/%d\n",
@@ -1327,6 +1358,9 @@
 		return -EINVAL;
 	}
 
+	if (check_ignored_ctl(state, unitid, 0))
+		return 0;
+
 	for (i = 0; i < num_ins; i++) {
 		if ((err = parse_audio_unit(state, desc[5 + i])) < 0)
 			return err;
diff -urN linux-2.5.70-bk6/sound/usb/usbmixer_maps.c linux-2.5.70-bk7/sound/usb/usbmixer_maps.c
--- linux-2.5.70-bk6/sound/usb/usbmixer_maps.c	2003-05-26 18:00:59.000000000 -0700
+++ linux-2.5.70-bk7/sound/usb/usbmixer_maps.c	2003-06-02 04:39:13.000000000 -0700
@@ -59,32 +59,33 @@
 	{ 2, "PCM Playback" }, /* FU */
 	/* 3: IT pcm */
 	/* 4: IT digital in */
-	{ 5, "Digital In Playback Source" }, /* SU */
+	{ 5, NULL }, /* DISABLED: this seems to be bogus on some firmware */
 	{ 6, "Digital In" }, /* FU */
 	/* 7: IT line */
 	{ 8, "Line Playback" }, /* FU */
 	/* 9: IT mic */
 	{ 10, "Mic Playback" }, /* FU */
-	{ 11, "Capture Source" }, /* SU */
+	{ 11, "Capture Input Source" }, /* SU */
 	{ 12, "Capture" }, /* FU */
 	/* 13: OT pcm capture */
 	/* 14: MU (w/o controls) */
 	/* 15: PU (3D enh) */
 	/* 16: MU (w/o controls) */
-	/* 17: PU (updown) */		/* FIXME: what control? */
+	{ 17, NULL, 1 }, /* DISABLED: PU-switch (any effect?) */
+	{ 17, "Channel Routing", 2 },	/* PU: mode select */
 	{ 18, "Tone Control - Bass", USB_FEATURE_BASS }, /* FU */
 	{ 18, "Tone Control - Treble", USB_FEATURE_TREBLE }, /* FU */
 	{ 18, "Master Playback" }, /* FU; others */
 	/* 19: OT speaker */
 	/* 20: OT headphone */
-	{ 21, "Digital Out Extension" }, /* EU */ /* FIXME: what? */
+	{ 21, NULL }, /* DISABLED: EU (for what?) */
 	{ 22, "Digital Out Playback" }, /* FU */
 	{ 23, "Digital Out1 Playback" }, /* FU */  /* FIXME: corresponds to 24 */
 	/* 24: OT digital out */
-	{ 25, "Digital Out2 Playback" }, /* FU */  /* FIXME: corresponds to 26 */
-	/* 26: OT digital out */
-	{ 27, "Output Extension" }, /* EU */ /* FIXME: what? */
-	/* 28: FU (mute) */
+	{ 25, "IEC958 Optical Playback" }, /* FU */
+	{ 26, "IEC958 Optical Playback" }, /* OT */
+	{ 27, NULL }, /* DISABLED: EU (for what?) */
+	/* 28: FU speaker (mute) */
 	{ 0 } /* terminator */
 };
 
diff -urN linux-2.5.70-bk6/sound/usb/usbquirks.h linux-2.5.70-bk7/sound/usb/usbquirks.h
--- linux-2.5.70-bk6/sound/usb/usbquirks.h	2003-05-26 18:00:39.000000000 -0700
+++ linux-2.5.70-bk7/sound/usb/usbquirks.h	2003-06-02 04:39:13.000000000 -0700
@@ -742,20 +742,4 @@
 	}
 },
 
-/*
- * Boot-up quirks
- */
-/* Extigy needs to send a vendor-specific control to boot up the
- * correct interface
- */
-{
-	USB_DEVICE(0x041e, 0x3000),
-	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
-		.vendor_name = "CreativeLabs",
-		.product_name = "Sound Blaster Extigy",
-		.ifnum = QUIRK_ANY_INTERFACE,
-		.type = QUIRK_BOOT_EXTIGY
-	}
-},
-
 #undef USB_DEVICE_VENDOR_SPEC
