Merge branch 'fix/misc' into topic/misc
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index 7f4dceb..d0eb696 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -300,6 +300,74 @@
            control correctly. If you have problems regarding this, try
            another ALSA compliant mixer (alsamixer works).
 
+  Module snd-azt1605
+  ------------------
+
+    Module for Aztech Sound Galaxy soundcards based on the Aztech AZT1605
+    chipset.
+
+    port	- port # for BASE (0x220,0x240,0x260,0x280)
+    wss_port	- port # for WSS (0x530,0x604,0xe80,0xf40)
+    irq		- IRQ # for WSS (7,9,10,11)
+    dma1	- DMA # for WSS playback (0,1,3)
+    dma2	- DMA # for WSS capture (0,1), -1 = disabled (default)
+    mpu_port	- port # for MPU-401 UART (0x300,0x330), -1 = disabled (default)
+    mpu_irq	- IRQ # for MPU-401 UART (3,5,7,9), -1 = disabled (default)
+    fm_port	- port # for OPL3 (0x388), -1 = disabled (default)
+
+    This module supports multiple cards. It does not support autoprobe: port,
+    wss_port, irq and dma1 have to be specified. The other values are
+    optional.
+
+    "port" needs to match the BASE ADDRESS jumper on the card (0x220 or 0x240)
+    or the value stored in the card's EEPROM for cards that have an EEPROM and
+    their "CONFIG MODE" jumper set to "EEPROM SETTING". The other values can
+    be choosen freely from the options enumerated above.
+
+    If dma2 is specified and different from dma1, the card will operate in
+    full-duplex mode. When dma1=3, only dma2=0 is valid and the only way to
+    enable capture since only channels 0 and 1 are available for capture.
+
+    Generic settings are "port=0x220 wss_port=0x530 irq=10 dma1=1 dma2=0
+    mpu_port=0x330 mpu_irq=9 fm_port=0x388".
+
+    Whatever IRQ and DMA channels you pick, be sure to reserve them for
+    legacy ISA in your BIOS.
+
+  Module snd-azt2316
+  ------------------
+
+    Module for Aztech Sound Galaxy soundcards based on the Aztech AZT2316
+    chipset.
+
+    port	- port # for BASE (0x220,0x240,0x260,0x280)
+    wss_port	- port # for WSS (0x530,0x604,0xe80,0xf40)
+    irq		- IRQ # for WSS (7,9,10,11)
+    dma1	- DMA # for WSS playback (0,1,3)
+    dma2	- DMA # for WSS capture (0,1), -1 = disabled (default)
+    mpu_port	- port # for MPU-401 UART (0x300,0x330), -1 = disabled (default)
+    mpu_irq	- IRQ # for MPU-401 UART (5,7,9,10), -1 = disabled (default)
+    fm_port	- port # for OPL3 (0x388), -1 = disabled (default)
+
+    This module supports multiple cards. It does not support autoprobe: port,
+    wss_port, irq and dma1 have to be specified. The other values are
+    optional.
+
+    "port" needs to match the BASE ADDRESS jumper on the card (0x220 or 0x240)
+    or the value stored in the card's EEPROM for cards that have an EEPROM and
+    their "CONFIG MODE" jumper set to "EEPROM SETTING". The other values can
+    be choosen freely from the options enumerated above.
+
+    If dma2 is specified and different from dma1, the card will operate in
+    full-duplex mode. When dma1=3, only dma2=0 is valid and the only way to
+    enable capture since only channels 0 and 1 are available for capture.
+
+    Generic settings are "port=0x220 wss_port=0x530 irq=10 dma1=1 dma2=0
+    mpu_port=0x330 mpu_irq=9 fm_port=0x388".
+
+    Whatever IRQ and DMA channels you pick, be sure to reserve them for
+    legacy ISA in your BIOS.
+
   Module snd-aw2
   --------------
 
@@ -1641,20 +1709,6 @@
 
     This card is also known as Audio Excel DSP 16 or Zoltrix AV302.
 
-  Module snd-sgalaxy
-  ------------------
-
-    Module for Aztech Sound Galaxy sound card.
-
-    sbport	- Port # for SB16 interface (0x220,0x240)
-    wssport	- Port # for WSS interface (0x530,0xe80,0xf40,0x604)
-    irq		- IRQ # (7,9,10,11)
-    dma1	- DMA #
-
-    This module supports multiple cards.
-
-    The power-management is supported.
-
   Module snd-sscape
   -----------------
 
diff --git a/include/sound/jack.h b/include/sound/jack.h
index d90b9fa..c140fc7 100644
--- a/include/sound/jack.h
+++ b/include/sound/jack.h
@@ -47,6 +47,9 @@
 	SND_JACK_BTN_0		= 0x4000,
 	SND_JACK_BTN_1		= 0x2000,
 	SND_JACK_BTN_2		= 0x1000,
+	SND_JACK_BTN_3		= 0x0800,
+	SND_JACK_BTN_4		= 0x0400,
+	SND_JACK_BTN_5		= 0x0200,
 };
 
 struct snd_jack {
@@ -55,7 +58,7 @@
 	int type;
 	const char *id;
 	char name[100];
-	unsigned int key[3];   /* Keep in sync with definitions above */
+	unsigned int key[6];   /* Keep in sync with definitions above */
 	void *private_data;
 	void (*private_free)(struct snd_jack *);
 };
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 85f1c6b..dfd9b76 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -278,6 +278,7 @@
 	snd_pcm_uframes_t hw_ptr_base;	/* Position at buffer restart */
 	snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */
 	unsigned long hw_ptr_jiffies;	/* Time when hw_ptr is updated */
+	unsigned long hw_ptr_buffer_jiffies; /* buffer time in jiffies */
 	snd_pcm_sframes_t delay;	/* extra delay; typically FIFO size */
 
 	/* -- HW params -- */
diff --git a/sound/core/init.c b/sound/core/init.c
index ec4a50c..2de45fb 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -607,11 +607,16 @@
 		return -EEXIST;
 	}
 	for (idx = 0; idx < snd_ecards_limit; idx++) {
-		if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1))
-			goto __exist;
+		if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1)) {
+			if (card == snd_cards[idx])
+				goto __ok;
+			else
+				goto __exist;
+		}
 	}
 	strcpy(card->id, buf1);
 	snd_info_card_id_change(card);
+__ok:
 	mutex_unlock(&snd_card_mutex);
 
 	return count;
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index f50ebf2..86afb13c 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -77,7 +77,7 @@
 	struct snd_mixer_oss_file *fmixer;
 
 	if (file->private_data) {
-		fmixer = (struct snd_mixer_oss_file *) file->private_data;
+		fmixer = file->private_data;
 		module_put(fmixer->card->module);
 		snd_card_file_remove(fmixer->card, file);
 		kfree(fmixer);
@@ -368,7 +368,7 @@
 
 static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-	return snd_mixer_oss_ioctl1((struct snd_mixer_oss_file *) file->private_data, cmd, arg);
+	return snd_mixer_oss_ioctl1(file->private_data, cmd, arg);
 }
 
 int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg)
@@ -582,7 +582,7 @@
 				     struct snd_mixer_oss_slot *pslot,
 				     int *left, int *right)
 {
-	struct slot *slot = (struct slot *)pslot->private_data;
+	struct slot *slot = pslot->private_data;
 	
 	*left = *right = 100;
 	if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
@@ -691,7 +691,7 @@
 				     struct snd_mixer_oss_slot *pslot,
 				     int left, int right)
 {
-	struct slot *slot = (struct slot *)pslot->private_data;
+	struct slot *slot = pslot->private_data;
 	
 	if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
 		snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
@@ -740,7 +740,7 @@
 					struct snd_mixer_oss_slot *pslot,
 					int *active)
 {
-	struct slot *slot = (struct slot *)pslot->private_data;
+	struct slot *slot = pslot->private_data;
 	int left, right;
 	
 	left = right = 1;
@@ -753,7 +753,7 @@
 					   struct snd_mixer_oss_slot *pslot,
 					   int *active)
 {
-	struct slot *slot = (struct slot *)pslot->private_data;
+	struct slot *slot = pslot->private_data;
 	int left, right;
 	
 	left = right = 1;
@@ -766,7 +766,7 @@
 					struct snd_mixer_oss_slot *pslot,
 					int active)
 {
-	struct slot *slot = (struct slot *)pslot->private_data;
+	struct slot *slot = pslot->private_data;
 	
 	snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0);
 	return 0;
@@ -776,7 +776,7 @@
 					   struct snd_mixer_oss_slot *pslot,
 					   int active)
 {
-	struct slot *slot = (struct slot *)pslot->private_data;
+	struct slot *slot = pslot->private_data;
 	
 	snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1);
 	return 0;
@@ -813,7 +813,7 @@
 		if (!(mixer->mask_recsrc & (1 << idx)))
 			continue;
 		pslot = &mixer->slots[idx];
-		slot = (struct slot *)pslot->private_data;
+		slot = pslot->private_data;
 		if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
 			continue;
 		if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
@@ -861,7 +861,7 @@
 		if (!(mixer->mask_recsrc & (1 << idx)))
 			continue;
 		pslot = &mixer->slots[idx];
-		slot = (struct slot *)pslot->private_data;
+		slot = pslot->private_data;
 		if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
 			continue;
 		if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
@@ -925,7 +925,7 @@
 
 static void snd_mixer_oss_slot_free(struct snd_mixer_oss_slot *chn)
 {
-	struct slot *p = (struct slot *)chn->private_data;
+	struct slot *p = chn->private_data;
 	if (p) {
 		if (p->allocated && p->assigned) {
 			kfree(p->assigned->name);
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index ac242a3..6b4b128 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -364,8 +364,7 @@
 static void snd_pcm_substream_proc_info_read(struct snd_info_entry *entry,
 					     struct snd_info_buffer *buffer)
 {
-	snd_pcm_proc_info_read((struct snd_pcm_substream *)entry->private_data,
-			       buffer);
+	snd_pcm_proc_info_read(entry->private_data, buffer);
 }
 
 static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index e23e0e7..a1707cc 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -334,11 +334,15 @@
 		/* delta = "expected next hw_ptr" for in_interrupt != 0 */
 		delta = runtime->hw_ptr_interrupt + runtime->period_size;
 		if (delta > new_hw_ptr) {
-			hw_base += runtime->buffer_size;
-			if (hw_base >= runtime->boundary)
-				hw_base = 0;
-			new_hw_ptr = hw_base + pos;
-			goto __delta;
+			/* check for double acknowledged interrupts */
+			hdelta = jiffies - runtime->hw_ptr_jiffies;
+			if (hdelta > runtime->hw_ptr_buffer_jiffies/2) {
+				hw_base += runtime->buffer_size;
+				if (hw_base >= runtime->boundary)
+					hw_base = 0;
+				new_hw_ptr = hw_base + pos;
+				goto __delta;
+			}
 		}
 	}
 	/* new_hw_ptr might be lower than old_hw_ptr in case when */
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index d4eb2ef..8bc7cb3 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -142,7 +142,7 @@
 
 #ifdef RULES_DEBUG
 #define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v
-char *snd_pcm_hw_param_names[] = {
+static const char * const snd_pcm_hw_param_names[] = {
 	HW_PARAM(ACCESS),
 	HW_PARAM(FORMAT),
 	HW_PARAM(SUBFORMAT),
@@ -864,6 +864,8 @@
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	snd_pcm_trigger_tstamp(substream);
 	runtime->hw_ptr_jiffies = jiffies;
+	runtime->hw_ptr_buffer_jiffies = (runtime->buffer_size * HZ) / 
+							    runtime->rate;
 	runtime->status->state = state;
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
 	    runtime->silence_size > 0)
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 480c386..c896116 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -74,6 +74,25 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-dummy.
 
+config SND_ALOOP
+        tristate "Generic loopback driver (PCM)"
+        select SND_PCM
+        help
+          Say 'Y' or 'M' to include support for the PCM loopback device.
+	  This module returns played samples back to the user space using
+	  the standard ALSA PCM device. The devices are routed 0->1 and
+          1->0, where first number is the playback PCM device and second
+	  number is the capture device. Module creates two PCM devices and
+	  configured number of substreams (see the pcm_substreams module
+          parameter).
+
+	  The looback device allow time sychronization with an external
+	  timing source using the time shift universal control (+-20%
+	  of system time).
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-aloop.
+
 config SND_VIRMIDI
 	tristate "Virtual MIDI soundcard"
 	depends on SND_SEQUENCER
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index d4a07f9..1a8440c 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -4,6 +4,7 @@
 #
 
 snd-dummy-objs := dummy.o
+snd-aloop-objs := aloop.o
 snd-mtpav-objs := mtpav.o
 snd-mts64-objs := mts64.o
 snd-portman2x4-objs := portman2x4.o
@@ -13,6 +14,7 @@
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
+obj-$(CONFIG_SND_ALOOP) += snd-aloop.o
 obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
 obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
 obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
new file mode 100644
index 0000000..040030a
--- /dev/null
+++ b/sound/drivers/aloop.c
@@ -0,0 +1,1057 @@
+/*
+ *  Loopback soundcard
+ *
+ *  Original code:
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *
+ *  More accurate positioning and full-duplex support:
+ *  Copyright (c) Ahmet İnan <ainan at mathematik.uni-freiburg.de>
+ *
+ *  Major (almost complete) rewrite:
+ *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *  A next major update in 2010 (separate timers for playback and capture):
+ *  Copyright (c) Jaroslav Kysela <perex@perex.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
+MODULE_DESCRIPTION("A loopback soundcard");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}");
+
+#define MAX_PCM_SUBSTREAMS	8
+
+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] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
+static int pcm_notify[SNDRV_CARDS];
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for loopback soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for loopback soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable this loopback soundcard.");
+module_param_array(pcm_substreams, int, NULL, 0444);
+MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver.");
+module_param_array(pcm_notify, int, NULL, 0444);
+MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes.");
+
+#define NO_PITCH 100000
+
+struct loopback_pcm;
+
+struct loopback_cable {
+	spinlock_t lock;
+	struct loopback_pcm *streams[2];
+	struct snd_pcm_hardware hw;
+	/* flags */
+	unsigned int valid;
+	unsigned int running;
+};
+
+struct loopback_setup {
+	unsigned int notify: 1;
+	unsigned int rate_shift;
+	unsigned int format;
+	unsigned int rate;
+	unsigned int channels;
+	struct snd_ctl_elem_id active_id;
+	struct snd_ctl_elem_id format_id;
+	struct snd_ctl_elem_id rate_id;
+	struct snd_ctl_elem_id channels_id;
+};
+
+struct loopback {
+	struct snd_card *card;
+	struct mutex cable_lock;
+	struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2];
+	struct snd_pcm *pcm[2];
+	struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2];
+};
+
+struct loopback_pcm {
+	struct loopback *loopback;
+	struct snd_pcm_substream *substream;
+	struct loopback_cable *cable;
+	unsigned int pcm_buffer_size;
+	unsigned int buf_pos;	/* position in buffer */
+	unsigned int silent_size;
+	/* PCM parameters */
+	unsigned int pcm_period_size;
+	unsigned int pcm_bps;		/* bytes per second */
+	unsigned int pcm_salign;	/* bytes per sample * channels */
+	unsigned int pcm_rate_shift;	/* rate shift value */
+	/* flags */
+	unsigned int period_update_pending :1;
+	/* timer stuff */
+	unsigned int irq_pos;		/* fractional IRQ position */
+	unsigned int period_size_frac;
+	unsigned long last_jiffies;
+	struct timer_list timer;
+};
+
+static struct platform_device *devices[SNDRV_CARDS];
+
+static inline unsigned int byte_pos(struct loopback_pcm *dpcm, unsigned int x)
+{
+	if (dpcm->pcm_rate_shift == NO_PITCH) {
+		x /= HZ;
+	} else {
+		x = div_u64(NO_PITCH * (unsigned long long)x,
+			    HZ * (unsigned long long)dpcm->pcm_rate_shift);
+	}
+	return x - (x % dpcm->pcm_salign);
+}
+
+static inline unsigned int frac_pos(struct loopback_pcm *dpcm, unsigned int x)
+{
+	if (dpcm->pcm_rate_shift == NO_PITCH) {	/* no pitch */
+		return x * HZ;
+	} else {
+		x = div_u64(dpcm->pcm_rate_shift * (unsigned long long)x * HZ,
+			    NO_PITCH);
+	}
+	return x;
+}
+
+static inline struct loopback_setup *get_setup(struct loopback_pcm *dpcm)
+{
+	int device = dpcm->substream->pstr->pcm->device;
+	
+	if (dpcm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		device ^= 1;
+	return &dpcm->loopback->setup[dpcm->substream->number][device];
+}
+
+static inline unsigned int get_notify(struct loopback_pcm *dpcm)
+{
+	return get_setup(dpcm)->notify;
+}
+
+static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm)
+{
+	return get_setup(dpcm)->rate_shift;
+}
+
+static void loopback_timer_start(struct loopback_pcm *dpcm)
+{
+	unsigned long tick;
+	unsigned int rate_shift = get_rate_shift(dpcm);
+
+	if (rate_shift != dpcm->pcm_rate_shift) {
+		dpcm->pcm_rate_shift = rate_shift;
+		dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size);
+	}
+	tick = dpcm->period_size_frac - dpcm->irq_pos;
+	tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps;
+	dpcm->timer.expires = jiffies + tick;
+	add_timer(&dpcm->timer);
+}
+
+static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
+{
+	del_timer(&dpcm->timer);
+}
+
+#define CABLE_VALID_PLAYBACK	(1 << SNDRV_PCM_STREAM_PLAYBACK)
+#define CABLE_VALID_CAPTURE	(1 << SNDRV_PCM_STREAM_CAPTURE)
+#define CABLE_VALID_BOTH	(CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE)
+
+static int loopback_check_format(struct loopback_cable *cable, int stream)
+{
+	struct snd_pcm_runtime *runtime;
+	struct loopback_setup *setup;
+	struct snd_card *card;
+	int check;
+
+	if (cable->valid != CABLE_VALID_BOTH) {
+		if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+			goto __notify;
+		return 0;
+	}
+	runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->
+							substream->runtime;
+	check = cable->hw.formats != (1ULL << runtime->format) ||
+		cable->hw.rate_min != runtime->rate ||
+		cable->hw.rate_max != runtime->rate ||
+		cable->hw.channels_min != runtime->channels ||
+		cable->hw.channels_max != runtime->channels;
+	if (!check)
+		return 0;
+	if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+		return -EIO;
+	} else {
+		snd_pcm_stop(cable->streams[SNDRV_PCM_STREAM_CAPTURE]->
+					substream, SNDRV_PCM_STATE_DRAINING);
+	      __notify:
+		runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->
+							substream->runtime;
+		setup = get_setup(cable->streams[SNDRV_PCM_STREAM_PLAYBACK]);
+		card = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->loopback->card;
+		if (setup->format != runtime->format) {
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+							&setup->format_id);
+			setup->format = runtime->format;
+		}
+		if (setup->rate != runtime->rate) {
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+							&setup->rate_id);
+			setup->rate = runtime->rate;
+		}
+		if (setup->channels != runtime->channels) {
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+							&setup->channels_id);
+			setup->channels = runtime->channels;
+		}
+	}
+	return 0;
+}
+
+static void loopback_active_notify(struct loopback_pcm *dpcm)
+{
+	snd_ctl_notify(dpcm->loopback->card,
+		       SNDRV_CTL_EVENT_MASK_VALUE,
+		       &get_setup(dpcm)->active_id);
+}
+
+static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct loopback_pcm *dpcm = runtime->private_data;
+	struct loopback_cable *cable = dpcm->cable;
+	int err;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		err = loopback_check_format(cable, substream->stream);
+		if (err < 0)
+			return err;
+		dpcm->last_jiffies = jiffies;
+		dpcm->pcm_rate_shift = 0;
+		loopback_timer_start(dpcm);
+		cable->running |= (1 << substream->stream);
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			loopback_active_notify(dpcm);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		cable->running &= ~(1 << substream->stream);
+		loopback_timer_stop(dpcm);
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			loopback_active_notify(dpcm);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int loopback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct loopback_pcm *dpcm = runtime->private_data;
+	struct loopback_cable *cable = dpcm->cable;
+	unsigned int bps, salign;
+
+	salign = (snd_pcm_format_width(runtime->format) *
+						runtime->channels) / 8;
+	bps = salign * runtime->rate;
+	if (bps <= 0 || salign <= 0)
+		return -EINVAL;
+
+	dpcm->buf_pos = 0;
+	dpcm->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size);
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		/* clear capture buffer */
+		dpcm->silent_size = dpcm->pcm_buffer_size;
+		snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
+					   runtime->buffer_size * runtime->channels);
+	}
+
+	dpcm->irq_pos = 0;
+	dpcm->period_update_pending = 0;
+	dpcm->pcm_bps = bps;
+	dpcm->pcm_salign = salign;
+	dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size);
+
+	mutex_lock(&dpcm->loopback->cable_lock);
+	if (!(cable->valid & ~(1 << substream->stream))) {
+		cable->hw.formats = (1ULL << runtime->format);
+		cable->hw.rate_min = runtime->rate;
+		cable->hw.rate_max = runtime->rate;
+		cable->hw.channels_min = runtime->channels;
+		cable->hw.channels_max = runtime->channels;
+	}
+	cable->valid |= 1 << substream->stream;
+	mutex_unlock(&dpcm->loopback->cable_lock);
+
+	return 0;
+}
+
+static void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes)
+{
+	struct snd_pcm_runtime *runtime = dpcm->substream->runtime;
+	char *dst = runtime->dma_area;
+	unsigned int dst_off = dpcm->buf_pos;
+
+	if (dpcm->silent_size >= dpcm->pcm_buffer_size)
+		return;
+	if (dpcm->silent_size + bytes > dpcm->pcm_buffer_size)
+		bytes = dpcm->pcm_buffer_size - dpcm->silent_size;
+
+	for (;;) {
+		unsigned int size = bytes;
+		if (dst_off + size > dpcm->pcm_buffer_size)
+			size = dpcm->pcm_buffer_size - dst_off;
+		snd_pcm_format_set_silence(runtime->format, dst + dst_off,
+					   bytes_to_frames(runtime, size) *
+					   	runtime->channels);
+		dpcm->silent_size += size;
+		bytes -= size;
+		if (!bytes)
+			break;
+		dst_off = 0;
+	}
+}
+
+static void copy_play_buf(struct loopback_pcm *play,
+			  struct loopback_pcm *capt,
+			  unsigned int bytes)
+{
+	struct snd_pcm_runtime *runtime = play->substream->runtime;
+	char *src = runtime->dma_area;
+	char *dst = capt->substream->runtime->dma_area;
+	unsigned int src_off = play->buf_pos;
+	unsigned int dst_off = capt->buf_pos;
+	unsigned int clear_bytes = 0;
+
+	/* check if playback is draining, trim the capture copy size
+	 * when our pointer is at the end of playback ring buffer */
+	if (runtime->status->state == SNDRV_PCM_STATE_DRAINING &&
+	    snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) { 
+	    	snd_pcm_uframes_t appl_ptr, appl_ptr1, diff;
+		appl_ptr = appl_ptr1 = runtime->control->appl_ptr;
+		appl_ptr1 -= appl_ptr1 % runtime->buffer_size;
+		appl_ptr1 += play->buf_pos / play->pcm_salign;
+		if (appl_ptr < appl_ptr1)
+			appl_ptr1 -= runtime->buffer_size;
+		diff = (appl_ptr - appl_ptr1) * play->pcm_salign;
+		if (diff < bytes) {
+			clear_bytes = bytes - diff;
+			bytes = diff;
+		}
+	}
+
+	for (;;) {
+		unsigned int size = bytes;
+		if (src_off + size > play->pcm_buffer_size)
+			size = play->pcm_buffer_size - src_off;
+		if (dst_off + size > capt->pcm_buffer_size)
+			size = capt->pcm_buffer_size - dst_off;
+		memcpy(dst + dst_off, src + src_off, size);
+		capt->silent_size = 0;
+		bytes -= size;
+		if (!bytes)
+			break;
+		src_off = (src_off + size) % play->pcm_buffer_size;
+		dst_off = (dst_off + size) % capt->pcm_buffer_size;
+	}
+
+	if (clear_bytes > 0) {
+		clear_capture_buf(capt, clear_bytes);
+		capt->silent_size = 0;
+	}
+}
+
+#define BYTEPOS_UPDATE_POSONLY	0
+#define BYTEPOS_UPDATE_CLEAR	1
+#define BYTEPOS_UPDATE_COPY	2
+
+static void loopback_bytepos_update(struct loopback_pcm *dpcm,
+				    unsigned int delta,
+				    unsigned int cmd)
+{
+	unsigned int count;
+	unsigned long last_pos;
+
+	last_pos = byte_pos(dpcm, dpcm->irq_pos);
+	dpcm->irq_pos += delta * dpcm->pcm_bps;
+	count = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
+	if (!count)
+		return;
+	if (cmd == BYTEPOS_UPDATE_CLEAR)
+		clear_capture_buf(dpcm, count);
+	else if (cmd == BYTEPOS_UPDATE_COPY)
+		copy_play_buf(dpcm->cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
+			      dpcm->cable->streams[SNDRV_PCM_STREAM_CAPTURE],
+			      count);
+	dpcm->buf_pos += count;
+	dpcm->buf_pos %= dpcm->pcm_buffer_size;
+	if (dpcm->irq_pos >= dpcm->period_size_frac) {
+		dpcm->irq_pos %= dpcm->period_size_frac;
+		dpcm->period_update_pending = 1;
+	}
+}
+
+static void loopback_pos_update(struct loopback_cable *cable)
+{
+	struct loopback_pcm *dpcm_play =
+			cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
+	struct loopback_pcm *dpcm_capt =
+			cable->streams[SNDRV_PCM_STREAM_CAPTURE];
+	unsigned long delta_play = 0, delta_capt = 0;
+
+	spin_lock(&cable->lock);	
+	if (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
+		delta_play = jiffies - dpcm_play->last_jiffies;
+		dpcm_play->last_jiffies += delta_play;
+	}
+
+	if (cable->running & (1 << SNDRV_PCM_STREAM_CAPTURE)) {
+		delta_capt = jiffies - dpcm_capt->last_jiffies;
+		dpcm_capt->last_jiffies += delta_capt;
+	}
+
+	if (delta_play == 0 && delta_capt == 0) {
+		spin_unlock(&cable->lock);
+		return;
+	}
+		
+	if (delta_play > delta_capt) {
+		loopback_bytepos_update(dpcm_play, delta_play - delta_capt,
+					BYTEPOS_UPDATE_POSONLY);
+		delta_play = delta_capt;
+	} else if (delta_play < delta_capt) {
+		loopback_bytepos_update(dpcm_capt, delta_capt - delta_play,
+					BYTEPOS_UPDATE_CLEAR);
+		delta_capt = delta_play;
+	}
+
+	if (delta_play == 0 && delta_capt == 0) {
+		spin_unlock(&cable->lock);
+		return;
+	}
+	/* note delta_capt == delta_play at this moment */
+	loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY);
+	loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY);
+	spin_unlock(&cable->lock);
+}
+
+static void loopback_timer_function(unsigned long data)
+{
+	struct loopback_pcm *dpcm = (struct loopback_pcm *)data;
+	int stream;
+
+	loopback_pos_update(dpcm->cable);
+	stream = dpcm->substream->stream;
+	if (dpcm->cable->running & (1 << stream))
+		loopback_timer_start(dpcm);
+	if (dpcm->period_update_pending) {
+		dpcm->period_update_pending = 0;
+		if (dpcm->cable->running & (1 << stream))
+			snd_pcm_period_elapsed(dpcm->substream);
+	}
+}
+
+static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct loopback_pcm *dpcm = runtime->private_data;
+
+	loopback_pos_update(dpcm->cable);
+	return bytes_to_frames(runtime, dpcm->buf_pos);
+}
+
+static struct snd_pcm_hardware loopback_pcm_hardware =
+{
+	.info =		(SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
+			 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+			 SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |
+			 SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE),
+	.rates =	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,
+	.rate_min =		8000,
+	.rate_max =		192000,
+	.channels_min =		1,
+	.channels_max =		32,
+	.buffer_bytes_max =	2 * 1024 * 1024,
+	.period_bytes_min =	64,
+	.period_bytes_max =	2 * 1024 * 1024,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+static void loopback_runtime_free(struct snd_pcm_runtime *runtime)
+{
+	struct loopback_pcm *dpcm = runtime->private_data;
+	kfree(dpcm);
+}
+
+static int loopback_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+}
+
+static int loopback_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct loopback_pcm *dpcm = runtime->private_data;
+	struct loopback_cable *cable = dpcm->cable;
+
+	mutex_lock(&dpcm->loopback->cable_lock);
+	cable->valid &= ~(1 << substream->stream);
+	mutex_unlock(&dpcm->loopback->cable_lock);
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static unsigned int get_cable_index(struct snd_pcm_substream *substream)
+{
+	if (!substream->pcm->device)
+		return substream->stream;
+	else
+		return !substream->stream;
+}
+
+static int loopback_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct loopback *loopback = substream->private_data;
+	struct loopback_pcm *dpcm;
+	struct loopback_cable *cable;
+	int err = 0;
+	int dev = get_cable_index(substream);
+
+	mutex_lock(&loopback->cable_lock);
+	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
+	if (!dpcm) {
+		err = -ENOMEM;
+		goto unlock;
+	}
+	dpcm->loopback = loopback;
+	dpcm->substream = substream;
+	setup_timer(&dpcm->timer, loopback_timer_function,
+		    (unsigned long)dpcm);
+
+	cable = loopback->cables[substream->number][dev];
+	if (!cable) {
+		cable = kzalloc(sizeof(*cable), GFP_KERNEL);
+		if (!cable) {
+			kfree(dpcm);
+			err = -ENOMEM;
+			goto unlock;
+		}
+		spin_lock_init(&cable->lock);
+		cable->hw = loopback_pcm_hardware;
+		loopback->cables[substream->number][dev] = cable;
+	}
+	dpcm->cable = cable;
+	cable->streams[substream->stream] = dpcm;
+
+	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+	runtime->private_data = dpcm;
+	runtime->private_free = loopback_runtime_free;
+	if (get_notify(dpcm) &&
+	    substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		runtime->hw = loopback_pcm_hardware;
+	} else {
+		runtime->hw = cable->hw;
+	}
+ unlock:
+	mutex_unlock(&loopback->cable_lock);
+	return err;
+}
+
+static int loopback_close(struct snd_pcm_substream *substream)
+{
+	struct loopback *loopback = substream->private_data;
+	struct loopback_pcm *dpcm = substream->runtime->private_data;
+	struct loopback_cable *cable;
+	int dev = get_cable_index(substream);
+
+	loopback_timer_stop(dpcm);
+	mutex_lock(&loopback->cable_lock);
+	cable = loopback->cables[substream->number][dev];
+	if (cable->streams[!substream->stream]) {
+		/* other stream is still alive */
+		cable->streams[substream->stream] = NULL;
+	} else {
+		/* free the cable */
+		loopback->cables[substream->number][dev] = NULL;
+		kfree(cable);
+	}
+	mutex_unlock(&loopback->cable_lock);
+	return 0;
+}
+
+static struct snd_pcm_ops loopback_playback_ops = {
+	.open =		loopback_open,
+	.close =	loopback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	loopback_hw_params,
+	.hw_free =	loopback_hw_free,
+	.prepare =	loopback_prepare,
+	.trigger =	loopback_trigger,
+	.pointer =	loopback_pointer,
+};
+
+static struct snd_pcm_ops loopback_capture_ops = {
+	.open =		loopback_open,
+	.close =	loopback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	loopback_hw_params,
+	.hw_free =	loopback_hw_free,
+	.prepare =	loopback_prepare,
+	.trigger =	loopback_trigger,
+	.pointer =	loopback_pointer,
+};
+
+static int __devinit loopback_pcm_new(struct loopback *loopback,
+				      int device, int substreams)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	err = snd_pcm_new(loopback->card, "Loopback PCM", device,
+			  substreams, substreams, &pcm);
+	if (err < 0)
+		return err;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_capture_ops);
+
+	pcm->private_data = loopback;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "Loopback PCM");
+
+	loopback->pcm[device] = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+			snd_dma_continuous_data(GFP_KERNEL),
+			0, 2 * 1024 * 1024);
+	return 0;
+}
+
+static int loopback_rate_shift_info(struct snd_kcontrol *kcontrol,   
+				    struct snd_ctl_elem_info *uinfo) 
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 80000;
+	uinfo->value.integer.max = 120000;
+	uinfo->value.integer.step = 1;
+	return 0;
+}                                  
+
+static int loopback_rate_shift_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] =
+		loopback->setup[kcontrol->id.subdevice]
+			       [kcontrol->id.device].rate_shift;
+	return 0;
+}
+
+static int loopback_rate_shift_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	int change = 0;
+
+	val = ucontrol->value.integer.value[0];
+	if (val < 80000)
+		val = 80000;
+	if (val > 120000)
+		val = 120000;	
+	mutex_lock(&loopback->cable_lock);
+	if (val != loopback->setup[kcontrol->id.subdevice]
+				  [kcontrol->id.device].rate_shift) {
+		loopback->setup[kcontrol->id.subdevice]
+			       [kcontrol->id.device].rate_shift = val;
+		change = 1;
+	}
+	mutex_unlock(&loopback->cable_lock);
+	return change;
+}
+
+static int loopback_notify_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] =
+		loopback->setup[kcontrol->id.subdevice]
+			       [kcontrol->id.device].notify;
+	return 0;
+}
+
+static int loopback_notify_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	int change = 0;
+
+	val = ucontrol->value.integer.value[0] ? 1 : 0;
+	if (val != loopback->setup[kcontrol->id.subdevice]
+				[kcontrol->id.device].notify) {
+		loopback->setup[kcontrol->id.subdevice]
+			[kcontrol->id.device].notify = val;
+		change = 1;
+	}
+	return change;
+}
+
+static int loopback_active_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+	struct loopback_cable *cable = loopback->cables
+			[kcontrol->id.subdevice][kcontrol->id.device ^ 1];
+	unsigned int val = 0;
+
+	if (cable != NULL)
+		val = (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
+									1 : 0;
+	ucontrol->value.integer.value[0] = val;
+	return 0;
+}
+
+static int loopback_format_info(struct snd_kcontrol *kcontrol,   
+				struct snd_ctl_elem_info *uinfo) 
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = SNDRV_PCM_FORMAT_LAST;
+	uinfo->value.integer.step = 1;
+	return 0;
+}                                  
+
+static int loopback_format_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] =
+		loopback->setup[kcontrol->id.subdevice]
+			       [kcontrol->id.device].format;
+	return 0;
+}
+
+static int loopback_rate_info(struct snd_kcontrol *kcontrol,   
+			      struct snd_ctl_elem_info *uinfo) 
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 192000;
+	uinfo->value.integer.step = 1;
+	return 0;
+}                                  
+
+static int loopback_rate_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] =
+		loopback->setup[kcontrol->id.subdevice]
+			       [kcontrol->id.device].rate;
+	return 0;
+}
+
+static int loopback_channels_info(struct snd_kcontrol *kcontrol,   
+				  struct snd_ctl_elem_info *uinfo) 
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 1;
+	uinfo->value.integer.max = 1024;
+	uinfo->value.integer.step = 1;
+	return 0;
+}                                  
+
+static int loopback_channels_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] =
+		loopback->setup[kcontrol->id.subdevice]
+			       [kcontrol->id.device].channels;
+	return 0;
+}
+
+static struct snd_kcontrol_new loopback_controls[]  __devinitdata = {
+{
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "PCM Rate Shift 100000",
+	.info =         loopback_rate_shift_info,
+	.get =          loopback_rate_shift_get,
+	.put =          loopback_rate_shift_put,
+},
+{
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "PCM Notify",
+	.info =         snd_ctl_boolean_mono_info,
+	.get =          loopback_notify_get,
+	.put =          loopback_notify_put,
+},
+#define ACTIVE_IDX 2
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "PCM Slave Active",
+	.info =         snd_ctl_boolean_mono_info,
+	.get =          loopback_active_get,
+},
+#define FORMAT_IDX 3
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "PCM Slave Format",
+	.info =         loopback_format_info,
+	.get =          loopback_format_get
+},
+#define RATE_IDX 4
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "PCM Slave Rate",
+	.info =         loopback_rate_info,
+	.get =          loopback_rate_get
+},
+#define CHANNELS_IDX 5
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "PCM Slave Channels",
+	.info =         loopback_channels_info,
+	.get =          loopback_channels_get
+}
+};
+
+static int __devinit loopback_mixer_new(struct loopback *loopback, int notify)
+{
+	struct snd_card *card = loopback->card;
+	struct snd_pcm *pcm;
+	struct snd_kcontrol *kctl;
+	struct loopback_setup *setup;
+	int err, dev, substr, substr_count, idx;
+
+	strcpy(card->mixername, "Loopback Mixer");
+	for (dev = 0; dev < 2; dev++) {
+		pcm = loopback->pcm[dev];
+		substr_count =
+		    pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count;
+		for (substr = 0; substr < substr_count; substr++) {
+			setup = &loopback->setup[substr][dev];
+			setup->notify = notify;
+			setup->rate_shift = NO_PITCH;
+			setup->format = SNDRV_PCM_FORMAT_S16_LE;
+			setup->rate = 48000;
+			setup->channels = 2;
+			for (idx = 0; idx < ARRAY_SIZE(loopback_controls);
+									idx++) {
+				kctl = snd_ctl_new1(&loopback_controls[idx],
+						    loopback);
+				if (!kctl)
+					return -ENOMEM;
+				kctl->id.device = dev;
+				kctl->id.subdevice = substr;
+				switch (idx) {
+				case ACTIVE_IDX:
+					setup->active_id = kctl->id;
+					break;
+				case FORMAT_IDX:
+					setup->format_id = kctl->id;
+					break;
+				case RATE_IDX:
+					setup->rate_id = kctl->id;
+					break;
+				case CHANNELS_IDX:
+					setup->channels_id = kctl->id;
+					break;
+				default:
+					break;
+				}
+				err = snd_ctl_add(card, kctl);
+				if (err < 0)
+					return err;
+			}
+		}
+	}
+	return 0;
+}
+
+static int __devinit loopback_probe(struct platform_device *devptr)
+{
+	struct snd_card *card;
+	struct loopback *loopback;
+	int dev = devptr->id;
+	int err;
+
+	err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+			      sizeof(struct loopback), &card);
+	if (err < 0)
+		return err;
+	loopback = card->private_data;
+
+	if (pcm_substreams[dev] < 1)
+		pcm_substreams[dev] = 1;
+	if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS)
+		pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
+	
+	loopback->card = card;
+	mutex_init(&loopback->cable_lock);
+
+	err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]);
+	if (err < 0)
+		goto __nodev;
+	err = loopback_pcm_new(loopback, 1, pcm_substreams[dev]);
+	if (err < 0)
+		goto __nodev;
+	err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0);
+	if (err < 0)
+		goto __nodev;
+	strcpy(card->driver, "Loopback");
+	strcpy(card->shortname, "Loopback");
+	sprintf(card->longname, "Loopback %i", dev + 1);
+	err = snd_card_register(card);
+	if (!err) {
+		platform_set_drvdata(devptr, card);
+		return 0;
+	}
+      __nodev:
+	snd_card_free(card);
+	return err;
+}
+
+static int __devexit loopback_remove(struct platform_device *devptr)
+{
+	snd_card_free(platform_get_drvdata(devptr));
+	platform_set_drvdata(devptr, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int loopback_suspend(struct platform_device *pdev,
+				pm_message_t state)
+{
+	struct snd_card *card = platform_get_drvdata(pdev);
+	struct loopback *loopback = card->private_data;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+	snd_pcm_suspend_all(loopback->pcm[0]);
+	snd_pcm_suspend_all(loopback->pcm[1]);
+	return 0;
+}
+	
+static int loopback_resume(struct platform_device *pdev)
+{
+	struct snd_card *card = platform_get_drvdata(pdev);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	return 0;
+}
+#endif
+
+#define SND_LOOPBACK_DRIVER	"snd_aloop"
+
+static struct platform_driver loopback_driver = {
+	.probe		= loopback_probe,
+	.remove		= __devexit_p(loopback_remove),
+#ifdef CONFIG_PM
+	.suspend	= loopback_suspend,
+	.resume		= loopback_resume,
+#endif
+	.driver		= {
+		.name	= SND_LOOPBACK_DRIVER
+	},
+};
+
+static void loopback_unregister_all(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(devices); ++i)
+		platform_device_unregister(devices[i]);
+	platform_driver_unregister(&loopback_driver);
+}
+
+static int __init alsa_card_loopback_init(void)
+{
+	int i, err, cards;
+
+	err = platform_driver_register(&loopback_driver);
+	if (err < 0)
+		return err;
+
+
+	cards = 0;
+	for (i = 0; i < SNDRV_CARDS; i++) {
+		struct platform_device *device;
+		if (!enable[i])
+			continue;
+		device = platform_device_register_simple(SND_LOOPBACK_DRIVER,
+							 i, NULL, 0);
+		if (IS_ERR(device))
+			continue;
+		if (!platform_get_drvdata(device)) {
+			platform_device_unregister(device);
+			continue;
+		}
+		devices[i] = device;
+		cards++;
+	}
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "aloop: No loopback enabled\n");
+#endif
+		loopback_unregister_all();
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_loopback_exit(void)
+{
+	loopback_unregister_all();
+}
+
+module_init(alsa_card_loopback_init)
+module_exit(alsa_card_loopback_exit)
diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c
index 0e631c3..f4cd493 100644
--- a/sound/drivers/virmidi.c
+++ b/sound/drivers/virmidi.c
@@ -94,7 +94,7 @@
 			      sizeof(struct snd_card_virmidi), &card);
 	if (err < 0)
 		return err;
-	vmidi = (struct snd_card_virmidi *)card->private_data;
+	vmidi = card->private_data;
 	vmidi->card = card;
 
 	if (midi_devs[dev] > MAX_MIDI_DEVICES) {
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index 42d7844..57ccba8 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -878,7 +878,7 @@
 static void proc_regs_read(struct snd_info_entry *entry,
 		struct snd_info_buffer *buffer)
 {
-	struct snd_akm4xxx *ak = (struct snd_akm4xxx *)entry->private_data;
+	struct snd_akm4xxx *ak = entry->private_data;
 	int reg, val, chip;
 	for (chip = 0; chip < ak->num_chips; chip++) {
 		for (reg = 0; reg < ak->total_regs; reg++) {
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index c6990c6..52064cf 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -77,6 +77,32 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-als100.
 
+config SND_AZT1605
+	tristate "Aztech AZT1605 Driver"
+	depends on SND
+	select SND_WSS_LIB
+	select SND_MPU401_UART
+	select SND_OPL3_LIB
+	help
+	  Say Y here to include support for Aztech Sound Galaxy cards
+	  based on the AZT1605 chipset.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-azt1605.
+
+config SND_AZT2316
+	tristate "Aztech AZT2316 Driver"
+	depends on SND
+	select SND_WSS_LIB
+	select SND_MPU401_UART
+	select SND_OPL3_LIB
+	help
+	  Say Y here to include support for Aztech Sound Galaxy cards
+	  based on the AZT2316 chipset.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-azt2316.
+
 config SND_AZT2320
 	tristate "Aztech Systems AZT2320"
 	depends on PNP
@@ -351,16 +377,6 @@
 	  coprocessor can do variable tasks like various compression and
 	  decompression algorithms.
 
-config SND_SGALAXY
-	tristate "Aztech Sound Galaxy"
-	select SND_WSS_LIB
-	help
-	  Say Y here to include support for Aztech Sound Galaxy
-	  soundcards.
-
-	  To compile this driver as a module, choose M here: the module
-	  will be called snd-sgalaxy.
-
 config SND_SSCAPE
 	tristate "Ensoniq SoundScape driver"
 	select SND_MPU401_UART
diff --git a/sound/isa/Makefile b/sound/isa/Makefile
index c73d30c..8d781e4 100644
--- a/sound/isa/Makefile
+++ b/sound/isa/Makefile
@@ -10,7 +10,6 @@
 snd-es18xx-objs := es18xx.o
 snd-opl3sa2-objs := opl3sa2.o
 snd-sc6000-objs := sc6000.o
-snd-sgalaxy-objs := sgalaxy.o
 snd-sscape-objs := sscape.o
 
 # Toplevel Module Dependency
@@ -21,8 +20,7 @@
 obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o
 obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o
 obj-$(CONFIG_SND_SC6000) += snd-sc6000.o
-obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o
 obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o
 
-obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ msnd/ opti9xx/ \
+obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ galaxy/ gus/ msnd/ opti9xx/ \
 		     sb/ wavefront/ wss/
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c
index bbcbf92..3cb75bc 100644
--- a/sound/isa/ad1816a/ad1816a.c
+++ b/sound/isa/ad1816a/ad1816a.c
@@ -162,7 +162,7 @@
 				sizeof(struct snd_card_ad1816a), &card);
 	if (error < 0)
 		return error;
-	acard = (struct snd_card_ad1816a *)card->private_data;
+	acard = card->private_data;
 
 	if ((error = snd_card_ad1816a_pnp(dev, acard, pcard, pid))) {
 		snd_card_free(card);
diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c
index f7aa637..aac8dc1 100644
--- a/sound/isa/azt2320.c
+++ b/sound/isa/azt2320.c
@@ -188,7 +188,7 @@
 				sizeof(struct snd_card_azt2320), &card);
 	if (error < 0)
 		return error;
-	acard = (struct snd_card_azt2320 *)card->private_data;
+	acard = card->private_data;
 
 	if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
 		snd_card_free(card);
diff --git a/sound/isa/galaxy/Makefile b/sound/isa/galaxy/Makefile
new file mode 100644
index 0000000..e307066
--- /dev/null
+++ b/sound/isa/galaxy/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-azt1605-objs := azt1605.o
+snd-azt2316-objs := azt2316.o
+
+obj-$(CONFIG_SND_AZT1605) += snd-azt1605.o
+obj-$(CONFIG_SND_AZT2316) += snd-azt2316.o
diff --git a/sound/isa/galaxy/azt1605.c b/sound/isa/galaxy/azt1605.c
new file mode 100644
index 0000000..9a97643
--- /dev/null
+++ b/sound/isa/galaxy/azt1605.c
@@ -0,0 +1,91 @@
+/*
+ * Aztech AZT1605 Driver
+ * Copyright (C) 2007,2010  Rene Herman
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define AZT1605
+
+#define CRD_NAME "Aztech AZT1605"
+#define DRV_NAME "AZT1605"
+#define DEV_NAME "azt1605"
+
+#define GALAXY_DSP_MAJOR		2
+#define GALAXY_DSP_MINOR		1
+
+#define GALAXY_CONFIG_SIZE		3
+
+/*
+ * 24-bit config register
+ */
+
+#define GALAXY_CONFIG_SBA_220		(0 << 0)
+#define GALAXY_CONFIG_SBA_240		(1 << 0)
+#define GALAXY_CONFIG_SBA_260		(2 << 0)
+#define GALAXY_CONFIG_SBA_280		(3 << 0)
+#define GALAXY_CONFIG_SBA_MASK		GALAXY_CONFIG_SBA_280
+
+#define GALAXY_CONFIG_MPUA_300		(0 << 2)
+#define GALAXY_CONFIG_MPUA_330		(1 << 2)
+
+#define GALAXY_CONFIG_MPU_ENABLE	(1 << 3)
+
+#define GALAXY_CONFIG_GAME_ENABLE	(1 << 4)
+
+#define GALAXY_CONFIG_CD_PANASONIC	(1 << 5)
+#define GALAXY_CONFIG_CD_MITSUMI	(1 << 6)
+#define GALAXY_CONFIG_CD_MASK		(\
+	GALAXY_CONFIG_CD_PANASONIC | GALAXY_CONFIG_CD_MITSUMI)
+
+#define GALAXY_CONFIG_UNUSED		(1 << 7)
+#define GALAXY_CONFIG_UNUSED_MASK	GALAXY_CONFIG_UNUSED
+
+#define GALAXY_CONFIG_SBIRQ_2		(1 << 8)
+#define GALAXY_CONFIG_SBIRQ_3		(1 << 9)
+#define GALAXY_CONFIG_SBIRQ_5		(1 << 10)
+#define GALAXY_CONFIG_SBIRQ_7		(1 << 11)
+
+#define GALAXY_CONFIG_MPUIRQ_2		(1 << 12)
+#define GALAXY_CONFIG_MPUIRQ_3		(1 << 13)
+#define GALAXY_CONFIG_MPUIRQ_5		(1 << 14)
+#define GALAXY_CONFIG_MPUIRQ_7		(1 << 15)
+
+#define GALAXY_CONFIG_WSSA_530		(0 << 16)
+#define GALAXY_CONFIG_WSSA_604		(1 << 16)
+#define GALAXY_CONFIG_WSSA_E80		(2 << 16)
+#define GALAXY_CONFIG_WSSA_F40		(3 << 16)
+
+#define GALAXY_CONFIG_WSS_ENABLE	(1 << 18)
+
+#define GALAXY_CONFIG_CDIRQ_11		(1 << 19)
+#define GALAXY_CONFIG_CDIRQ_12		(1 << 20)
+#define GALAXY_CONFIG_CDIRQ_15		(1 << 21)
+#define GALAXY_CONFIG_CDIRQ_MASK	(\
+	GALAXY_CONFIG_CDIRQ_11 | GALAXY_CONFIG_CDIRQ_12 |\
+	GALAXY_CONFIG_CDIRQ_15)
+
+#define GALAXY_CONFIG_CDDMA_DISABLE	(0 << 22)
+#define GALAXY_CONFIG_CDDMA_0		(1 << 22)
+#define GALAXY_CONFIG_CDDMA_1		(2 << 22)
+#define GALAXY_CONFIG_CDDMA_3		(3 << 22)
+#define GALAXY_CONFIG_CDDMA_MASK	GALAXY_CONFIG_CDDMA_3
+
+#define GALAXY_CONFIG_MASK		(\
+	GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CD_MASK |\
+	GALAXY_CONFIG_UNUSED_MASK | GALAXY_CONFIG_CDIRQ_MASK |\
+	GALAXY_CONFIG_CDDMA_MASK)
+
+#include "galaxy.c"
diff --git a/sound/isa/galaxy/azt2316.c b/sound/isa/galaxy/azt2316.c
new file mode 100644
index 0000000..1894411
--- /dev/null
+++ b/sound/isa/galaxy/azt2316.c
@@ -0,0 +1,111 @@
+/*
+ * Aztech AZT2316 Driver
+ * Copyright (C) 2007,2010  Rene Herman
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define AZT2316
+
+#define CRD_NAME "Aztech AZT2316"
+#define DRV_NAME "AZT2316"
+#define DEV_NAME "azt2316"
+
+#define GALAXY_DSP_MAJOR		3
+#define GALAXY_DSP_MINOR		1
+
+#define GALAXY_CONFIG_SIZE		4
+
+/*
+ * 32-bit config register
+ */
+
+#define GALAXY_CONFIG_SBA_220		(0 << 0)
+#define GALAXY_CONFIG_SBA_240		(1 << 0)
+#define GALAXY_CONFIG_SBA_260		(2 << 0)
+#define GALAXY_CONFIG_SBA_280		(3 << 0)
+#define GALAXY_CONFIG_SBA_MASK		GALAXY_CONFIG_SBA_280
+
+#define GALAXY_CONFIG_SBIRQ_2		(1 << 2)
+#define GALAXY_CONFIG_SBIRQ_5		(1 << 3)
+#define GALAXY_CONFIG_SBIRQ_7		(1 << 4)
+#define GALAXY_CONFIG_SBIRQ_10		(1 << 5)
+
+#define GALAXY_CONFIG_SBDMA_DISABLE	(0 << 6)
+#define GALAXY_CONFIG_SBDMA_0		(1 << 6)
+#define GALAXY_CONFIG_SBDMA_1		(2 << 6)
+#define GALAXY_CONFIG_SBDMA_3		(3 << 6)
+
+#define GALAXY_CONFIG_WSSA_530		(0 << 8)
+#define GALAXY_CONFIG_WSSA_604		(1 << 8)
+#define GALAXY_CONFIG_WSSA_E80		(2 << 8)
+#define GALAXY_CONFIG_WSSA_F40		(3 << 8)
+
+#define GALAXY_CONFIG_WSS_ENABLE	(1 << 10)
+
+#define GALAXY_CONFIG_GAME_ENABLE	(1 << 11)
+
+#define GALAXY_CONFIG_MPUA_300		(0 << 12)
+#define GALAXY_CONFIG_MPUA_330		(1 << 12)
+
+#define GALAXY_CONFIG_MPU_ENABLE	(1 << 13)
+
+#define GALAXY_CONFIG_CDA_310		(0 << 14)
+#define GALAXY_CONFIG_CDA_320		(1 << 14)
+#define GALAXY_CONFIG_CDA_340		(2 << 14)
+#define GALAXY_CONFIG_CDA_350		(3 << 14)
+#define GALAXY_CONFIG_CDA_MASK		GALAXY_CONFIG_CDA_350
+
+#define GALAXY_CONFIG_CD_DISABLE	(0 << 16)
+#define GALAXY_CONFIG_CD_PANASONIC	(1 << 16)
+#define GALAXY_CONFIG_CD_SONY		(2 << 16)
+#define GALAXY_CONFIG_CD_MITSUMI	(3 << 16)
+#define GALAXY_CONFIG_CD_AZTECH		(4 << 16)
+#define GALAXY_CONFIG_CD_UNUSED_5	(5 << 16)
+#define GALAXY_CONFIG_CD_UNUSED_6	(6 << 16)
+#define GALAXY_CONFIG_CD_UNUSED_7	(7 << 16)
+#define GALAXY_CONFIG_CD_MASK		GALAXY_CONFIG_CD_UNUSED_7
+
+#define GALAXY_CONFIG_CDDMA8_DISABLE	(0 << 20)
+#define GALAXY_CONFIG_CDDMA8_0		(1 << 20)
+#define GALAXY_CONFIG_CDDMA8_1		(2 << 20)
+#define GALAXY_CONFIG_CDDMA8_3		(3 << 20)
+#define GALAXY_CONFIG_CDDMA8_MASK	GALAXY_CONFIG_CDDMA8_3
+
+#define GALAXY_CONFIG_CDDMA16_DISABLE	(0 << 22)
+#define GALAXY_CONFIG_CDDMA16_5		(1 << 22)
+#define GALAXY_CONFIG_CDDMA16_6		(2 << 22)
+#define GALAXY_CONFIG_CDDMA16_7		(3 << 22)
+#define GALAXY_CONFIG_CDDMA16_MASK	GALAXY_CONFIG_CDDMA16_7
+
+#define GALAXY_CONFIG_MPUIRQ_2		(1 << 24)
+#define GALAXY_CONFIG_MPUIRQ_5		(1 << 25)
+#define GALAXY_CONFIG_MPUIRQ_7		(1 << 26)
+#define GALAXY_CONFIG_MPUIRQ_10		(1 << 27)
+
+#define GALAXY_CONFIG_CDIRQ_5		(1 << 28)
+#define GALAXY_CONFIG_CDIRQ_11		(1 << 29)
+#define GALAXY_CONFIG_CDIRQ_12		(1 << 30)
+#define GALAXY_CONFIG_CDIRQ_15		(1 << 31)
+#define GALAXY_CONFIG_CDIRQ_MASK	(\
+	GALAXY_CONFIG_CDIRQ_5 | GALAXY_CONFIG_CDIRQ_11 |\
+	GALAXY_CONFIG_CDIRQ_12 | GALAXY_CONFIG_CDIRQ_15)
+
+#define GALAXY_CONFIG_MASK		(\
+	GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CDA_MASK |\
+	GALAXY_CONFIG_CD_MASK | GALAXY_CONFIG_CDDMA16_MASK |\
+	GALAXY_CONFIG_CDDMA8_MASK | GALAXY_CONFIG_CDIRQ_MASK)
+
+#include "galaxy.c"
diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c
new file mode 100644
index 0000000..ee54df0
--- /dev/null
+++ b/sound/isa/galaxy/galaxy.c
@@ -0,0 +1,652 @@
+/*
+ * Aztech AZT1605/AZT2316 Driver
+ * Copyright (C) 2007,2010  Rene Herman
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/isa.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <asm/processor.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/wss.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+
+MODULE_DESCRIPTION(CRD_NAME);
+MODULE_AUTHOR("Rene Herman");
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
+
+static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+
+module_param_array(port, long, NULL, 0444);
+MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
+module_param_array(wss_port, long, NULL, 0444);
+MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver.");
+module_param_array(mpu_port, long, NULL, 0444);
+MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
+module_param_array(fm_port, long, NULL, 0444);
+MODULE_PARM_DESC(fm_port, "FM port # for " CRD_NAME " driver.");
+module_param_array(irq, int, NULL, 0444);
+MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
+module_param_array(mpu_irq, int, NULL, 0444);
+MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
+module_param_array(dma1, int, NULL, 0444);
+MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver.");
+module_param_array(dma2, int, NULL, 0444);
+MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver.");
+
+/*
+ * Generic SB DSP support routines
+ */
+
+#define DSP_PORT_RESET		0x6
+#define DSP_PORT_READ		0xa
+#define DSP_PORT_COMMAND	0xc
+#define DSP_PORT_STATUS		0xc
+#define DSP_PORT_DATA_AVAIL	0xe
+
+#define DSP_SIGNATURE		0xaa
+
+#define DSP_COMMAND_GET_VERSION	0xe1
+
+static int __devinit dsp_get_byte(void __iomem *port, u8 *val)
+{
+	int loops = 1000;
+
+	while (!(ioread8(port + DSP_PORT_DATA_AVAIL) & 0x80)) {
+		if (!loops--)
+			return -EIO;
+		cpu_relax();
+	}
+	*val = ioread8(port + DSP_PORT_READ);
+	return 0;
+}
+
+static int __devinit dsp_reset(void __iomem *port)
+{
+	u8 val;
+
+	iowrite8(1, port + DSP_PORT_RESET);
+	udelay(10);
+	iowrite8(0, port + DSP_PORT_RESET);
+
+	if (dsp_get_byte(port, &val) < 0 || val != DSP_SIGNATURE)
+		return -ENODEV;
+
+	return 0;
+}
+
+static int __devinit dsp_command(void __iomem *port, u8 cmd)
+{
+	int loops = 1000;
+
+	while (ioread8(port + DSP_PORT_STATUS) & 0x80) {
+		if (!loops--)
+			return -EIO;
+		cpu_relax();
+	}
+	iowrite8(cmd, port + DSP_PORT_COMMAND);
+	return 0;
+}
+
+static int __devinit dsp_get_version(void __iomem *port, u8 *major, u8 *minor)
+{
+	int err;
+
+	err = dsp_command(port, DSP_COMMAND_GET_VERSION);
+	if (err < 0)
+		return err;
+
+	err = dsp_get_byte(port, major);
+	if (err < 0)
+		return err;
+
+	err = dsp_get_byte(port, minor);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+/*
+ * Generic WSS support routines
+ */
+
+#define WSS_CONFIG_DMA_0	(1 << 0)
+#define WSS_CONFIG_DMA_1	(2 << 0)
+#define WSS_CONFIG_DMA_3	(3 << 0)
+#define WSS_CONFIG_DUPLEX	(1 << 2)
+#define WSS_CONFIG_IRQ_7	(1 << 3)
+#define WSS_CONFIG_IRQ_9	(2 << 3)
+#define WSS_CONFIG_IRQ_10	(3 << 3)
+#define WSS_CONFIG_IRQ_11	(4 << 3)
+
+#define WSS_PORT_CONFIG		0
+#define WSS_PORT_SIGNATURE	3
+
+#define WSS_SIGNATURE		4
+
+static int __devinit wss_detect(void __iomem *wss_port)
+{
+	if ((ioread8(wss_port + WSS_PORT_SIGNATURE) & 0x3f) != WSS_SIGNATURE)
+		return -ENODEV;
+
+	return 0;
+}
+
+static void wss_set_config(void __iomem *wss_port, u8 wss_config)
+{
+	iowrite8(wss_config, wss_port + WSS_PORT_CONFIG);
+}
+
+/*
+ * Aztech Sound Galaxy specifics
+ */
+
+#define GALAXY_PORT_CONFIG	1024
+#define CONFIG_PORT_SET		4
+
+#define DSP_COMMAND_GALAXY_8	8
+#define GALAXY_COMMAND_GET_TYPE	5
+
+#define DSP_COMMAND_GALAXY_9	9
+#define GALAXY_COMMAND_WSSMODE	0
+#define GALAXY_COMMAND_SB8MODE	1
+
+#define GALAXY_MODE_WSS		GALAXY_COMMAND_WSSMODE
+#define GALAXY_MODE_SB8		GALAXY_COMMAND_SB8MODE
+
+struct snd_galaxy {
+	void __iomem *port;
+	void __iomem *config_port;
+	void __iomem *wss_port;
+	u32 config;
+	struct resource *res_port;
+	struct resource *res_config_port;
+	struct resource *res_wss_port;
+};
+
+static u32 config[SNDRV_CARDS];
+static u8 wss_config[SNDRV_CARDS];
+
+static int __devinit snd_galaxy_match(struct device *dev, unsigned int n)
+{
+	if (!enable[n])
+		return 0;
+
+	switch (port[n]) {
+	case SNDRV_AUTO_PORT:
+		dev_err(dev, "please specify port\n");
+		return 0;
+	case 0x220:
+		config[n] |= GALAXY_CONFIG_SBA_220;
+		break;
+	case 0x240:
+		config[n] |= GALAXY_CONFIG_SBA_240;
+		break;
+	case 0x260:
+		config[n] |= GALAXY_CONFIG_SBA_260;
+		break;
+	case 0x280:
+		config[n] |= GALAXY_CONFIG_SBA_280;
+		break;
+	default:
+		dev_err(dev, "invalid port %#lx\n", port[n]);
+		return 0;
+	}
+
+	switch (wss_port[n]) {
+	case SNDRV_AUTO_PORT:
+		dev_err(dev,  "please specify wss_port\n");
+		return 0;
+	case 0x530:
+		config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_530;
+		break;
+	case 0x604:
+		config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_604;
+		break;
+	case 0xe80:
+		config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_E80;
+		break;
+	case 0xf40:
+		config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_F40;
+		break;
+	default:
+		dev_err(dev, "invalid WSS port %#lx\n", wss_port[n]);
+		return 0;
+	}
+
+	switch (irq[n]) {
+	case SNDRV_AUTO_IRQ:
+		dev_err(dev,  "please specify irq\n");
+		return 0;
+	case 7:
+		wss_config[n] |= WSS_CONFIG_IRQ_7;
+		break;
+	case 2:
+		irq[n] = 9;
+	case 9:
+		wss_config[n] |= WSS_CONFIG_IRQ_9;
+		break;
+	case 10:
+		wss_config[n] |= WSS_CONFIG_IRQ_10;
+		break;
+	case 11:
+		wss_config[n] |= WSS_CONFIG_IRQ_11;
+		break;
+	default:
+		dev_err(dev, "invalid IRQ %d\n", irq[n]);
+		return 0;
+	}
+
+	switch (dma1[n]) {
+	case SNDRV_AUTO_DMA:
+		dev_err(dev,  "please specify dma1\n");
+		return 0;
+	case 0:
+		wss_config[n] |= WSS_CONFIG_DMA_0;
+		break;
+	case 1:
+		wss_config[n] |= WSS_CONFIG_DMA_1;
+		break;
+	case 3:
+		wss_config[n] |= WSS_CONFIG_DMA_3;
+		break;
+	default:
+		dev_err(dev, "invalid playback DMA %d\n", dma1[n]);
+		return 0;
+	}
+
+	if (dma2[n] == SNDRV_AUTO_DMA || dma2[n] == dma1[n]) {
+		dma2[n] = -1;
+		goto mpu;
+	}
+
+	wss_config[n] |= WSS_CONFIG_DUPLEX;
+	switch (dma2[n]) {
+	case 0:
+		break;
+	case 1:
+		if (dma1[n] == 0)
+			break;
+	default:
+		dev_err(dev, "invalid capture DMA %d\n", dma2[n]);
+		return 0;
+	}
+
+mpu:
+	switch (mpu_port[n]) {
+	case SNDRV_AUTO_PORT:
+		dev_warn(dev, "mpu_port not specified; not using MPU-401\n");
+		mpu_port[n] = -1;
+		goto fm;
+	case 0x300:
+		config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_300;
+		break;
+	case 0x330:
+		config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_330;
+		break;
+	default:
+		dev_err(dev, "invalid MPU port %#lx\n", mpu_port[n]);
+		return 0;
+	}
+
+	switch (mpu_irq[n]) {
+	case SNDRV_AUTO_IRQ:
+		dev_warn(dev, "mpu_irq not specified: using polling mode\n");
+		mpu_irq[n] = -1;
+		break;
+	case 2:
+		mpu_irq[n] = 9;
+	case 9:
+		config[n] |= GALAXY_CONFIG_MPUIRQ_2;
+		break;
+#ifdef AZT1605
+	case 3:
+		config[n] |= GALAXY_CONFIG_MPUIRQ_3;
+		break;
+#endif
+	case 5:
+		config[n] |= GALAXY_CONFIG_MPUIRQ_5;
+		break;
+	case 7:
+		config[n] |= GALAXY_CONFIG_MPUIRQ_7;
+		break;
+#ifdef AZT2316
+	case 10:
+		config[n] |= GALAXY_CONFIG_MPUIRQ_10;
+		break;
+#endif
+	default:
+		dev_err(dev, "invalid MPU IRQ %d\n", mpu_irq[n]);
+		return 0;
+	}
+
+	if (mpu_irq[n] == irq[n]) {
+		dev_err(dev, "cannot share IRQ between WSS and MPU-401\n");
+		return 0;
+	}
+
+fm:
+	switch (fm_port[n]) {
+	case SNDRV_AUTO_PORT:
+		dev_warn(dev, "fm_port not specified: not using OPL3\n");
+		fm_port[n] = -1;
+		break;
+	case 0x388:
+		break;
+	default:
+		dev_err(dev, "illegal FM port %#lx\n", fm_port[n]);
+		return 0;
+	}
+
+	config[n] |= GALAXY_CONFIG_GAME_ENABLE;
+	return 1;
+}
+
+static int __devinit galaxy_init(struct snd_galaxy *galaxy, u8 *type)
+{
+	u8 major;
+	u8 minor;
+	int err;
+
+	err = dsp_reset(galaxy->port);
+	if (err < 0)
+		return err;
+
+	err = dsp_get_version(galaxy->port, &major, &minor);
+	if (err < 0)
+		return err;
+
+	if (major != GALAXY_DSP_MAJOR || minor != GALAXY_DSP_MINOR)
+		return -ENODEV;
+
+	err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_8);
+	if (err < 0)
+		return err;
+
+	err = dsp_command(galaxy->port, GALAXY_COMMAND_GET_TYPE);
+	if (err < 0)
+		return err;
+
+	err = dsp_get_byte(galaxy->port, type);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int __devinit galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode)
+{
+	int err;
+
+	err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_9);
+	if (err < 0)
+		return err;
+
+	err = dsp_command(galaxy->port, mode);
+	if (err < 0)
+		return err;
+
+#ifdef AZT1605
+	/*
+	 * Needed for MPU IRQ on AZT1605, but AZT2316 loses WSS again
+	 */
+	err = dsp_reset(galaxy->port);
+	if (err < 0)
+		return err;
+#endif
+
+	return 0;
+}
+
+static void galaxy_set_config(struct snd_galaxy *galaxy, u32 config)
+{
+	u8 tmp = ioread8(galaxy->config_port + CONFIG_PORT_SET);
+	int i;
+
+	iowrite8(tmp | 0x80, galaxy->config_port + CONFIG_PORT_SET);
+	for (i = 0; i < GALAXY_CONFIG_SIZE; i++) {
+		iowrite8(config, galaxy->config_port + i);
+		config >>= 8;
+	}
+	iowrite8(tmp & 0x7f, galaxy->config_port + CONFIG_PORT_SET);
+	msleep(10);
+}
+
+static void __devinit galaxy_config(struct snd_galaxy *galaxy, u32 config)
+{
+	int i;
+
+	for (i = GALAXY_CONFIG_SIZE; i; i--) {
+		u8 tmp = ioread8(galaxy->config_port + i - 1);
+		galaxy->config = (galaxy->config << 8) | tmp;
+	}
+	config |= galaxy->config & GALAXY_CONFIG_MASK;
+	galaxy_set_config(galaxy, config);
+}
+
+static int __devinit galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config)
+{
+	int err;
+
+	err = wss_detect(galaxy->wss_port);
+	if (err < 0)
+		return err;
+
+	wss_set_config(galaxy->wss_port, wss_config);
+
+	err = galaxy_set_mode(galaxy, GALAXY_MODE_WSS);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static void snd_galaxy_free(struct snd_card *card)
+{
+	struct snd_galaxy *galaxy = card->private_data;
+
+	if (galaxy->wss_port) {
+		wss_set_config(galaxy->wss_port, 0);
+		ioport_unmap(galaxy->wss_port);
+		release_and_free_resource(galaxy->res_wss_port);
+	}
+	if (galaxy->config_port) {
+		galaxy_set_config(galaxy, galaxy->config);
+		ioport_unmap(galaxy->config_port);
+		release_and_free_resource(galaxy->res_config_port);
+	}
+	if (galaxy->port) {
+		ioport_unmap(galaxy->port);
+		release_and_free_resource(galaxy->res_port);
+	}
+}
+
+static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n)
+{
+	struct snd_galaxy *galaxy;
+	struct snd_wss *chip;
+	struct snd_card *card;
+	u8 type;
+	int err;
+
+	err = snd_card_create(index[n], id[n], THIS_MODULE, sizeof *galaxy,
+			      &card);
+	if (err < 0)
+		return err;
+
+	snd_card_set_dev(card, dev);
+
+	card->private_free = snd_galaxy_free;
+	galaxy = card->private_data;
+
+	galaxy->res_port = request_region(port[n], 16, DRV_NAME);
+	if (!galaxy->res_port) {
+		dev_err(dev, "could not grab ports %#lx-%#lx\n", port[n],
+			port[n] + 15);
+		err = -EBUSY;
+		goto error;
+	}
+	galaxy->port = ioport_map(port[n], 16);
+
+	err = galaxy_init(galaxy, &type);
+	if (err < 0) {
+		dev_err(dev, "did not find a Sound Galaxy at %#lx\n", port[n]);
+		goto error;
+	}
+	dev_info(dev, "Sound Galaxy (type %d) found at %#lx\n", type, port[n]);
+
+	galaxy->res_config_port = request_region(port[n] + GALAXY_PORT_CONFIG,
+						 16, DRV_NAME);
+	if (!galaxy->res_config_port) {
+		dev_err(dev, "could not grab ports %#lx-%#lx\n",
+			port[n] + GALAXY_PORT_CONFIG,
+			port[n] + GALAXY_PORT_CONFIG + 15);
+		err = -EBUSY;
+		goto error;
+	}
+	galaxy->config_port = ioport_map(port[n] + GALAXY_PORT_CONFIG, 16);
+
+	galaxy_config(galaxy, config[n]);
+
+	galaxy->res_wss_port = request_region(wss_port[n], 4, DRV_NAME);
+	if (!galaxy->res_wss_port)  {
+		dev_err(dev, "could not grab ports %#lx-%#lx\n", wss_port[n],
+			wss_port[n] + 3);
+		err = -EBUSY;
+		goto error;
+	}
+	galaxy->wss_port = ioport_map(wss_port[n], 4);
+
+	err = galaxy_wss_config(galaxy, wss_config[n]);
+	if (err < 0) {
+		dev_err(dev, "could not configure WSS\n");
+		goto error;
+	}
+
+	strcpy(card->driver, DRV_NAME);
+	strcpy(card->shortname, DRV_NAME);
+	sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d",
+		card->shortname, port[n], wss_port[n], irq[n], dma1[n],
+		dma2[n]);
+
+	err = snd_wss_create(card, wss_port[n] + 4, -1, irq[n], dma1[n],
+			     dma2[n], WSS_HW_DETECT, 0, &chip);
+	if (err < 0)
+		goto error;
+
+	err = snd_wss_pcm(chip, 0, NULL);
+	if (err < 0)
+		goto error;
+
+	err = snd_wss_mixer(chip);
+	if (err < 0)
+		goto error;
+
+	err = snd_wss_timer(chip, 0, NULL);
+	if (err < 0)
+		goto error;
+
+	if (mpu_port[n] >= 0) {
+		err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+					  mpu_port[n], 0, mpu_irq[n],
+					  IRQF_DISABLED, NULL);
+		if (err < 0)
+			goto error;
+	}
+
+	if (fm_port[n] >= 0) {
+		struct snd_opl3 *opl3;
+
+		err = snd_opl3_create(card, fm_port[n], fm_port[n] + 2,
+				      OPL3_HW_AUTO, 0, &opl3);
+		if (err < 0) {
+			dev_err(dev, "no OPL device at %#lx\n", fm_port[n]);
+			goto error;
+		}
+		err = snd_opl3_timer_new(opl3, 1, 2);
+		if (err < 0)
+			goto error;
+
+		err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+		if (err < 0)
+			goto error;
+	}
+
+	err = snd_card_register(card);
+	if (err < 0)
+		goto error;
+
+	dev_set_drvdata(dev, card);
+	return 0;
+
+error:
+	snd_card_free(card);
+	return err;
+}
+
+static int __devexit snd_galaxy_remove(struct device *dev, unsigned int n)
+{
+	snd_card_free(dev_get_drvdata(dev));
+	dev_set_drvdata(dev, NULL);
+	return 0;
+}
+
+static struct isa_driver snd_galaxy_driver = {
+	.match		= snd_galaxy_match,
+	.probe		= snd_galaxy_probe,
+	.remove		= __devexit_p(snd_galaxy_remove),
+
+	.driver		= {
+		.name	= DEV_NAME
+	}
+};
+
+static int __init alsa_card_galaxy_init(void)
+{
+	return isa_register_driver(&snd_galaxy_driver, SNDRV_CARDS);
+}
+
+static void __exit alsa_card_galaxy_exit(void)
+{
+	isa_unregister_driver(&snd_galaxy_driver);
+}
+
+module_init(alsa_card_galaxy_init);
+module_exit(alsa_card_galaxy_exit);
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index f26eac8..3e4a58b 100644
--- a/sound/isa/gus/gusmax.c
+++ b/sound/isa/gus/gusmax.c
@@ -191,7 +191,7 @@
 
 static void snd_gusmax_free(struct snd_card *card)
 {
-	struct snd_gusmax *maxcard = (struct snd_gusmax *)card->private_data;
+	struct snd_gusmax *maxcard = card->private_data;
 	
 	if (maxcard == NULL)
 		return;
@@ -219,7 +219,7 @@
 	if (err < 0)
 		return err;
 	card->private_free = snd_gusmax_free;
-	maxcard = (struct snd_gusmax *)card->private_data;
+	maxcard = card->private_data;
 	maxcard->card = card;
 	maxcard->irq = -1;
 	
diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c
index 81284a8..2259e3f 100644
--- a/sound/isa/sb/sb8.c
+++ b/sound/isa/sb/sb8.c
@@ -72,7 +72,7 @@
 
 static void snd_sb8_free(struct snd_card *card)
 {
-	struct snd_sb8 *acard = (struct snd_sb8 *)card->private_data;
+	struct snd_sb8 *acard = card->private_data;
 
 	if (acard == NULL)
 		return;
diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c
deleted file mode 100644
index 6fe27b9..0000000
--- a/sound/isa/sgalaxy.c
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- *  Driver for Aztech Sound Galaxy cards
- *  Copyright (c) by Christopher Butler <chrisb@sandy.force9.co.uk.
- *
- *  I don't have documentation for this card, I based this driver on the
- *  driver for OSS/Free included in the kernel source (drivers/sound/sgalaxy.c)
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/isa.h>
-#include <linux/delay.h>
-#include <linux/time.h>
-#include <linux/interrupt.h>
-#include <linux/moduleparam.h>
-#include <asm/dma.h>
-#include <sound/core.h>
-#include <sound/sb.h>
-#include <sound/wss.h>
-#include <sound/control.h>
-#define SNDRV_LEGACY_FIND_FREE_IRQ
-#define SNDRV_LEGACY_FIND_FREE_DMA
-#include <sound/initval.h>
-
-MODULE_AUTHOR("Christopher Butler <chrisb@sandy.force9.co.uk>");
-MODULE_DESCRIPTION("Aztech Sound Galaxy");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Aztech Systems,Sound Galaxy}}");
-
-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;	/* Enable this card */
-static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240 */
-static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x530,0xe80,0xf40,0x604 */
-static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 7,9,10,11 */
-static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for Sound Galaxy soundcard.");
-module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string for Sound Galaxy soundcard.");
-module_param_array(sbport, long, NULL, 0444);
-MODULE_PARM_DESC(sbport, "Port # for Sound Galaxy SB driver.");
-module_param_array(wssport, long, NULL, 0444);
-MODULE_PARM_DESC(wssport, "Port # for Sound Galaxy WSS driver.");
-module_param_array(irq, int, NULL, 0444);
-MODULE_PARM_DESC(irq, "IRQ # for Sound Galaxy driver.");
-module_param_array(dma1, int, NULL, 0444);
-MODULE_PARM_DESC(dma1, "DMA1 # for Sound Galaxy driver.");
-
-#define SGALAXY_AUXC_LEFT 18
-#define SGALAXY_AUXC_RIGHT 19
-
-#define PFX	"sgalaxy: "
-
-/*
-
- */
-
-#define AD1848P1( port, x ) ( port + c_d_c_AD1848##x )
-
-/* from lowlevel/sb/sb.c - to avoid having to allocate a struct snd_sb for the */
-/* short time we actually need it.. */
-
-static int snd_sgalaxy_sbdsp_reset(unsigned long port)
-{
-	int i;
-
-	outb(1, SBP1(port, RESET));
-	udelay(10);
-	outb(0, SBP1(port, RESET));
-	udelay(30);
-	for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++);
-	if (inb(SBP1(port, READ)) != 0xaa) {
-		snd_printd("sb_reset: failed at 0x%lx!!!\n", port);
-		return -ENODEV;
-	}
-	return 0;
-}
-
-static int __devinit snd_sgalaxy_sbdsp_command(unsigned long port,
-					       unsigned char val)
-{
-	int i;
-       	
-	for (i = 10000; i; i--)
-		if ((inb(SBP1(port, STATUS)) & 0x80) == 0) {
-			outb(val, SBP1(port, COMMAND));
-			return 1;
-		}
-
-	return 0;
-}
-
-static irqreturn_t snd_sgalaxy_dummy_interrupt(int irq, void *dev_id)
-{
-	return IRQ_NONE;
-}
-
-static int __devinit snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma)
-{
-	static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 
-				       0x10, 0x18, 0x20, -1, -1, -1, -1};
-	static int dma_bits[] = {1, 2, 0, 3};
-	int tmp, tmp1;
-
-	if ((tmp = inb(port + 3)) == 0xff)
-	{
-		snd_printdd("I/O address dead (0x%lx)\n", port);
-		return 0;
-	}
-#if 0
-	snd_printdd("WSS signature = 0x%x\n", tmp);
-#endif
-
-        if ((tmp & 0x3f) != 0x04 &&
-            (tmp & 0x3f) != 0x0f &&
-            (tmp & 0x3f) != 0x00) {
-		snd_printdd("No WSS signature detected on port 0x%lx\n",
-			    port + 3);
-		return 0;
-	}
-
-#if 0
-	snd_printdd(PFX "setting up IRQ/DMA for WSS\n");
-#endif
-
-        /* initialize IRQ for WSS codec */
-        tmp = interrupt_bits[irq % 16];
-        if (tmp < 0)
-                return -EINVAL;
-
-	if (request_irq(irq, snd_sgalaxy_dummy_interrupt, IRQF_DISABLED, "sgalaxy", NULL)) {
-		snd_printk(KERN_ERR "sgalaxy: can't grab irq %d\n", irq);
-		return -EIO;
-	}
-
-        outb(tmp | 0x40, port);
-        tmp1 = dma_bits[dma % 4];
-        outb(tmp | tmp1, port);
-
-	free_irq(irq, NULL);
-
-	return 0;
-}
-
-static int __devinit snd_sgalaxy_detect(int dev, int irq, int dma)
-{
-#if 0
-	snd_printdd(PFX "switching to WSS mode\n");
-#endif
-
-	/* switch to WSS mode */
-	snd_sgalaxy_sbdsp_reset(sbport[dev]);
-
-	snd_sgalaxy_sbdsp_command(sbport[dev], 9);
-	snd_sgalaxy_sbdsp_command(sbport[dev], 0);
-
-	udelay(400);
-	return snd_sgalaxy_setup_wss(wssport[dev], irq, dma);
-}
-
-static struct snd_kcontrol_new snd_sgalaxy_controls[] = {
-WSS_DOUBLE("Aux Playback Switch", 0,
-		SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Playback Volume", 0,
-		SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0)
-};
-
-static int __devinit snd_sgalaxy_mixer(struct snd_wss *chip)
-{
-	struct snd_card *card = chip->card;
-	struct snd_ctl_elem_id id1, id2;
-	unsigned int idx;
-	int err;
-
-	memset(&id1, 0, sizeof(id1));
-	memset(&id2, 0, sizeof(id2));
-	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-	/* reassign AUX0 to LINE */
-	strcpy(id1.name, "Aux Playback Switch");
-	strcpy(id2.name, "Line Playback Switch");
-	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
-		return err;
-	strcpy(id1.name, "Aux Playback Volume");
-	strcpy(id2.name, "Line Playback Volume");
-	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
-		return err;
-	/* reassign AUX1 to FM */
-	strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
-	strcpy(id2.name, "FM Playback Switch");
-	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
-		return err;
-	strcpy(id1.name, "Aux Playback Volume");
-	strcpy(id2.name, "FM Playback Volume");
-	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
-		return err;
-	/* build AUX2 input */
-	for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) {
-		err = snd_ctl_add(card,
-				snd_ctl_new1(&snd_sgalaxy_controls[idx], chip));
-		if (err < 0)
-			return err;
-	}
-	return 0;
-}
-
-static int __devinit snd_sgalaxy_match(struct device *devptr, unsigned int dev)
-{
-	if (!enable[dev])
-		return 0;
-	if (sbport[dev] == SNDRV_AUTO_PORT) {
-		snd_printk(KERN_ERR PFX "specify SB port\n");
-		return 0;
-	}
-	if (wssport[dev] == SNDRV_AUTO_PORT) {
-		snd_printk(KERN_ERR PFX "specify WSS port\n");
-		return 0;
-	}
-	return 1;
-}
-
-static int __devinit snd_sgalaxy_probe(struct device *devptr, unsigned int dev)
-{
-	static int possible_irqs[] = {7, 9, 10, 11, -1};
-	static int possible_dmas[] = {1, 3, 0, -1};
-	int err, xirq, xdma1;
-	struct snd_card *card;
-	struct snd_wss *chip;
-
-	err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
-	if (err < 0)
-		return err;
-
-	xirq = irq[dev];
-	if (xirq == SNDRV_AUTO_IRQ) {
-		if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
-			snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
-			err = -EBUSY;
-			goto _err;
-		}
-	}
-	xdma1 = dma1[dev];
-        if (xdma1 == SNDRV_AUTO_DMA) {
-		if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
-			snd_printk(KERN_ERR PFX "unable to find a free DMA\n");
-			err = -EBUSY;
-			goto _err;
-		}
-	}
-
-	if ((err = snd_sgalaxy_detect(dev, xirq, xdma1)) < 0)
-		goto _err;
-
-	err = snd_wss_create(card, wssport[dev] + 4, -1,
-			     xirq, xdma1, -1,
-			     WSS_HW_DETECT, 0, &chip);
-	if (err < 0)
-		goto _err;
-	card->private_data = chip;
-
-	err = snd_wss_pcm(chip, 0, NULL);
-	if (err < 0) {
-		snd_printdd(PFX "error creating new WSS PCM device\n");
-		goto _err;
-	}
-	err = snd_wss_mixer(chip);
-	if (err < 0) {
-		snd_printdd(PFX "error creating new WSS mixer\n");
-		goto _err;
-	}
-	if ((err = snd_sgalaxy_mixer(chip)) < 0) {
-		snd_printdd(PFX "the mixer rewrite failed\n");
-		goto _err;
-	}
-
-	strcpy(card->driver, "Sound Galaxy");
-	strcpy(card->shortname, "Sound Galaxy");
-	sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d",
-		wssport[dev], xirq, xdma1);
-
-	snd_card_set_dev(card, devptr);
-
-	if ((err = snd_card_register(card)) < 0)
-		goto _err;
-
-	dev_set_drvdata(devptr, card);
-	return 0;
-
- _err:
-	snd_card_free(card);
-	return err;
-}
-
-static int __devexit snd_sgalaxy_remove(struct device *devptr, unsigned int dev)
-{
-	snd_card_free(dev_get_drvdata(devptr));
-	dev_set_drvdata(devptr, NULL);
-	return 0;
-}
-
-#ifdef CONFIG_PM
-static int snd_sgalaxy_suspend(struct device *pdev, unsigned int n,
-			       pm_message_t state)
-{
-	struct snd_card *card = dev_get_drvdata(pdev);
-	struct snd_wss *chip = card->private_data;
-
-	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-	chip->suspend(chip);
-	return 0;
-}
-
-static int snd_sgalaxy_resume(struct device *pdev, unsigned int n)
-{
-	struct snd_card *card = dev_get_drvdata(pdev);
-	struct snd_wss *chip = card->private_data;
-
-	chip->resume(chip);
-	snd_wss_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]);
-	snd_wss_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]);
-
-	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
-	return 0;
-}
-#endif
-
-#define DEV_NAME "sgalaxy"
-
-static struct isa_driver snd_sgalaxy_driver = {
-	.match		= snd_sgalaxy_match,
-	.probe		= snd_sgalaxy_probe,
-	.remove		= __devexit_p(snd_sgalaxy_remove),
-#ifdef CONFIG_PM
-	.suspend	= snd_sgalaxy_suspend,
-	.resume		= snd_sgalaxy_resume,
-#endif
-	.driver		= {
-		.name	= DEV_NAME
-	},
-};
-
-static int __init alsa_card_sgalaxy_init(void)
-{
-	return isa_register_driver(&snd_sgalaxy_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_sgalaxy_exit(void)
-{
-	isa_unregister_driver(&snd_sgalaxy_driver);
-}
-
-module_init(alsa_card_sgalaxy_init)
-module_exit(alsa_card_sgalaxy_exit)
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
index a513651..76c0902 100644
--- a/sound/oss/Kconfig
+++ b/sound/oss/Kconfig
@@ -545,11 +545,3 @@
 
 endif	# SOUND_OSS
 
-config SOUND_SH_DAC_AUDIO
-	tristate "SuperH DAC audio support"
-	depends on CPU_SH3 && HIGH_RES_TIMERS
-
-config SOUND_SH_DAC_AUDIO_CHANNEL
-	int "DAC channel"
-	default "1"
-	depends on SOUND_SH_DAC_AUDIO
diff --git a/sound/oss/Makefile b/sound/oss/Makefile
index 567b8a7..96f14dc 100644
--- a/sound/oss/Makefile
+++ b/sound/oss/Makefile
@@ -9,7 +9,6 @@
 
 # Please leave it as is, cause the link order is significant !
 
-obj-$(CONFIG_SOUND_SH_DAC_AUDIO)	+= sh_dac_audio.o
 obj-$(CONFIG_SOUND_AEDSP16)	+= aedsp16.o
 obj-$(CONFIG_SOUND_PSS)		+= pss.o ad1848.o mpu401.o
 obj-$(CONFIG_SOUND_TRIX)	+= trix.o ad1848.o sb_lib.o uart401.o
diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c
index c6f2621..a8f626d 100644
--- a/sound/oss/au1550_ac97.c
+++ b/sound/oss/au1550_ac97.c
@@ -43,7 +43,6 @@
 #include <linux/sound.h>
 #include <linux/slab.h>
 #include <linux/soundcard.h>
-#include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
@@ -77,6 +76,7 @@
 /* Boot options
  * 0 = no VRA, 1 = use VRA if codec supports it
  */
+static DEFINE_MUTEX(au1550_ac97_mutex);
 static int      vra = 1;
 module_param(vra, bool, 0);
 MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");
@@ -171,7 +171,7 @@
 static u16
 rdcodec(struct ac97_codec *codec, u8 addr)
 {
-	struct au1550_state *s = (struct au1550_state *)codec->private_data;
+	struct au1550_state *s = codec->private_data;
 	unsigned long   flags;
 	u32             cmd, val;
 	u16             data;
@@ -239,7 +239,7 @@
 static void
 wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
 {
-	struct au1550_state *s = (struct au1550_state *)codec->private_data;
+	struct au1550_state *s = codec->private_data;
 	unsigned long   flags;
 	u32             cmd, val;
 	int             i;
@@ -798,9 +798,9 @@
 static int
 au1550_open_mixdev(struct inode *inode, struct file *file)
 {
-	lock_kernel();
+	mutex_lock(&au1550_ac97_mutex);
 	file->private_data = &au1550_state;
-	unlock_kernel();
+	mutex_unlock(&au1550_ac97_mutex);
 	return 0;
 }
 
@@ -820,13 +820,13 @@
 static long
 au1550_ioctl_mixdev(struct file *file, unsigned int cmd, unsigned long arg)
 {
-	struct au1550_state *s = (struct au1550_state *)file->private_data;
+	struct au1550_state *s = file->private_data;
 	struct ac97_codec *codec = s->codec;
 	int ret;
 
-	lock_kernel();
+	mutex_lock(&au1550_ac97_mutex);
 	ret = mixdev_ioctl(codec, cmd, arg);
-	unlock_kernel();
+	mutex_unlock(&au1550_ac97_mutex);
 
 	return ret;
 }
@@ -1031,7 +1031,7 @@
 static ssize_t
 au1550_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
-	struct au1550_state *s = (struct au1550_state *)file->private_data;
+	struct au1550_state *s = file->private_data;
 	struct dmabuf  *db = &s->dma_adc;
 	DECLARE_WAITQUEUE(wait, current);
 	ssize_t         ret;
@@ -1111,7 +1111,7 @@
 static ssize_t
 au1550_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
 {
-	struct au1550_state *s = (struct au1550_state *)file->private_data;
+	struct au1550_state *s = file->private_data;
 	struct dmabuf  *db = &s->dma_dac;
 	DECLARE_WAITQUEUE(wait, current);
 	ssize_t         ret = 0;
@@ -1211,7 +1211,7 @@
 static unsigned int
 au1550_poll(struct file *file, struct poll_table_struct *wait)
 {
-	struct au1550_state *s = (struct au1550_state *)file->private_data;
+	struct au1550_state *s = file->private_data;
 	unsigned long   flags;
 	unsigned int    mask = 0;
 
@@ -1250,12 +1250,12 @@
 static int
 au1550_mmap(struct file *file, struct vm_area_struct *vma)
 {
-	struct au1550_state *s = (struct au1550_state *)file->private_data;
+	struct au1550_state *s = file->private_data;
 	struct dmabuf  *db;
 	unsigned long   size;
 	int ret = 0;
 
-	lock_kernel();
+	mutex_lock(&au1550_ac97_mutex);
 	mutex_lock(&s->sem);
 	if (vma->vm_flags & VM_WRITE)
 		db = &s->dma_dac;
@@ -1283,7 +1283,7 @@
 	db->mapped = 1;
 out:
 	mutex_unlock(&s->sem);
-	unlock_kernel();
+	mutex_unlock(&au1550_ac97_mutex);
 	return ret;
 }
 
@@ -1342,7 +1342,7 @@
 static int
 au1550_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-	struct au1550_state *s = (struct au1550_state *)file->private_data;
+	struct au1550_state *s = file->private_data;
 	unsigned long   flags;
 	audio_buf_info  abinfo;
 	count_info      cinfo;
@@ -1781,9 +1781,9 @@
 {
 	int ret;
 
-	lock_kernel();
+	mutex_lock(&au1550_ac97_mutex);
 	ret = au1550_ioctl(file, cmd, arg);
-	unlock_kernel();
+	mutex_unlock(&au1550_ac97_mutex);
 
 	return ret;
 }
@@ -1804,7 +1804,7 @@
 #endif
 
 	file->private_data = s;
-	lock_kernel();
+	mutex_lock(&au1550_ac97_mutex);
 	/* wait for device to become free */
 	mutex_lock(&s->open_mutex);
 	while (s->open_mode & file->f_mode) {
@@ -1861,21 +1861,21 @@
 out:
 	mutex_unlock(&s->open_mutex);
 out2:
-	unlock_kernel();
+	mutex_unlock(&au1550_ac97_mutex);
 	return ret;
 }
 
 static int
 au1550_release(struct inode *inode, struct file *file)
 {
-	struct au1550_state *s = (struct au1550_state *)file->private_data;
+	struct au1550_state *s = file->private_data;
 
-	lock_kernel();
+	mutex_lock(&au1550_ac97_mutex);
 
 	if (file->f_mode & FMODE_WRITE) {
-		unlock_kernel();
+		mutex_unlock(&au1550_ac97_mutex);
 		drain_dac(s, file->f_flags & O_NONBLOCK);
-		lock_kernel();
+		mutex_lock(&au1550_ac97_mutex);
 	}
 
 	mutex_lock(&s->open_mutex);
@@ -1892,7 +1892,7 @@
 	s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE));
 	mutex_unlock(&s->open_mutex);
 	wake_up(&s->open_wait);
-	unlock_kernel();
+	mutex_unlock(&au1550_ac97_mutex);
 	return 0;
 }
 
diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c
index 6ecd41a..87e2c72 100644
--- a/sound/oss/dmasound/dmasound_core.c
+++ b/sound/oss/dmasound/dmasound_core.c
@@ -181,7 +181,7 @@
 #include <linux/init.h>
 #include <linux/soundcard.h>
 #include <linux/poll.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
 
 #include <asm/uaccess.h>
 
@@ -194,6 +194,7 @@
      *  Declarations
      */
 
+static DEFINE_MUTEX(dmasound_core_mutex);
 int dmasound_catchRadius = 0;
 module_param(dmasound_catchRadius, int, 0);
 
@@ -323,22 +324,22 @@
 
 static int mixer_open(struct inode *inode, struct file *file)
 {
-	lock_kernel();
+	mutex_lock(&dmasound_core_mutex);
 	if (!try_module_get(dmasound.mach.owner)) {
-		unlock_kernel();
+		mutex_unlock(&dmasound_core_mutex);
 		return -ENODEV;
 	}
 	mixer.busy = 1;
-	unlock_kernel();
+	mutex_unlock(&dmasound_core_mutex);
 	return 0;
 }
 
 static int mixer_release(struct inode *inode, struct file *file)
 {
-	lock_kernel();
+	mutex_lock(&dmasound_core_mutex);
 	mixer.busy = 0;
 	module_put(dmasound.mach.owner);
-	unlock_kernel();
+	mutex_unlock(&dmasound_core_mutex);
 	return 0;
 }
 
@@ -370,9 +371,9 @@
 {
 	int ret;
 
-	lock_kernel();
+	mutex_lock(&dmasound_core_mutex);
 	ret = mixer_ioctl(file, cmd, arg);
-	unlock_kernel();
+	mutex_unlock(&dmasound_core_mutex);
 
 	return ret;
 }
@@ -752,9 +753,9 @@
 {
 	int rc;
 
-	lock_kernel();
+	mutex_lock(&dmasound_core_mutex);
 	if (!try_module_get(dmasound.mach.owner)) {
-		unlock_kernel();
+		mutex_unlock(&dmasound_core_mutex);
 		return -ENODEV;
 	}
 
@@ -799,11 +800,11 @@
 		sound_set_format(AFMT_MU_LAW);
 	}
 #endif
-	unlock_kernel();
+	mutex_unlock(&dmasound_core_mutex);
 	return 0;
  out:
 	module_put(dmasound.mach.owner);
-	unlock_kernel();
+	mutex_unlock(&dmasound_core_mutex);
 	return rc;
 }
 
@@ -869,7 +870,7 @@
 {
 	int rc = 0;
 
-	lock_kernel();
+	mutex_lock(&dmasound_core_mutex);
 
 	if (file->f_mode & FMODE_WRITE) {
 		if (write_sq.busy)
@@ -900,7 +901,7 @@
 	write_sq_wake_up(file); /* checks f_mode */
 #endif /* blocking open() */
 
-	unlock_kernel();
+	mutex_unlock(&dmasound_core_mutex);
 
 	return rc;
 }
@@ -1141,9 +1142,9 @@
 {
 	int ret;
 
-	lock_kernel();
+	mutex_lock(&dmasound_core_mutex);
 	ret = sq_ioctl(file, cmd, arg);
-	unlock_kernel();
+	mutex_unlock(&dmasound_core_mutex);
 
 	return ret;
 }
@@ -1257,7 +1258,7 @@
 	int len = 0;
 	int ret;
 
-	lock_kernel();
+	mutex_lock(&dmasound_core_mutex);
 	ret = -EBUSY;
 	if (state.busy)
 		goto out;
@@ -1329,16 +1330,16 @@
 	state.len = len;
 	ret = 0;
 out:
-	unlock_kernel();
+	mutex_unlock(&dmasound_core_mutex);
 	return ret;
 }
 
 static int state_release(struct inode *inode, struct file *file)
 {
-	lock_kernel();
+	mutex_lock(&dmasound_core_mutex);
 	state.busy = 0;
 	module_put(dmasound.mach.owner);
-	unlock_kernel();
+	mutex_unlock(&dmasound_core_mutex);
 	return 0;
 }
 
diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c
index 2e48b17..b4c1eb5 100644
--- a/sound/oss/msnd_pinnacle.c
+++ b/sound/oss/msnd_pinnacle.c
@@ -39,7 +39,7 @@
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
 #include <linux/gfp.h>
 #include <asm/irq.h>
 #include <asm/io.h>
@@ -79,6 +79,7 @@
 					 dev.rec_sample_rate /		\
 					 dev.rec_channels)
 
+static DEFINE_MUTEX(msnd_pinnacle_mutex);
 static multisound_dev_t			dev;
 
 #ifndef HAVE_DSPCODEH
@@ -651,12 +652,12 @@
 
 	ret = -EINVAL;
 
-	lock_kernel();
+	mutex_lock(&msnd_pinnacle_mutex);
 	if (minor == dev.dsp_minor)
 		ret = dsp_ioctl(file, cmd, arg);
 	else if (minor == dev.mixer_minor)
 		ret = mixer_ioctl(cmd, arg);
-	unlock_kernel();
+	mutex_unlock(&msnd_pinnacle_mutex);
 
 	return ret;
 }
@@ -761,7 +762,7 @@
 	int minor = iminor(inode);
 	int err = 0;
 
-	lock_kernel();
+	mutex_lock(&msnd_pinnacle_mutex);
 	if (minor == dev.dsp_minor) {
 		if ((file->f_mode & FMODE_WRITE &&
 		     test_bit(F_AUDIO_WRITE_INUSE, &dev.flags)) ||
@@ -791,7 +792,7 @@
 	} else
 		err = -EINVAL;
 out:
-	unlock_kernel();
+	mutex_unlock(&msnd_pinnacle_mutex);
 	return err;
 }
 
@@ -800,14 +801,14 @@
 	int minor = iminor(inode);
 	int err = 0;
 
-	lock_kernel();
+	mutex_lock(&msnd_pinnacle_mutex);
 	if (minor == dev.dsp_minor)
 		err = dsp_release(file);
 	else if (minor == dev.mixer_minor) {
 		/* nothing */
 	} else
 		err = -EINVAL;
-	unlock_kernel();
+	mutex_unlock(&msnd_pinnacle_mutex);
 	return err;
 }
 
diff --git a/sound/oss/sh_dac_audio.c b/sound/oss/sh_dac_audio.c
deleted file mode 100644
index 479e302..0000000
--- a/sound/oss/sh_dac_audio.c
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * sound/oss/sh_dac_audio.c
- *
- * SH DAC based sound :(
- *
- *  Copyright (C) 2004,2005  Andriy Skulysh
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/linkage.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/sound.h>
-#include <linux/smp_lock.h>
-#include <linux/soundcard.h>
-#include <linux/interrupt.h>
-#include <linux/hrtimer.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/irq.h>
-#include <asm/delay.h>
-#include <asm/clock.h>
-#include <cpu/dac.h>
-#include <asm/machvec.h>
-#include <mach/hp6xx.h>
-#include <asm/hd64461.h>
-
-#define MODNAME "sh_dac_audio"
-
-#define BUFFER_SIZE 48000
-
-static int rate;
-static int empty;
-static char *data_buffer, *buffer_begin, *buffer_end;
-static int in_use, device_major;
-static struct hrtimer hrtimer;
-static ktime_t wakeups_per_second;
-
-static void dac_audio_start_timer(void)
-{
-	hrtimer_start(&hrtimer, wakeups_per_second, HRTIMER_MODE_REL);
-}
-
-static void dac_audio_stop_timer(void)
-{
-	hrtimer_cancel(&hrtimer);
-}
-
-static void dac_audio_reset(void)
-{
-	dac_audio_stop_timer();
-	buffer_begin = buffer_end = data_buffer;
-	empty = 1;
-}
-
-static void dac_audio_sync(void)
-{
-	while (!empty)
-		schedule();
-}
-
-static void dac_audio_start(void)
-{
-	if (mach_is_hp6xx()) {
-		u16 v = __raw_readw(HD64461_GPADR);
-		v &= ~HD64461_GPADR_SPEAKER;
-		__raw_writew(v, HD64461_GPADR);
-	}
-
-	sh_dac_enable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
-}
-static void dac_audio_stop(void)
-{
-	dac_audio_stop_timer();
-
-	if (mach_is_hp6xx()) {
-		u16 v = __raw_readw(HD64461_GPADR);
-		v |= HD64461_GPADR_SPEAKER;
-		__raw_writew(v, HD64461_GPADR);
-	}
-
-	sh_dac_output(0, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
-	sh_dac_disable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
-}
-
-static void dac_audio_set_rate(void)
-{
-	wakeups_per_second = ktime_set(0, 1000000000 / rate);
-}
-
-static int dac_audio_ioctl(struct file *file,
-			   unsigned int cmd, unsigned long arg)
-{
-	int val;
-
-	switch (cmd) {
-	case OSS_GETVERSION:
-		return put_user(SOUND_VERSION, (int *)arg);
-
-	case SNDCTL_DSP_SYNC:
-		dac_audio_sync();
-		return 0;
-
-	case SNDCTL_DSP_RESET:
-		dac_audio_reset();
-		return 0;
-
-	case SNDCTL_DSP_GETFMTS:
-		return put_user(AFMT_U8, (int *)arg);
-
-	case SNDCTL_DSP_SETFMT:
-		return put_user(AFMT_U8, (int *)arg);
-
-	case SNDCTL_DSP_NONBLOCK:
-		spin_lock(&file->f_lock);
-		file->f_flags |= O_NONBLOCK;
-		spin_unlock(&file->f_lock);
-		return 0;
-
-	case SNDCTL_DSP_GETCAPS:
-		return 0;
-
-	case SOUND_PCM_WRITE_RATE:
-		val = *(int *)arg;
-		if (val > 0) {
-			rate = val;
-			dac_audio_set_rate();
-		}
-		return put_user(rate, (int *)arg);
-
-	case SNDCTL_DSP_STEREO:
-		return put_user(0, (int *)arg);
-
-	case SOUND_PCM_WRITE_CHANNELS:
-		return put_user(1, (int *)arg);
-
-	case SNDCTL_DSP_SETDUPLEX:
-		return -EINVAL;
-
-	case SNDCTL_DSP_PROFILE:
-		return -EINVAL;
-
-	case SNDCTL_DSP_GETBLKSIZE:
-		return put_user(BUFFER_SIZE, (int *)arg);
-
-	case SNDCTL_DSP_SETFRAGMENT:
-		return 0;
-
-	default:
-		printk(KERN_ERR "sh_dac_audio: unimplemented ioctl=0x%x\n",
-		       cmd);
-		return -EINVAL;
-	}
-	return -EINVAL;
-}
-
-static long dac_audio_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
-{
-	int ret;
-
-	lock_kernel();
-	ret = dac_audio_ioctl(file, cmd, arg);
-	unlock_kernel();
-
-	return ret;
-}
-
-static ssize_t dac_audio_write(struct file *file, const char *buf, size_t count,
-			       loff_t * ppos)
-{
-	int free;
-	int nbytes;
-
-	if (!count) {
-		dac_audio_sync();
-		return 0;
-	}
-
-	free = buffer_begin - buffer_end;
-
-	if (free < 0)
-		free += BUFFER_SIZE;
-	if ((free == 0) && (empty))
-		free = BUFFER_SIZE;
-	if (count > free)
-		count = free;
-	if (buffer_begin > buffer_end) {
-		if (copy_from_user((void *)buffer_end, buf, count))
-			return -EFAULT;
-
-		buffer_end += count;
-	} else {
-		nbytes = data_buffer + BUFFER_SIZE - buffer_end;
-		if (nbytes > count) {
-			if (copy_from_user((void *)buffer_end, buf, count))
-				return -EFAULT;
-			buffer_end += count;
-		} else {
-			if (copy_from_user((void *)buffer_end, buf, nbytes))
-				return -EFAULT;
-			if (copy_from_user
-			    ((void *)data_buffer, buf + nbytes, count - nbytes))
-				return -EFAULT;
-			buffer_end = data_buffer + count - nbytes;
-		}
-	}
-
-	if (empty) {
-		empty = 0;
-		dac_audio_start_timer();
-	}
-
-	return count;
-}
-
-static ssize_t dac_audio_read(struct file *file, char *buf, size_t count,
-			      loff_t * ppos)
-{
-	return -EINVAL;
-}
-
-static int dac_audio_open(struct inode *inode, struct file *file)
-{
-	if (file->f_mode & FMODE_READ)
-		return -ENODEV;
-
-	lock_kernel();
-	if (in_use) {
-		unlock_kernel();
-		return -EBUSY;
-	}
-
-	in_use = 1;
-
-	dac_audio_start();
-	unlock_kernel();
-	return 0;
-}
-
-static int dac_audio_release(struct inode *inode, struct file *file)
-{
-	dac_audio_sync();
-	dac_audio_stop();
-	in_use = 0;
-
-	return 0;
-}
-
-const struct file_operations dac_audio_fops = {
-      .read =		dac_audio_read,
-      .write =		dac_audio_write,
-      .unlocked_ioctl =	dac_audio_unlocked_ioctl,
-      .open =		dac_audio_open,
-      .release =	dac_audio_release,
-};
-
-static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle)
-{
-	if (!empty) {
-		sh_dac_output(*buffer_begin, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
-		buffer_begin++;
-
-		if (buffer_begin == data_buffer + BUFFER_SIZE)
-			buffer_begin = data_buffer;
-		if (buffer_begin == buffer_end)
-			empty = 1;
-	}
-
-	if (!empty)
-		hrtimer_start(&hrtimer, wakeups_per_second, HRTIMER_MODE_REL);
-
-	return HRTIMER_NORESTART;
-}
-
-static int __init dac_audio_init(void)
-{
-	if ((device_major = register_sound_dsp(&dac_audio_fops, -1)) < 0) {
-		printk(KERN_ERR "Cannot register dsp device");
-		return device_major;
-	}
-
-	in_use = 0;
-
-	data_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
-	if (data_buffer == NULL)
-		return -ENOMEM;
-
-	dac_audio_reset();
-	rate = 8000;
-	dac_audio_set_rate();
-
-	/* Today: High Resolution Timer driven DAC playback.
-	 * The timer callback gets called once per sample. Ouch.
-	 *
-	 * Future: A much better approach would be to use the
-	 * SH7720 CMT+DMAC+DAC hardware combination like this:
-	 * - Program sample rate using CMT0 or CMT1
-	 * - Program DMAC to use CMT for timing and output to DAC
-	 * - Play sound using DMAC, let CPU sleep.
-	 * - While at it, rewrite this driver to use ALSA.
-	 */
-
-	hrtimer_init(&hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-	hrtimer.function = sh_dac_audio_timer;
-
-	return 0;
-}
-
-static void __exit dac_audio_exit(void)
-{
-	unregister_sound_dsp(device_major);
-	kfree((void *)data_buffer);
-}
-
-module_init(dac_audio_init);
-module_exit(dac_audio_exit);
-
-MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
-MODULE_DESCRIPTION("SH DAC sound driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c
index 07f803e..a5ab61e 100644
--- a/sound/oss/soundcard.c
+++ b/sound/oss/soundcard.c
@@ -40,7 +40,7 @@
 #include <linux/major.h>
 #include <linux/delay.h>
 #include <linux/proc_fs.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
 #include <linux/module.h>
 #include <linux/mm.h>
 #include <linux/device.h>
@@ -56,6 +56,7 @@
  * Table for permanently allocated memory (used when unloading the module)
  */
 void *          sound_mem_blocks[MAX_MEM_BLOCKS];
+static DEFINE_MUTEX(soundcard_mutex);
 int             sound_nblocks = 0;
 
 /* Persistent DMA buffers */
@@ -151,7 +152,7 @@
 	 *	big one anyway, we might as well bandage here..
 	 */
 	 
-	lock_kernel();
+	mutex_lock(&soundcard_mutex);
 	
 	DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count));
 	switch (dev & 0x0f) {
@@ -169,7 +170,7 @@
 	case SND_DEV_MIDIN:
 		ret = MIDIbuf_read(dev, file, buf, count);
 	}
-	unlock_kernel();
+	mutex_unlock(&soundcard_mutex);
 	return ret;
 }
 
@@ -178,7 +179,7 @@
 	int dev = iminor(file->f_path.dentry->d_inode);
 	int ret = -EINVAL;
 	
-	lock_kernel();
+	mutex_lock(&soundcard_mutex);
 	DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count));
 	switch (dev & 0x0f) {
 	case SND_DEV_SEQ:
@@ -196,7 +197,7 @@
 		ret =  MIDIbuf_write(dev, file, buf, count);
 		break;
 	}
-	unlock_kernel();
+	mutex_unlock(&soundcard_mutex);
 	return ret;
 }
 
@@ -210,7 +211,7 @@
 		printk(KERN_ERR "Invalid minor device %d\n", dev);
 		return -ENXIO;
 	}
-	lock_kernel();
+	mutex_lock(&soundcard_mutex);
 	switch (dev & 0x0f) {
 	case SND_DEV_CTL:
 		dev >>= 4;
@@ -247,7 +248,7 @@
 		retval = -ENXIO;
 	}
 
-	unlock_kernel();
+	mutex_unlock(&soundcard_mutex);
 	return 0;
 }
 
@@ -255,7 +256,7 @@
 {
 	int dev = iminor(inode);
 
-	lock_kernel();
+	mutex_lock(&soundcard_mutex);
 	DEB(printk("sound_release(dev=%d)\n", dev));
 	switch (dev & 0x0f) {
 	case SND_DEV_CTL:
@@ -280,7 +281,7 @@
 	default:
 		printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev);
 	}
-	unlock_kernel();
+	mutex_unlock(&soundcard_mutex);
 
 	return 0;
 }
@@ -354,7 +355,7 @@
 	if (cmd == OSS_GETVERSION)
 		return __put_user(SOUND_VERSION, (int __user *)p);
 	
-	lock_kernel();
+	mutex_lock(&soundcard_mutex);
 	if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 &&   /* Mixer ioctl */
 	    (dev & 0x0f) != SND_DEV_CTL) {              
 		dtype = dev & 0x0f;
@@ -369,7 +370,7 @@
 			ret = sound_mixer_ioctl(dev >> 4, cmd, p);
 			break;
 		}
-		unlock_kernel();
+		mutex_unlock(&soundcard_mutex);
 		return ret;
 	}
 
@@ -399,7 +400,7 @@
 		break;
 
 	}
-	unlock_kernel();
+	mutex_unlock(&soundcard_mutex);
 	return ret;
 }
 
@@ -439,35 +440,35 @@
 		printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n");
 		return -EINVAL;
 	}
-	lock_kernel();
+	mutex_lock(&soundcard_mutex);
 	if (vma->vm_flags & VM_WRITE)	/* Map write and read/write to the output buf */
 		dmap = audio_devs[dev]->dmap_out;
 	else if (vma->vm_flags & VM_READ)
 		dmap = audio_devs[dev]->dmap_in;
 	else {
 		printk(KERN_ERR "Sound: Undefined mmap() access\n");
-		unlock_kernel();
+		mutex_unlock(&soundcard_mutex);
 		return -EINVAL;
 	}
 
 	if (dmap == NULL) {
 		printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n");
-		unlock_kernel();
+		mutex_unlock(&soundcard_mutex);
 		return -EIO;
 	}
 	if (dmap->raw_buf == NULL) {
 		printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n");
-		unlock_kernel();
+		mutex_unlock(&soundcard_mutex);
 		return -EIO;
 	}
 	if (dmap->mapping_flags) {
 		printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n");
-		unlock_kernel();
+		mutex_unlock(&soundcard_mutex);
 		return -EIO;
 	}
 	if (vma->vm_pgoff != 0) {
 		printk(KERN_ERR "Sound: mmap() offset must be 0.\n");
-		unlock_kernel();
+		mutex_unlock(&soundcard_mutex);
 		return -EINVAL;
 	}
 	size = vma->vm_end - vma->vm_start;
@@ -478,7 +479,7 @@
 	if (remap_pfn_range(vma, vma->vm_start,
 			virt_to_phys(dmap->raw_buf) >> PAGE_SHIFT,
 			vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
-		unlock_kernel();
+		mutex_unlock(&soundcard_mutex);
 		return -EAGAIN;
 	}
 
@@ -490,7 +491,7 @@
 	memset(dmap->raw_buf,
 	       dmap->neutral_byte,
 	       dmap->bytes_in_use);
-	unlock_kernel();
+	mutex_unlock(&soundcard_mutex);
 	return 0;
 }
 
diff --git a/sound/oss/swarm_cs4297a.c b/sound/oss/swarm_cs4297a.c
index b15840a..44357d8 100644
--- a/sound/oss/swarm_cs4297a.c
+++ b/sound/oss/swarm_cs4297a.c
@@ -68,7 +68,6 @@
 #include <linux/delay.h>
 #include <linux/sound.h>
 #include <linux/slab.h>
-#include <linux/smp_lock.h>
 #include <linux/soundcard.h>
 #include <linux/ac97_codec.h>
 #include <linux/pci.h>
@@ -94,6 +93,7 @@
 
 struct cs4297a_state;
 
+static DEFINE_MUTEX(swarm_cs4297a_mutex);
 static void stop_dac(struct cs4297a_state *s);
 static void stop_adc(struct cs4297a_state *s);
 static void start_dac(struct cs4297a_state *s);
@@ -1535,7 +1535,7 @@
 	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
 		  printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()+\n"));
 
-	lock_kernel();
+	mutex_lock(&swarm_cs4297a_mutex);
 	list_for_each(entry, &cs4297a_devs)
 	{
 		s = list_entry(entry, struct cs4297a_state, list);
@@ -1547,7 +1547,7 @@
 		CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2,
 			printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- -ENODEV\n"));
 
-		unlock_kernel();
+		mutex_unlock(&swarm_cs4297a_mutex);
 		return -ENODEV;
 	}
 	VALIDATE_STATE(s);
@@ -1555,7 +1555,7 @@
 
 	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
 		  printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- 0\n"));
-	unlock_kernel();
+	mutex_unlock(&swarm_cs4297a_mutex);
 
 	return nonseekable_open(inode, file);
 }
@@ -1575,10 +1575,10 @@
 			       unsigned int cmd, unsigned long arg)
 {
 	int ret;
-	lock_kernel();
+	mutex_lock(&swarm_cs4297a_mutex);
 	ret = mixer_ioctl((struct cs4297a_state *) file->private_data, cmd,
 			   arg);
-	unlock_kernel();
+	mutex_unlock(&swarm_cs4297a_mutex);
 	return ret;
 }
 
@@ -2350,9 +2350,9 @@
 {
 	int ret;
 
-	lock_kernel();
+	mutex_lock(&swarm_cs4297a_mutex);
 	ret = cs4297a_ioctl(file, cmd, arg);
-	unlock_kernel();
+	mutex_unlock(&swarm_cs4297a_mutex);
 
 	return ret;
 }
@@ -2509,9 +2509,9 @@
 {
 	int ret;
 
-	lock_kernel();
+	mutex_lock(&swarm_cs4297a_mutex);
 	ret = cs4297a_open(inode, file);
-	unlock_kernel();
+	mutex_unlock(&swarm_cs4297a_mutex);
 
 	return ret;
 }
diff --git a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c
index 8cd73cd..643f111 100644
--- a/sound/oss/vwsnd.c
+++ b/sound/oss/vwsnd.c
@@ -145,7 +145,6 @@
 #include <linux/init.h>
 
 #include <linux/spinlock.h>
-#include <linux/smp_lock.h>
 #include <linux/wait.h>
 #include <linux/interrupt.h>
 #include <linux/mutex.h>
@@ -160,6 +159,7 @@
 
 #ifdef VWSND_DEBUG
 
+static DEFINE_MUTEX(vwsnd_mutex);
 static int shut_up = 1;
 
 /*
@@ -2891,11 +2891,11 @@
 	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
 	int ret;
 
-	lock_kernel();
+	mutex_lock(&vwsnd_mutex);
 	mutex_lock(&devc->io_mutex);
 	ret = vwsnd_audio_do_ioctl(file, cmd, arg);
 	mutex_unlock(&devc->io_mutex);
-	unlock_kernel();
+	mutex_unlock(&vwsnd_mutex);
 
 	return ret;
 }
@@ -2922,7 +2922,7 @@
 
 	DBGE("(inode=0x%p, file=0x%p)\n", inode, file);
 
-	lock_kernel();
+	mutex_lock(&vwsnd_mutex);
 	INC_USE_COUNT;
 	for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
 		if ((devc->audio_minor & ~0x0F) == (minor & ~0x0F))
@@ -2930,7 +2930,7 @@
 
 	if (devc == NULL) {
 		DEC_USE_COUNT;
-		unlock_kernel();
+		mutex_unlock(&vwsnd_mutex);
 		return -ENODEV;
 	}
 
@@ -2939,13 +2939,13 @@
 		mutex_unlock(&devc->open_mutex);
 		if (file->f_flags & O_NONBLOCK) {
 			DEC_USE_COUNT;
-			unlock_kernel();
+			mutex_unlock(&vwsnd_mutex);
 			return -EBUSY;
 		}
 		interruptible_sleep_on(&devc->open_wait);
 		if (signal_pending(current)) {
 			DEC_USE_COUNT;
-			unlock_kernel();
+			mutex_unlock(&vwsnd_mutex);
 			return -ERESTARTSYS;
 		}
 		mutex_lock(&devc->open_mutex);
@@ -2998,7 +2998,7 @@
 
 	file->private_data = devc;
 	DBGRV();
-	unlock_kernel();
+	mutex_unlock(&vwsnd_mutex);
 	return 0;
 }
 
@@ -3012,7 +3012,7 @@
 	vwsnd_port_t *wport = NULL, *rport = NULL;
 	int err = 0;
 
-	lock_kernel();
+	mutex_lock(&vwsnd_mutex);
 	mutex_lock(&devc->io_mutex);
 	{
 		DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
@@ -3040,7 +3040,7 @@
 	wake_up(&devc->open_wait);
 	DEC_USE_COUNT;
 	DBGR();
-	unlock_kernel();
+	mutex_unlock(&vwsnd_mutex);
 	return err;
 }
 
@@ -3068,18 +3068,18 @@
 	DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
 
 	INC_USE_COUNT;
-	lock_kernel();
+	mutex_lock(&vwsnd_mutex);
 	for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
 		if (devc->mixer_minor == iminor(inode))
 			break;
 
 	if (devc == NULL) {
 		DEC_USE_COUNT;
-		unlock_kernel();
+		mutex_unlock(&vwsnd_mutex);
 		return -ENODEV;
 	}
 	file->private_data = devc;
-	unlock_kernel();
+	mutex_unlock(&vwsnd_mutex);
 	return 0;
 }
 
@@ -3223,7 +3223,7 @@
 
 	DBGEV("(devc=0x%p, cmd=0x%x, arg=0x%lx)\n", devc, cmd, arg);
 
-	lock_kernel();
+	mutex_lock(&vwsnd_mutex);
 	mutex_lock(&devc->mix_mutex);
 	{
 		if ((cmd & ~nrmask) == MIXER_READ(0))
@@ -3234,7 +3234,7 @@
 			retval = -EINVAL;
 	}
 	mutex_unlock(&devc->mix_mutex);
-	unlock_kernel();
+	mutex_unlock(&vwsnd_mutex);
 	return retval;
 }
 
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index e7a8cd0..12e3465 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -207,12 +207,12 @@
 
 config SND_OXYGEN_LIB
         tristate
-	select SND_PCM
-	select SND_MPU401_UART
 
 config SND_OXYGEN
 	tristate "C-Media 8788 (Oxygen)"
 	select SND_OXYGEN_LIB
+	select SND_PCM
+	select SND_MPU401_UART
 	help
 	  Say Y here to include support for sound cards based on the
 	  C-Media CMI8788 (Oxygen HD Audio) chip:
@@ -581,6 +581,8 @@
 config SND_HIFIER
 	tristate "TempoTec HiFier Fantasia"
 	select SND_OXYGEN_LIB
+	select SND_PCM
+	select SND_MPU401_UART
 	help
 	  Say Y here to include support for the MediaTek/TempoTec HiFier
 	  Fantasia sound card.
@@ -815,14 +817,17 @@
 	  will be called snd-via82xx-modem.
 
 config SND_VIRTUOSO
-	tristate "Asus Virtuoso 100/200 (Xonar)"
+	tristate "Asus Virtuoso 66/100/200 (Xonar)"
 	select SND_OXYGEN_LIB
+	select SND_PCM
+	select SND_MPU401_UART
+	select SND_JACK if INPUT=y || INPUT=SND
 	help
 	  Say Y here to include support for sound cards based on the
-	  Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X,
+	  Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS,
 	  Essence ST (Deluxe), and Essence STX.
-	  Support for the DS is experimental.
-	  Support for the HDAV1.3 (Deluxe) is very experimental.
+	  Support for the HDAV1.3 (Deluxe) is incomplete; for the
+	  HDAV1.3 Slim and Xense, missing.
 
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-virtuoso.
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 0a3d3d6..8e69620 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -1002,29 +1002,27 @@
 	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_ca0106_pcm *epcm = runtime->private_data;
-	snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
+	unsigned int ptr, prev_ptr;
 	int channel = epcm->channel_id;
+	int timeout = 10;
 
 	if (!epcm->running)
 		return 0;
 
-	ptr3 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
-	ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel);
-	ptr4 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
-	if (ptr3 != ptr4) ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel);
-	ptr2 = bytes_to_frames(runtime, ptr1);
-	ptr2+= (ptr4 >> 3) * runtime->period_size;
-	ptr=ptr2;
-        if (ptr >= runtime->buffer_size)
-		ptr -= runtime->buffer_size;
-	/*
-	printk(KERN_DEBUG "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
-	       "buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n",
-	       ptr1, ptr2, ptr, (int)runtime->buffer_size,
-	       (int)runtime->period_size, (int)runtime->frame_bits,
-	       (int)runtime->rate);
-	*/
-	return ptr;
+	prev_ptr = -1;
+	do {
+		ptr = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+		ptr = (ptr >> 3) * runtime->period_size;
+		ptr += bytes_to_frames(runtime,
+			snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel));
+		if (ptr >= runtime->buffer_size)
+			ptr -= runtime->buffer_size;
+		if (prev_ptr == ptr)
+			return ptr;
+		prev_ptr = ptr;
+	} while (--timeout);
+	snd_printk(KERN_WARNING "ca0106: unstable DMA pointer!\n");
+	return 0;
 }
 
 /* pointer_capture callback */
diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
index 8578c70..bab5648 100644
--- a/sound/pci/emu10k1/emumpu401.c
+++ b/sound/pci/emu10k1/emumpu401.c
@@ -321,7 +321,7 @@
 
 static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi)
 {
-	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)rmidi->private_data;
+	struct snd_emu10k1_midi *midi = rmidi->private_data;
 	midi->interrupt = NULL;
 	midi->rmidi = NULL;
 }
diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
index d216362..712c171 100644
--- a/sound/pci/ice1712/delta.c
+++ b/sound/pci/ice1712/delta.c
@@ -563,6 +563,7 @@
 	case ICE1712_SUBDEVICE_DELTA1010E:
 	case ICE1712_SUBDEVICE_DELTA1010LT:
 	case ICE1712_SUBDEVICE_MEDIASTATION:
+	case ICE1712_SUBDEVICE_EDIROLDA2496:
 		ice->num_total_dacs = 8;
 		ice->num_total_adcs = 8;
 		break;
@@ -635,6 +636,7 @@
 		err = snd_ice1712_akm4xxx_init(ak, &akm_delta410, &akm_delta410_priv, ice);
 		break;
 	case ICE1712_SUBDEVICE_DELTA1010LT:
+	case ICE1712_SUBDEVICE_EDIROLDA2496:
 		err = snd_ice1712_akm4xxx_init(ak, &akm_delta1010lt, &akm_delta1010lt_priv, ice);
 		break;
 	case ICE1712_SUBDEVICE_DELTA66:
@@ -734,6 +736,7 @@
 	case ICE1712_SUBDEVICE_DELTA66:
 	case ICE1712_SUBDEVICE_VX442:
 	case ICE1712_SUBDEVICE_DELTA66E:
+	case ICE1712_SUBDEVICE_EDIROLDA2496:
 		err = snd_ice1712_akm4xxx_build_controls(ice);
 		if (err < 0)
 			return err;
@@ -813,5 +816,12 @@
 		.chip_init = snd_ice1712_delta_init,
 		.build_controls = snd_ice1712_delta_add_controls,
 	},
+	{
+		.subvendor = ICE1712_SUBDEVICE_EDIROLDA2496,
+		.name = "Edirol DA2496",
+		.model = "da2496",
+		.chip_init = snd_ice1712_delta_init,
+		.build_controls = snd_ice1712_delta_add_controls,
+	},
 	{ } /* terminator */
 };
diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h
index f7f14df..1a0ac6c 100644
--- a/sound/pci/ice1712/delta.h
+++ b/sound/pci/ice1712/delta.h
@@ -34,7 +34,8 @@
 		"{MidiMan M Audio,Delta 410},"\
 		"{MidiMan M Audio,Audiophile 24/96},"\
 		"{Digigram,VX442},"\
-		"{Lionstracs,Mediastation},"
+		"{Lionstracs,Mediastation},"\
+		"{Edirol,DA2496},"
 
 #define ICE1712_SUBDEVICE_DELTA1010	0x121430d6
 #define ICE1712_SUBDEVICE_DELTA1010E	0xff1430d6
@@ -47,6 +48,7 @@
 #define ICE1712_SUBDEVICE_DELTA1010LT	0x12143bd6
 #define ICE1712_SUBDEVICE_VX442		0x12143cd6
 #define ICE1712_SUBDEVICE_MEDIASTATION	0x694c0100
+#define ICE1712_SUBDEVICE_EDIROLDA2496	0xce164010
 
 /* entry point */
 extern struct snd_ice1712_card_info snd_ice1712_delta_cards[];
diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
index 6bc3f91..cdb873f 100644
--- a/sound/pci/ice1712/pontis.c
+++ b/sound/pci/ice1712/pontis.c
@@ -638,7 +638,7 @@
  */
 static void wm_proc_regs_write(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 {
-	struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+	struct snd_ice1712 *ice = entry->private_data;
 	char line[64];
 	unsigned int reg, val;
 	mutex_lock(&ice->gpio_mutex);
@@ -653,7 +653,7 @@
 
 static void wm_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 {
-	struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+	struct snd_ice1712 *ice = entry->private_data;
 	int reg, val;
 
 	mutex_lock(&ice->gpio_mutex);
@@ -676,7 +676,7 @@
 
 static void cs_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 {
-	struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+	struct snd_ice1712 *ice = entry->private_data;
 	int reg, val;
 
 	mutex_lock(&ice->gpio_mutex);
diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
index 2a8e5cd..e36ddb9 100644
--- a/sound/pci/ice1712/prodigy192.c
+++ b/sound/pci/ice1712/prodigy192.c
@@ -654,7 +654,7 @@
 static void stac9460_proc_regs_read(struct snd_info_entry *entry,
 		struct snd_info_buffer *buffer)
 {
-	struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+	struct snd_ice1712 *ice = entry->private_data;
 	int reg, val;
 	/* registers 0x0 - 0x14 */
 	for (reg = 0; reg <= 0x15; reg++) {
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index 6c0a11a..98a8eb3 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -79,6 +79,7 @@
 	{ OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF },
+	{ OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
@@ -505,7 +506,8 @@
 			 PLAYBACK_2_TO_AC97_1 |
 			 CAPTURE_0_FROM_I2S_1 |
 			 CAPTURE_1_FROM_SPDIF |
-			 CAPTURE_2_FROM_AC97_1,
+			 CAPTURE_2_FROM_AC97_1 |
+			 AC97_CD_INPUT,
 	.dac_channels = 8,
 	.dac_volume_min = 0,
 	.dac_volume_max = 255,
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index a3409ed..7d5222c 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -34,6 +34,7 @@
      /* CAPTURE_3_FROM_I2S_3		not implemented */
 #define MIDI_OUTPUT		0x0800
 #define MIDI_INPUT		0x1000
+#define AC97_CD_INPUT		0x2000
 
 enum {
 	CONTROL_SPDIF_PCM,
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index 7e93cf8..e5ebe56 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -308,25 +308,46 @@
 	}
 }
 
-static void pci_bridge_magic(void)
+static void configure_pcie_bridge(struct pci_dev *pci)
 {
-	struct pci_dev *pci = NULL;
+	enum { PEX811X, PI7C9X110 };
+	static const struct pci_device_id bridge_ids[] = {
+		{ PCI_VDEVICE(PLX, 0x8111), .driver_data = PEX811X },
+		{ PCI_VDEVICE(PLX, 0x8112), .driver_data = PEX811X },
+		{ PCI_DEVICE(0x12d8, 0xe110), .driver_data = PI7C9X110 },
+		{ }
+	};
+	struct pci_dev *bridge;
+	const struct pci_device_id *id;
 	u32 tmp;
 
-	for (;;) {
-		/* If there is any Pericom PI7C9X110 PCI-E/PCI bridge ... */
-		pci = pci_get_device(0x12d8, 0xe110, pci);
-		if (!pci)
-			break;
-		/*
-		 * ... configure its secondary internal arbiter to park to
-		 * the secondary port, instead of to the last master.
-		 */
-		if (!pci_read_config_dword(pci, 0x40, &tmp)) {
-			tmp |= 1;
-			pci_write_config_dword(pci, 0x40, tmp);
-		}
-		/* Why?  Try asking C-Media. */
+	if (!pci->bus || !pci->bus->self)
+		return;
+	bridge = pci->bus->self;
+
+	id = pci_match_id(bridge_ids, bridge);
+	if (!id)
+		return;
+
+	switch (id->driver_data) {
+	case PEX811X:	/* PLX PEX8111/PEX8112 PCIe/PCI bridge */
+		pci_read_config_dword(bridge, 0x48, &tmp);
+		tmp |= 1;	/* enable blind prefetching */
+		tmp |= 1 << 11;	/* enable beacon generation */
+		pci_write_config_dword(bridge, 0x48, tmp);
+
+		pci_write_config_dword(bridge, 0x84, 0x0c);
+		pci_read_config_dword(bridge, 0x88, &tmp);
+		tmp &= ~(7 << 27);
+		tmp |= 2 << 27;	/* set prefetch size to 128 bytes */
+		pci_write_config_dword(bridge, 0x88, tmp);
+		break;
+
+	case PI7C9X110:	/* Pericom PI7C9X110 PCIe/PCI bridge */
+		pci_read_config_dword(bridge, 0x40, &tmp);
+		tmp |= 1;	/* park the PCI arbiter to the sound chip */
+		pci_write_config_dword(bridge, 0x40, tmp);
+		break;
 	}
 }
 
@@ -613,7 +634,7 @@
 	snd_card_set_dev(card, &pci->dev);
 	card->private_free = oxygen_card_free;
 
-	pci_bridge_magic();
+	configure_pcie_bridge(pci);
 	oxygen_init(chip);
 	chip->model.init(chip);
 
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
index f375b8a..2849b36 100644
--- a/sound/pci/oxygen/oxygen_mixer.c
+++ b/sound/pci/oxygen/oxygen_mixer.c
@@ -708,7 +708,7 @@
 		.private_value = ((codec) << 24) | ((stereo) << 16) | (index), \
 	}
 
-static DECLARE_TLV_DB_SCALE(monitor_db_scale, -1000, 1000, 0);
+static DECLARE_TLV_DB_SCALE(monitor_db_scale, -600, 600, 0);
 static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0);
 static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0);
 
@@ -972,6 +972,9 @@
 		if (!strcmp(template.name, "Stereo Upmixing") &&
 		    chip->model.dac_channels == 2)
 			continue;
+		if (!strncmp(template.name, "CD Capture ", 11) &&
+		    !(chip->model.device_config & AC97_CD_INPUT))
+			continue;
 		if (!strcmp(template.name, "Master Playback Volume") &&
 		    chip->model.dac_tlv) {
 			template.tlv.p = chip->model.dac_tlv;
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
index 9dff695..8146674 100644
--- a/sound/pci/oxygen/oxygen_pcm.c
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -56,8 +56,8 @@
 	.channels_max = 2,
 	.buffer_bytes_max = BUFFER_BYTES_MAX,
 	.period_bytes_min = PERIOD_BYTES_MIN,
-	.period_bytes_max = BUFFER_BYTES_MAX / 2,
-	.periods_min = 2,
+	.period_bytes_max = BUFFER_BYTES_MAX,
+	.periods_min = 1,
 	.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
 };
 static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
@@ -82,8 +82,8 @@
 	.channels_max = 8,
 	.buffer_bytes_max = BUFFER_BYTES_MAX_MULTICH,
 	.period_bytes_min = PERIOD_BYTES_MIN,
-	.period_bytes_max = BUFFER_BYTES_MAX_MULTICH / 2,
-	.periods_min = 2,
+	.period_bytes_max = BUFFER_BYTES_MAX_MULTICH,
+	.periods_min = 1,
 	.periods_max = BUFFER_BYTES_MAX_MULTICH / PERIOD_BYTES_MIN,
 };
 static const struct snd_pcm_hardware oxygen_ac97_hardware = {
@@ -100,8 +100,8 @@
 	.channels_max = 2,
 	.buffer_bytes_max = BUFFER_BYTES_MAX,
 	.period_bytes_min = PERIOD_BYTES_MIN,
-	.period_bytes_max = BUFFER_BYTES_MAX / 2,
-	.periods_min = 2,
+	.period_bytes_max = BUFFER_BYTES_MAX,
+	.periods_min = 1,
 	.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
 };
 
diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h
index 72de159..4dcd41b 100644
--- a/sound/pci/oxygen/oxygen_regs.h
+++ b/sound/pci/oxygen/oxygen_regs.h
@@ -436,13 +436,15 @@
 /* OXYGEN_CHANNEL_* */
 
 #define OXYGEN_CODEC_VERSION		0xe4
-#define  OXYGEN_XCID_MASK		0x07
+#define  OXYGEN_CODEC_ID_MASK		0x07
 
 #define OXYGEN_REVISION			0xe6
-#define  OXYGEN_REVISION_XPKGID_MASK	0x0007
+#define  OXYGEN_PACKAGE_ID_MASK		0x0007
+#define  OXYGEN_PACKAGE_ID_8786		0x0004
+#define  OXYGEN_PACKAGE_ID_8787		0x0006
+#define  OXYGEN_PACKAGE_ID_8788		0x0007
 #define  OXYGEN_REVISION_MASK		0xfff8
-#define  OXYGEN_REVISION_2		0x0008	/* bit flag */
-#define  OXYGEN_REVISION_8787		0x0014	/* 8 bits */
+#define  OXYGEN_REVISION_2		0x0008
 
 #define OXYGEN_OFFSIN_48K		0xe8
 #define OXYGEN_OFFSBASE_48K		0xe9
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 06c863e..469010a 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -25,9 +25,9 @@
 #include "xonar.h"
 
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
-MODULE_DESCRIPTION("Asus AVx00 driver");
+MODULE_DESCRIPTION("Asus Virtuoso driver");
 MODULE_LICENSE("GPL v2");
-MODULE_SUPPORTED_DEVICE("{{Asus,AV100},{Asus,AV200}}");
+MODULE_SUPPORTED_DEVICE("{{Asus,AV66},{Asus,AV100},{Asus,AV200}}");
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -49,6 +49,7 @@
 	{ OXYGEN_PCI_SUBID(0x1043, 0x834f) },
 	{ OXYGEN_PCI_SUBID(0x1043, 0x835c) },
 	{ OXYGEN_PCI_SUBID(0x1043, 0x835d) },
+	{ OXYGEN_PCI_SUBID(0x1043, 0x835e) },
 	{ OXYGEN_PCI_SUBID(0x1043, 0x838e) },
 	{ OXYGEN_PCI_SUBID_BROKEN_EEPROM },
 	{ }
diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c
index 7c4986b..aa27c310 100644
--- a/sound/pci/oxygen/xonar_cs43xx.c
+++ b/sound/pci/oxygen/xonar_cs43xx.c
@@ -367,13 +367,6 @@
 
 static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0);
 
-static int xonar_d1_control_filter(struct snd_kcontrol_new *template)
-{
-	if (!strncmp(template->name, "CD Capture ", 11))
-		return 1; /* no CD input */
-	return 0;
-}
-
 static int xonar_d1_mixer_init(struct oxygen *chip)
 {
 	int err;
@@ -391,7 +384,6 @@
 	.longname = "Asus Virtuoso 100",
 	.chip = "AV200",
 	.init = xonar_d1_init,
-	.control_filter = xonar_d1_control_filter,
 	.mixer_init = xonar_d1_mixer_init,
 	.cleanup = xonar_d1_cleanup,
 	.suspend = xonar_d1_suspend,
diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c
index ba18fb5..d491fd6 100644
--- a/sound/pci/oxygen/xonar_pcm179x.c
+++ b/sound/pci/oxygen/xonar_pcm179x.c
@@ -132,6 +132,18 @@
  * GPIO 5 <- 0
  */
 
+/*
+ * Xonar HDAV1.3 Slim
+ * ------------------
+ *
+ * CMI8788:
+ *
+ * GPIO 1 -> enable output
+ *
+ * TXD -> HDMI controller
+ * RXD <- HDMI controller
+ */
+
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/mutex.h>
@@ -362,7 +374,6 @@
 {
 	struct xonar_pcm179x *data = chip->model_data;
 
-	data->generic.anti_pop_delay = 100;
 	data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE;
 	data->dacs = chip->model.private_data ? 4 : 1;
 	data->hp_gain_offset = 2*-18;
@@ -408,6 +419,7 @@
 {
 	struct xonar_pcm179x *data = chip->model_data;
 
+	data->generic.anti_pop_delay = 100;
 	data->has_cs2000 = 1;
 	data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1;
 
@@ -428,6 +440,7 @@
 	struct xonar_pcm179x *data = chip->model_data;
 
 	xonar_st_init_i2c(chip);
+	data->generic.anti_pop_delay = 800;
 	data->generic.ext_power_reg = OXYGEN_GPI_DATA;
 	data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
 	data->generic.ext_power_bit = GPI_EXT_POWER;
@@ -915,13 +928,6 @@
 	return 0;
 }
 
-static int xonar_st_control_filter(struct snd_kcontrol_new *template)
-{
-	if (!strncmp(template->name, "CD Capture ", 11))
-		return 1; /* no CD input */
-	return 0;
-}
-
 static int add_pcm1796_controls(struct oxygen *chip)
 {
 	int err;
@@ -991,7 +997,8 @@
 			 CAPTURE_0_FROM_I2S_2 |
 			 CAPTURE_1_FROM_SPDIF |
 			 MIDI_OUTPUT |
-			 MIDI_INPUT,
+			 MIDI_INPUT |
+			 AC97_CD_INPUT,
 	.dac_channels = 8,
 	.dac_volume_min = 255 - 2*60,
 	.dac_volume_max = 255,
@@ -1037,7 +1044,6 @@
 	.longname = "Asus Virtuoso 100",
 	.chip = "AV200",
 	.init = xonar_st_init,
-	.control_filter = xonar_st_control_filter,
 	.mixer_init = xonar_st_mixer_init,
 	.cleanup = xonar_st_cleanup,
 	.suspend = xonar_st_suspend,
@@ -1108,6 +1114,9 @@
 		chip->model.resume = xonar_stx_resume;
 		chip->model.set_dac_params = set_pcm1796_params;
 		break;
+	case 0x835e:
+		snd_printk(KERN_ERR "the HDAV1.3 Slim is not supported\n");
+		return -ENODEV;
 	default:
 		return -EINVAL;
 	}
diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c
index b82c1cf..200f760 100644
--- a/sound/pci/oxygen/xonar_wm87x6.c
+++ b/sound/pci/oxygen/xonar_wm87x6.c
@@ -25,16 +25,24 @@
  * SPI 0 -> WM8766 (surround, center/LFE, back)
  * SPI 1 -> WM8776 (front, input)
  *
- * GPIO 4 <- headphone detect
- * GPIO 6 -> route input jack to input 1/2 (1/0)
- * GPIO 7 -> enable output to speakers
- * GPIO 8 -> enable output to speakers
+ * GPIO 4 <- headphone detect, 0 = plugged
+ * GPIO 6 -> route input jack to mic-in (0) or line-in (1)
+ * GPIO 7 -> enable output to front L/R speaker channels
+ * GPIO 8 -> enable output to other speaker channels and front panel headphone
+ *
+ * WM8766:
+ *
+ * input 1 <- line
+ * input 2 <- mic
+ * input 3 <- front mic
+ * input 4 <- aux
  */
 
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <sound/control.h>
 #include <sound/core.h>
+#include <sound/jack.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/tlv.h>
@@ -44,7 +52,8 @@
 
 #define GPIO_DS_HP_DETECT	0x0010
 #define GPIO_DS_INPUT_ROUTE	0x0040
-#define GPIO_DS_OUTPUT_ENABLE	0x0180
+#define GPIO_DS_OUTPUT_FRONTLR	0x0080
+#define GPIO_DS_OUTPUT_ENABLE	0x0100
 
 #define LC_CONTROL_LIMITER	0x40000000
 #define LC_CONTROL_ALC		0x20000000
@@ -56,6 +65,7 @@
 	struct snd_kcontrol *line_adcmux_control;
 	struct snd_kcontrol *mic_adcmux_control;
 	struct snd_kcontrol *lc_controls[13];
+	struct snd_jack *hp_jack;
 };
 
 static void wm8776_write(struct oxygen *chip,
@@ -97,8 +107,12 @@
 			 (0 << OXYGEN_SPI_CODEC_SHIFT) |
 			 OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
 			 (reg << 9) | value);
-	if (reg < ARRAY_SIZE(data->wm8766_regs))
+	if (reg < ARRAY_SIZE(data->wm8766_regs)) {
+		if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) ||
+		    (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA))
+			value &= ~WM8766_UPDATE;
 		data->wm8766_regs[reg] = value;
+	}
 }
 
 static void wm8766_write_cached(struct oxygen *chip,
@@ -107,12 +121,8 @@
 	struct xonar_wm87x6 *data = chip->model_data;
 
 	if (reg >= ARRAY_SIZE(data->wm8766_regs) ||
-	    value != data->wm8766_regs[reg]) {
-		if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) ||
-		    (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA))
-			value &= ~WM8766_UPDATE;
+	    value != data->wm8766_regs[reg])
 		wm8766_write(chip, reg, value);
-	}
 }
 
 static void wm8776_registers_init(struct oxygen *chip)
@@ -141,7 +151,10 @@
 
 static void wm8766_registers_init(struct oxygen *chip)
 {
+	struct xonar_wm87x6 *data = chip->model_data;
+
 	wm8766_write(chip, WM8766_RESET, 0);
+	wm8766_write(chip, WM8766_DAC_CTRL, data->wm8766_regs[WM8766_DAC_CTRL]);
 	wm8766_write(chip, WM8766_INT_CTRL, WM8766_FMT_LJUST | WM8766_IWL_24);
 	wm8766_write(chip, WM8766_DAC_CTRL2,
 		     WM8766_ZCD | (chip->dac_mute ? WM8766_DMUTE_MASK : 0));
@@ -170,6 +183,40 @@
 	wm8776_registers_init(chip);
 }
 
+static void wm8766_init(struct oxygen *chip)
+{
+	struct xonar_wm87x6 *data = chip->model_data;
+
+	data->wm8766_regs[WM8766_DAC_CTRL] =
+		WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT;
+	wm8766_registers_init(chip);
+}
+
+static void xonar_ds_handle_hp_jack(struct oxygen *chip)
+{
+	struct xonar_wm87x6 *data = chip->model_data;
+	bool hp_plugged;
+	unsigned int reg;
+
+	mutex_lock(&chip->mutex);
+
+	hp_plugged = !(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
+		       GPIO_DS_HP_DETECT);
+
+	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+			      hp_plugged ? 0 : GPIO_DS_OUTPUT_FRONTLR,
+			      GPIO_DS_OUTPUT_FRONTLR);
+
+	reg = data->wm8766_regs[WM8766_DAC_CTRL] & ~WM8766_MUTEALL;
+	if (hp_plugged)
+		reg |= WM8766_MUTEALL;
+	wm8766_write_cached(chip, WM8766_DAC_CTRL, reg);
+
+	snd_jack_report(data->hp_jack, hp_plugged ? SND_JACK_HEADPHONE : 0);
+
+	mutex_unlock(&chip->mutex);
+}
+
 static void xonar_ds_init(struct oxygen *chip)
 {
 	struct xonar_wm87x6 *data = chip->model_data;
@@ -178,16 +225,22 @@
 	data->generic.output_enable_bit = GPIO_DS_OUTPUT_ENABLE;
 
 	wm8776_init(chip);
-	wm8766_registers_init(chip);
+	wm8766_init(chip);
 
-	oxygen_write16_masked(chip, OXYGEN_GPIO_CONTROL, GPIO_DS_INPUT_ROUTE,
-			      GPIO_DS_HP_DETECT | GPIO_DS_INPUT_ROUTE);
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+			  GPIO_DS_INPUT_ROUTE | GPIO_DS_OUTPUT_FRONTLR);
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+			    GPIO_DS_HP_DETECT);
 	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DS_INPUT_ROUTE);
 	oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, GPIO_DS_HP_DETECT);
 	chip->interrupt_mask |= OXYGEN_INT_GPIO;
 
 	xonar_enable_output(chip);
 
+	snd_jack_new(chip->card, "Headphone",
+		     SND_JACK_HEADPHONE, &data->hp_jack);
+	xonar_ds_handle_hp_jack(chip);
+
 	snd_component_add(chip->card, "WM8776");
 	snd_component_add(chip->card, "WM8766");
 }
@@ -208,6 +261,7 @@
 	wm8776_registers_init(chip);
 	wm8766_registers_init(chip);
 	xonar_enable_output(chip);
+	xonar_ds_handle_hp_jack(chip);
 }
 
 static void wm8776_adc_hardware_filter(unsigned int channel,
@@ -323,12 +377,27 @@
 			    (chip->dac_mute ? WM8766_DMUTE_MASK : 0));
 }
 
+static void update_wm8766_center_lfe_mix(struct oxygen *chip, bool mixed)
+{
+	struct xonar_wm87x6 *data = chip->model_data;
+	unsigned int reg;
+
+	/*
+	 * The WM8766 can mix left and right channels, but this setting
+	 * applies to all three stereo pairs.
+	 */
+	reg = data->wm8766_regs[WM8766_DAC_CTRL] &
+		~(WM8766_PL_LEFT_MASK | WM8766_PL_RIGHT_MASK);
+	if (mixed)
+		reg |= WM8766_PL_LEFT_LRMIX | WM8766_PL_RIGHT_LRMIX;
+	else
+		reg |= WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT;
+	wm8766_write_cached(chip, WM8766_DAC_CTRL, reg);
+}
+
 static void xonar_ds_gpio_changed(struct oxygen *chip)
 {
-	u16 bits;
-
-	bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
-	snd_printk(KERN_INFO "HP detect: %d\n", !!(bits & GPIO_DS_HP_DETECT));
+	xonar_ds_handle_hp_jack(chip);
 }
 
 static int wm8776_bit_switch_get(struct snd_kcontrol *ctl,
@@ -896,7 +965,10 @@
 		.put = wm8776_input_mux_put,
 		.private_value = 1 << 1,
 	},
-	WM8776_BIT_SWITCH("Aux", WM8776_ADCMUX, 1 << 2, 0, 0),
+	WM8776_BIT_SWITCH("Front Mic Capture Switch",
+			  WM8776_ADCMUX, 1 << 2, 0, 0),
+	WM8776_BIT_SWITCH("Aux Capture Switch",
+			  WM8776_ADCMUX, 1 << 3, 0, 0),
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "ADC Filter Capture Enum",
@@ -956,13 +1028,6 @@
 				LC_CONTROL_ALC, wm8776_ngth_db_scale),
 };
 
-static int xonar_ds_control_filter(struct snd_kcontrol_new *template)
-{
-	if (!strncmp(template->name, "CD Capture ", 11))
-		return 1; /* no CD input */
-	return 0;
-}
-
 static int xonar_ds_mixer_init(struct oxygen *chip)
 {
 	struct xonar_wm87x6 *data = chip->model_data;
@@ -999,10 +1064,9 @@
 
 static const struct oxygen_model model_xonar_ds = {
 	.shortname = "Xonar DS",
-	.longname = "Asus Virtuoso 200",
+	.longname = "Asus Virtuoso 66",
 	.chip = "AV200",
 	.init = xonar_ds_init,
-	.control_filter = xonar_ds_control_filter,
 	.mixer_init = xonar_ds_mixer_init,
 	.cleanup = xonar_ds_cleanup,
 	.suspend = xonar_ds_suspend,
@@ -1013,6 +1077,7 @@
 	.set_adc_params = set_wm8776_adc_params,
 	.update_dac_volume = update_wm87x6_volume,
 	.update_dac_mute = update_wm87x6_mute,
+	.update_center_lfe_mix = update_wm8766_center_lfe_mix,
 	.gpio_changed = xonar_ds_gpio_changed,
 	.dac_tlv = wm87x6_dac_db_scale,
 	.model_data_size = sizeof(struct xonar_wm87x6),
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index d19dc05..d5f5b44 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -1527,14 +1527,14 @@
 static void
 snd_rme96_free_spdif_pcm(struct snd_pcm *pcm)
 {
-	struct rme96 *rme96 = (struct rme96 *) pcm->private_data;
+	struct rme96 *rme96 = pcm->private_data;
 	rme96->spdif_pcm = NULL;
 }
 
 static void
 snd_rme96_free_adat_pcm(struct snd_pcm *pcm)
 {
-	struct rme96 *rme96 = (struct rme96 *) pcm->private_data;
+	struct rme96 *rme96 = pcm->private_data;
 	rme96->adat_pcm = NULL;
 }
 
@@ -1661,7 +1661,7 @@
 snd_rme96_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 {
 	int n;
-	struct rme96 *rme96 = (struct rme96 *)entry->private_data;
+	struct rme96 *rme96 = entry->private_data;
 	
 	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
 
@@ -2348,7 +2348,7 @@
 	if (err < 0)
 		return err;
 	card->private_free = snd_rme96_card_free;
-	rme96 = (struct rme96 *)card->private_data;	
+	rme96 = card->private_data;
 	rme96->card = card;
 	rme96->pci = pci;
 	snd_card_set_dev(card, &pci->dev);
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index d6fa7bf..0b720cf 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -3284,7 +3284,7 @@
 static void
 snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 {
-	struct hdsp *hdsp = (struct hdsp *) entry->private_data;
+	struct hdsp *hdsp = entry->private_data;
 	unsigned int status;
 	unsigned int status2;
 	char *pref_sync_ref;
@@ -4566,7 +4566,7 @@
 
 static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigned int cmd, unsigned long arg)
 {
-	struct hdsp *hdsp = (struct hdsp *)hw->private_data;
+	struct hdsp *hdsp = hw->private_data;
 	void __user *argp = (void __user *)arg;
 	int err;
 
@@ -5156,7 +5156,7 @@
 
 static void snd_hdsp_card_free(struct snd_card *card)
 {
-	struct hdsp *hdsp = (struct hdsp *) card->private_data;
+	struct hdsp *hdsp = card->private_data;
 
 	if (hdsp)
 		snd_hdsp_free(hdsp);
@@ -5182,7 +5182,7 @@
 	if (err < 0)
 		return err;
 
-	hdsp = (struct hdsp *) card->private_data;
+	hdsp = card->private_data;
 	card->private_free = snd_hdsp_card_free;
 	hdsp->dev = dev;
 	hdsp->pci = pci;
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 20afdf9..961d982 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -785,7 +785,7 @@
 	if (! mix->i2c.client)
 		return -ENODEV;
 	if (mix->capture_source)
-		mix->acs = mix->acs |= 2;
+		mix->acs |= 2;
 	else
 		mix->acs &= ~2;
 	return i2c_smbus_write_byte_data(mix->i2c.client, TAS_REG_ACS, mix->acs);
diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c
index ff0b2a8..5ae1eae 100644
--- a/sound/synth/emux/emux_hwdep.c
+++ b/sound/synth/emux/emux_hwdep.c
@@ -128,6 +128,9 @@
 	strcpy(hw->name, SNDRV_EMUX_HWDEP_NAME);
 	hw->iface = SNDRV_HWDEP_IFACE_EMUX_WAVETABLE;
 	hw->ops.ioctl = snd_emux_hwdep_ioctl;
+	/* The ioctl parameter types are compatible between 32- and
+	 * 64-bit architectures, so use the same function. */
+	hw->ops.ioctl_compat = snd_emux_hwdep_ioctl;
 	hw->exclusive = 1;
 	hw->private_data = emu;
 	if ((err = snd_card_register(emu->card)) < 0)
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 44d6d2e..112984f 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -65,6 +65,7 @@
 	    * Native Instruments Guitar Rig Session I/O
 	    * Native Instruments Guitar Rig mobile
 	    * Native Instruments Traktor Kontrol X1
+	    * Native Instruments Traktor Kontrol S4
 
 	   To compile this driver as a module, choose M here: the module
 	   will be called snd-usb-caiaq.
@@ -82,6 +83,7 @@
 	   * Native Instruments Kore Controller
 	   * Native Instruments Kore Controller 2
 	   * Native Instruments Audio Kontrol 1
+	   * Native Instruments Traktor Kontrol S4
 
 config SND_USB_US122L
 	tristate "Tascam US-122L USB driver"
diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c
index 4328cad..68b9747 100644
--- a/sound/usb/caiaq/audio.c
+++ b/sound/usb/caiaq/audio.c
@@ -111,7 +111,7 @@
 	memset(dev->sub_capture, 0, sizeof(dev->sub_capture));
 	dev->input_panic = 0;
 	dev->output_panic = 0;
-	dev->first_packet = 1;
+	dev->first_packet = 4;
 	dev->streaming = 1;
 	dev->warned = 0;
 
@@ -169,7 +169,7 @@
 }
 
 static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub,
-			     		struct snd_pcm_hw_params *hw_params)
+				       struct snd_pcm_hw_params *hw_params)
 {
 	debug("%s(%p)\n", __func__, sub);
 	return snd_pcm_lib_malloc_pages(sub, params_buffer_bytes(hw_params));
@@ -189,7 +189,7 @@
 #endif
 
 static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
-                                 48000, 64000, 88200, 96000, 176400, 192000 };
+				48000, 64000, 88200, 96000, 176400, 192000 };
 
 static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream)
 {
@@ -201,12 +201,39 @@
 	debug("%s(%p)\n", __func__, substream);
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		dev->period_out_count[index] = BYTES_PER_SAMPLE + 1;
-		dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1;
+		int out_pos;
+
+		switch (dev->spec.data_alignment) {
+		case 0:
+		case 2:
+			out_pos = BYTES_PER_SAMPLE + 1;
+			break;
+		case 3:
+		default:
+			out_pos = 0;
+			break;
+		}
+
+		dev->period_out_count[index] = out_pos;
+		dev->audio_out_buf_pos[index] = out_pos;
 	} else {
-		int in_pos = (dev->spec.data_alignment == 2) ? 0 : 2;
-		dev->period_in_count[index] = BYTES_PER_SAMPLE + in_pos;
-		dev->audio_in_buf_pos[index] = BYTES_PER_SAMPLE + in_pos;
+		int in_pos;
+
+		switch (dev->spec.data_alignment) {
+		case 0:
+			in_pos = BYTES_PER_SAMPLE + 2;
+			break;
+		case 2:
+			in_pos = BYTES_PER_SAMPLE;
+			break;
+		case 3:
+		default:
+			in_pos = 0;
+			break;
+		}
+
+		dev->period_in_count[index] = in_pos;
+		dev->audio_in_buf_pos[index] = in_pos;
 	}
 
 	if (dev->streaming)
@@ -221,7 +248,7 @@
 	snd_pcm_limit_hw_rates(runtime);
 
 	bytes_per_sample = BYTES_PER_SAMPLE;
-	if (dev->spec.data_alignment == 2)
+	if (dev->spec.data_alignment >= 2)
 		bytes_per_sample++;
 
 	bpp = ((runtime->rate / 8000) + CLOCK_DRIFT_TOLERANCE)
@@ -253,6 +280,8 @@
 {
 	struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub);
 
+	debug("%s(%p) cmd %d\n", __func__, sub, cmd);
+
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
@@ -402,6 +431,61 @@
 	}
 }
 
+static void read_in_urb_mode3(struct snd_usb_caiaqdev *dev,
+			      const struct urb *urb,
+			      const struct usb_iso_packet_descriptor *iso)
+{
+	unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
+	int stream, i;
+
+	/* paranoia check */
+	if (iso->actual_length % (BYTES_PER_SAMPLE_USB * CHANNELS_PER_STREAM))
+		return;
+
+	for (i = 0; i < iso->actual_length;) {
+		for (stream = 0; stream < dev->n_streams; stream++) {
+			struct snd_pcm_substream *sub = dev->sub_capture[stream];
+			char *audio_buf = NULL;
+			int c, n, sz = 0;
+
+			if (sub && !dev->input_panic) {
+				struct snd_pcm_runtime *rt = sub->runtime;
+				audio_buf = rt->dma_area;
+				sz = frames_to_bytes(rt, rt->buffer_size);
+			}
+
+			for (c = 0; c < CHANNELS_PER_STREAM; c++) {
+				/* 3 audio data bytes, followed by 1 check byte */
+				if (audio_buf) {
+					for (n = 0; n < BYTES_PER_SAMPLE; n++) {
+						audio_buf[dev->audio_in_buf_pos[stream]++] = usb_buf[i+n];
+
+						if (dev->audio_in_buf_pos[stream] == sz)
+							dev->audio_in_buf_pos[stream] = 0;
+					}
+
+					dev->period_in_count[stream] += BYTES_PER_SAMPLE;
+				}
+
+				i += BYTES_PER_SAMPLE;
+
+				if (usb_buf[i] != ((stream << 1) | c) &&
+				    !dev->first_packet) {
+					if (!dev->input_panic)
+						printk(" EXPECTED: %02x got %02x, c %d, stream %d, i %d\n",
+							((stream << 1) | c), usb_buf[i], c, stream, i);
+					dev->input_panic = 1;
+				}
+
+				i++;
+			}
+		}
+	}
+
+	if (dev->first_packet > 0)
+		dev->first_packet--;
+}
+
 static void read_in_urb(struct snd_usb_caiaqdev *dev,
 			const struct urb *urb,
 			const struct usb_iso_packet_descriptor *iso)
@@ -419,6 +503,9 @@
 	case 2:
 		read_in_urb_mode2(dev, urb, iso);
 		break;
+	case 3:
+		read_in_urb_mode3(dev, urb, iso);
+		break;
 	}
 
 	if ((dev->input_panic || dev->output_panic) && !dev->warned) {
@@ -429,9 +516,9 @@
 	}
 }
 
-static void fill_out_urb(struct snd_usb_caiaqdev *dev,
-			 struct urb *urb,
-			 const struct usb_iso_packet_descriptor *iso)
+static void fill_out_urb_mode_0(struct snd_usb_caiaqdev *dev,
+				struct urb *urb,
+				const struct usb_iso_packet_descriptor *iso)
 {
 	unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
 	struct snd_pcm_substream *sub;
@@ -457,9 +544,67 @@
 		/* fill in the check bytes */
 		if (dev->spec.data_alignment == 2 &&
 		    i % (dev->n_streams * BYTES_PER_SAMPLE_USB) ==
-		    	(dev->n_streams * CHANNELS_PER_STREAM))
-		    for (stream = 0; stream < dev->n_streams; stream++, i++)
-		    	usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i);
+		        (dev->n_streams * CHANNELS_PER_STREAM))
+			for (stream = 0; stream < dev->n_streams; stream++, i++)
+				usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i);
+	}
+}
+
+static void fill_out_urb_mode_3(struct snd_usb_caiaqdev *dev,
+				struct urb *urb,
+				const struct usb_iso_packet_descriptor *iso)
+{
+	unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
+	int stream, i;
+
+	for (i = 0; i < iso->length;) {
+		for (stream = 0; stream < dev->n_streams; stream++) {
+			struct snd_pcm_substream *sub = dev->sub_playback[stream];
+			char *audio_buf = NULL;
+			int c, n, sz = 0;
+
+			if (sub) {
+				struct snd_pcm_runtime *rt = sub->runtime;
+				audio_buf = rt->dma_area;
+				sz = frames_to_bytes(rt, rt->buffer_size);
+			}
+
+			for (c = 0; c < CHANNELS_PER_STREAM; c++) {
+				for (n = 0; n < BYTES_PER_SAMPLE; n++) {
+					if (audio_buf) {
+						usb_buf[i+n] = audio_buf[dev->audio_out_buf_pos[stream]++];
+
+						if (dev->audio_out_buf_pos[stream] == sz)
+							dev->audio_out_buf_pos[stream] = 0;
+					} else {
+						usb_buf[i+n] = 0;
+					}
+				}
+
+				if (audio_buf)
+					dev->period_out_count[stream] += BYTES_PER_SAMPLE;
+
+				i += BYTES_PER_SAMPLE;
+
+				/* fill in the check byte pattern */
+				usb_buf[i++] = (stream << 1) | c;
+			}
+		}
+	}
+}
+
+static inline void fill_out_urb(struct snd_usb_caiaqdev *dev,
+				struct urb *urb,
+				const struct usb_iso_packet_descriptor *iso)
+{
+	switch (dev->spec.data_alignment) {
+	case 0:
+	case 2:
+		fill_out_urb_mode_0(dev, urb, iso);
+		break;
+	case 3:
+		fill_out_urb_mode_3(dev, urb, iso);
+		break;
 	}
 }
 
diff --git a/sound/usb/caiaq/control.c b/sound/usb/caiaq/control.c
index 91c804c..00e5d0a 100644
--- a/sound/usb/caiaq/control.c
+++ b/sound/usb/caiaq/control.c
@@ -55,6 +55,10 @@
 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
 		maxval = 127;
 		break;
+
+	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
+		maxval = 31;
+		break;
 	}
 
 	if (is_intval) {
@@ -93,6 +97,7 @@
 	struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
 	struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
 	int pos = kcontrol->private_value;
+	int v = ucontrol->value.integer.value[0];
 	unsigned char cmd = EP1_CMD_WRITE_IO;
 
 	if (dev->chip.usb_id ==
@@ -100,12 +105,27 @@
 		cmd = EP1_CMD_DIMM_LEDS;
 
 	if (pos & CNT_INTVAL) {
-		dev->control_state[pos & ~CNT_INTVAL]
-			= ucontrol->value.integer.value[0];
-		snd_usb_caiaq_send_command(dev, cmd,
-				dev->control_state, sizeof(dev->control_state));
+		int i = pos & ~CNT_INTVAL;
+
+		dev->control_state[i] = v;
+
+		if (dev->chip.usb_id ==
+			USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4)) {
+			int actual_len;
+
+			dev->ep8_out_buf[0] = i;
+			dev->ep8_out_buf[1] = v;
+
+			usb_bulk_msg(dev->chip.dev,
+				     usb_sndbulkpipe(dev->chip.dev, 8),
+				     dev->ep8_out_buf, sizeof(dev->ep8_out_buf),
+				     &actual_len, 200);
+		} else {
+			snd_usb_caiaq_send_command(dev, cmd,
+					dev->control_state, sizeof(dev->control_state));
+		}
 	} else {
-		if (ucontrol->value.integer.value[0])
+		if (v)
 			dev->control_state[pos / 8] |= 1 << (pos % 8);
 		else
 			dev->control_state[pos / 8] &= ~(1 << (pos % 8));
@@ -296,6 +316,179 @@
 	{ "LED Deck B: SYNC",		8  | CNT_INTVAL	},
 };
 
+static struct caiaq_controller kontrols4_controller[] = {
+	{ "LED: Master: Quant",			10  | CNT_INTVAL },
+	{ "LED: Master: Headphone",		11  | CNT_INTVAL },
+	{ "LED: Master: Master",		12  | CNT_INTVAL },
+	{ "LED: Master: Snap",			14  | CNT_INTVAL },
+	{ "LED: Master: Warning",		15  | CNT_INTVAL },
+	{ "LED: Master: Master button",		112 | CNT_INTVAL },
+	{ "LED: Master: Snap button",		113 | CNT_INTVAL },
+	{ "LED: Master: Rec",			118 | CNT_INTVAL },
+	{ "LED: Master: Size",			119 | CNT_INTVAL },
+	{ "LED: Master: Quant button",		120 | CNT_INTVAL },
+	{ "LED: Master: Browser button",	121 | CNT_INTVAL },
+	{ "LED: Master: Play button",		126 | CNT_INTVAL },
+	{ "LED: Master: Undo button",		127 | CNT_INTVAL },
+
+	{ "LED: Channel A: >",			4   | CNT_INTVAL },
+	{ "LED: Channel A: <",			5   | CNT_INTVAL },
+	{ "LED: Channel A: Meter 1",		97  | CNT_INTVAL },
+	{ "LED: Channel A: Meter 2",		98  | CNT_INTVAL },
+	{ "LED: Channel A: Meter 3",		99  | CNT_INTVAL },
+	{ "LED: Channel A: Meter 4",		100 | CNT_INTVAL },
+	{ "LED: Channel A: Meter 5",		101 | CNT_INTVAL },
+	{ "LED: Channel A: Meter 6",		102 | CNT_INTVAL },
+	{ "LED: Channel A: Meter clip",		103 | CNT_INTVAL },
+	{ "LED: Channel A: Active",		114 | CNT_INTVAL },
+	{ "LED: Channel A: Cue",		116 | CNT_INTVAL },
+	{ "LED: Channel A: FX1",		149 | CNT_INTVAL },
+	{ "LED: Channel A: FX2",		148 | CNT_INTVAL },
+
+	{ "LED: Channel B: >",			2   | CNT_INTVAL },
+	{ "LED: Channel B: <",			3   | CNT_INTVAL },
+	{ "LED: Channel B: Meter 1",		89  | CNT_INTVAL },
+	{ "LED: Channel B: Meter 2",		90  | CNT_INTVAL },
+	{ "LED: Channel B: Meter 3",		91  | CNT_INTVAL },
+	{ "LED: Channel B: Meter 4",		92  | CNT_INTVAL },
+	{ "LED: Channel B: Meter 5",		93  | CNT_INTVAL },
+	{ "LED: Channel B: Meter 6",		94  | CNT_INTVAL },
+	{ "LED: Channel B: Meter clip",		95  | CNT_INTVAL },
+	{ "LED: Channel B: Active",		122 | CNT_INTVAL },
+	{ "LED: Channel B: Cue",		125 | CNT_INTVAL },
+	{ "LED: Channel B: FX1",		147 | CNT_INTVAL },
+	{ "LED: Channel B: FX2",		146 | CNT_INTVAL },
+
+	{ "LED: Channel C: >",			6   | CNT_INTVAL },
+	{ "LED: Channel C: <",			7   | CNT_INTVAL },
+	{ "LED: Channel C: Meter 1",		105 | CNT_INTVAL },
+	{ "LED: Channel C: Meter 2",		106 | CNT_INTVAL },
+	{ "LED: Channel C: Meter 3",		107 | CNT_INTVAL },
+	{ "LED: Channel C: Meter 4",		108 | CNT_INTVAL },
+	{ "LED: Channel C: Meter 5",		109 | CNT_INTVAL },
+	{ "LED: Channel C: Meter 6",		110 | CNT_INTVAL },
+	{ "LED: Channel C: Meter clip",		111 | CNT_INTVAL },
+	{ "LED: Channel C: Active",		115 | CNT_INTVAL },
+	{ "LED: Channel C: Cue",		117 | CNT_INTVAL },
+	{ "LED: Channel C: FX1",		151 | CNT_INTVAL },
+	{ "LED: Channel C: FX2",		150 | CNT_INTVAL },
+
+	{ "LED: Channel D: >",			0   | CNT_INTVAL },
+	{ "LED: Channel D: <",			1   | CNT_INTVAL },
+	{ "LED: Channel D: Meter 1",		81  | CNT_INTVAL },
+	{ "LED: Channel D: Meter 2",		82  | CNT_INTVAL },
+	{ "LED: Channel D: Meter 3",		83  | CNT_INTVAL },
+	{ "LED: Channel D: Meter 4",		84  | CNT_INTVAL },
+	{ "LED: Channel D: Meter 5",		85  | CNT_INTVAL },
+	{ "LED: Channel D: Meter 6",		86  | CNT_INTVAL },
+	{ "LED: Channel D: Meter clip",		87  | CNT_INTVAL },
+	{ "LED: Channel D: Active",		123 | CNT_INTVAL },
+	{ "LED: Channel D: Cue",		124 | CNT_INTVAL },
+	{ "LED: Channel D: FX1",		145 | CNT_INTVAL },
+	{ "LED: Channel D: FX2",		144 | CNT_INTVAL },
+
+	{ "LED: Deck A: 1 (blue)",		22  | CNT_INTVAL },
+	{ "LED: Deck A: 1 (green)",		23  | CNT_INTVAL },
+	{ "LED: Deck A: 2 (blue)",		20  | CNT_INTVAL },
+	{ "LED: Deck A: 2 (green)",		21  | CNT_INTVAL },
+	{ "LED: Deck A: 3 (blue)",		18  | CNT_INTVAL },
+	{ "LED: Deck A: 3 (green)",		19  | CNT_INTVAL },
+	{ "LED: Deck A: 4 (blue)",		16  | CNT_INTVAL },
+	{ "LED: Deck A: 4 (green)",		17  | CNT_INTVAL },
+	{ "LED: Deck A: Load",			44  | CNT_INTVAL },
+	{ "LED: Deck A: Deck C button",		45  | CNT_INTVAL },
+	{ "LED: Deck A: In",			47  | CNT_INTVAL },
+	{ "LED: Deck A: Out",			46  | CNT_INTVAL },
+	{ "LED: Deck A: Shift",			24  | CNT_INTVAL },
+	{ "LED: Deck A: Sync",			27  | CNT_INTVAL },
+	{ "LED: Deck A: Cue",			26  | CNT_INTVAL },
+	{ "LED: Deck A: Play",			25  | CNT_INTVAL },
+	{ "LED: Deck A: Tempo up",		33  | CNT_INTVAL },
+	{ "LED: Deck A: Tempo down",		32  | CNT_INTVAL },
+	{ "LED: Deck A: Master",		34  | CNT_INTVAL },
+	{ "LED: Deck A: Keylock",		35  | CNT_INTVAL },
+	{ "LED: Deck A: Deck A",		37  | CNT_INTVAL },
+	{ "LED: Deck A: Deck C",		36  | CNT_INTVAL },
+	{ "LED: Deck A: Samples",		38  | CNT_INTVAL },
+	{ "LED: Deck A: On Air",		39  | CNT_INTVAL },
+	{ "LED: Deck A: Sample 1",		31  | CNT_INTVAL },
+	{ "LED: Deck A: Sample 2",		30  | CNT_INTVAL },
+	{ "LED: Deck A: Sample 3",		29  | CNT_INTVAL },
+	{ "LED: Deck A: Sample 4",		28  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 1 - A",		55  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 1 - B",		54  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 1 - C",		53  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 1 - D",		52  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 1 - E",		51  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 1 - F",		50  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 1 - G",		49  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 1 - dot",		48  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 2 - A",		63  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 2 - B",		62  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 2 - C",		61  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 2 - D",		60  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 2 - E",		59  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 2 - F",		58  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 2 - G",		57  | CNT_INTVAL },
+	{ "LED: Deck A: Digit 2 - dot",		56  | CNT_INTVAL },
+
+	{ "LED: Deck B: 1 (blue)",		78  | CNT_INTVAL },
+	{ "LED: Deck B: 1 (green)",		79  | CNT_INTVAL },
+	{ "LED: Deck B: 2 (blue)",		76  | CNT_INTVAL },
+	{ "LED: Deck B: 2 (green)",		77  | CNT_INTVAL },
+	{ "LED: Deck B: 3 (blue)",		74  | CNT_INTVAL },
+	{ "LED: Deck B: 3 (green)",		75  | CNT_INTVAL },
+	{ "LED: Deck B: 4 (blue)",		72  | CNT_INTVAL },
+	{ "LED: Deck B: 4 (green)",		73  | CNT_INTVAL },
+	{ "LED: Deck B: Load",			180 | CNT_INTVAL },
+	{ "LED: Deck B: Deck D button",		181 | CNT_INTVAL },
+	{ "LED: Deck B: In",			183 | CNT_INTVAL },
+	{ "LED: Deck B: Out",			182 | CNT_INTVAL },
+	{ "LED: Deck B: Shift",			64  | CNT_INTVAL },
+	{ "LED: Deck B: Sync",			67  | CNT_INTVAL },
+	{ "LED: Deck B: Cue",			66  | CNT_INTVAL },
+	{ "LED: Deck B: Play",			65  | CNT_INTVAL },
+	{ "LED: Deck B: Tempo up",		185 | CNT_INTVAL },
+	{ "LED: Deck B: Tempo down",		184 | CNT_INTVAL },
+	{ "LED: Deck B: Master",		186 | CNT_INTVAL },
+	{ "LED: Deck B: Keylock",		187 | CNT_INTVAL },
+	{ "LED: Deck B: Deck B",		189 | CNT_INTVAL },
+	{ "LED: Deck B: Deck D",		188 | CNT_INTVAL },
+	{ "LED: Deck B: Samples",		190 | CNT_INTVAL },
+	{ "LED: Deck B: On Air",		191 | CNT_INTVAL },
+	{ "LED: Deck B: Sample 1",		71  | CNT_INTVAL },
+	{ "LED: Deck B: Sample 2",		70  | CNT_INTVAL },
+	{ "LED: Deck B: Sample 3",		69  | CNT_INTVAL },
+	{ "LED: Deck B: Sample 4",		68  | CNT_INTVAL },
+	{ "LED: Deck B: Digit 1 - A",		175 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 1 - B",		174 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 1 - C",		173 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 1 - D",		172 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 1 - E",		171 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 1 - F",		170 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 1 - G",		169 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 1 - dot",		168 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 2 - A",		167 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 2 - B",		166 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 2 - C",		165 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 2 - D",		164 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 2 - E",		163 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 2 - F",		162 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 2 - G",		161 | CNT_INTVAL },
+	{ "LED: Deck B: Digit 2 - dot",		160 | CNT_INTVAL },
+
+	{ "LED: FX1: dry/wet",			153 | CNT_INTVAL },
+	{ "LED: FX1: 1",			154 | CNT_INTVAL },
+	{ "LED: FX1: 2",			155 | CNT_INTVAL },
+	{ "LED: FX1: 3",			156 | CNT_INTVAL },
+	{ "LED: FX1: Mode",			157 | CNT_INTVAL },
+	{ "LED: FX2: dry/wet",			129 | CNT_INTVAL },
+	{ "LED: FX2: 1",			130 | CNT_INTVAL },
+	{ "LED: FX2: 2",			131 | CNT_INTVAL },
+	{ "LED: FX2: 3",			132 | CNT_INTVAL },
+	{ "LED: FX2: Mode",			133 | CNT_INTVAL },
+};
+
 static int __devinit add_controls(struct caiaq_controller *c, int num,
 				  struct snd_usb_caiaqdev *dev)
 {
@@ -354,6 +547,11 @@
 		ret = add_controls(kontrolx1_controller,
 			ARRAY_SIZE(kontrolx1_controller), dev);
 		break;
+
+	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
+		ret = add_controls(kontrols4_controller,
+			ARRAY_SIZE(kontrols4_controller), dev);
+		break;
 	}
 
 	return ret;
diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c
index cdfb856..6480c32 100644
--- a/sound/usb/caiaq/device.c
+++ b/sound/usb/caiaq/device.c
@@ -36,7 +36,7 @@
 #include "input.h"
 
 MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.3.21");
+MODULE_DESCRIPTION("caiaq USB audio");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
 			 "{Native Instruments, RigKontrol3},"
@@ -48,7 +48,8 @@
 			 "{Native Instruments, Audio 8 DJ},"
 			 "{Native Instruments, Session I/O},"
 			 "{Native Instruments, GuitarRig mobile}"
-			 "{Native Instruments, Traktor Kontrol X1}");
+			 "{Native Instruments, Traktor Kontrol X1}"
+			 "{Native Instruments, Traktor Kontrol S4}");
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
 static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
@@ -134,6 +135,11 @@
 		.idVendor =     USB_VID_NATIVEINSTRUMENTS,
 		.idProduct =    USB_PID_TRAKTORKONTROLX1
 	},
+	{
+		.match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
+		.idVendor =     USB_VID_NATIVEINSTRUMENTS,
+		.idProduct =    USB_PID_TRAKTORKONTROLS4
+	},
 	{ /* terminator */ }
 };
 
diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h
index f1117ec..e3d8a3e 100644
--- a/sound/usb/caiaq/device.h
+++ b/sound/usb/caiaq/device.h
@@ -16,6 +16,7 @@
 #define USB_PID_SESSIONIO		0x1915
 #define USB_PID_GUITARRIGMOBILE		0x0d8d
 #define USB_PID_TRAKTORKONTROLX1	0x2305
+#define USB_PID_TRAKTORKONTROLS4	0xbaff
 
 #define EP1_BUFSIZE 64
 #define EP4_BUFSIZE 512
@@ -99,13 +100,14 @@
 	struct snd_pcm_substream *sub_capture[MAX_STREAMS];
 
 	/* Controls */
-	unsigned char control_state[64];
+	unsigned char control_state[256];
+	unsigned char ep8_out_buf[2];
 
 	/* Linux input */
 #ifdef CONFIG_SND_USB_CAIAQ_INPUT
 	struct input_dev *input_dev;
 	char phys[64];			/* physical device path */
-	unsigned short keycode[64];
+	unsigned short keycode[128];
 	struct urb *ep4_in_urb;
 	unsigned char ep4_in_buf[EP4_BUFSIZE];
 #endif
diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c
index dcb6207..4432ef7 100644
--- a/sound/usb/caiaq/input.c
+++ b/sound/usb/caiaq/input.c
@@ -67,7 +67,12 @@
 	KEY_BRL_DOT5
 };
 
-#define KONTROLX1_INPUTS 40
+#define KONTROLX1_INPUTS	(40)
+#define KONTROLS4_BUTTONS	(12 * 8)
+#define KONTROLS4_AXIS		(46)
+
+#define KONTROLS4_BUTTON(X)	((X) + BTN_MISC)
+#define KONTROLS4_ABS(X)	((X) + ABS_HAT0X)
 
 #define DEG90		(range / 2)
 #define DEG180		(range)
@@ -139,6 +144,13 @@
 #undef HIGH_PEAK
 #undef LOW_PEAK
 
+static inline void snd_caiaq_input_report_abs(struct snd_usb_caiaqdev *dev,
+					      int axis, const unsigned char *buf,
+					      int offset)
+{
+	input_report_abs(dev->input_dev, axis,
+			 (buf[offset * 2] << 8) | buf[offset * 2 + 1]);
+}
 
 static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev,
 					const unsigned char *buf,
@@ -148,36 +160,30 @@
 
 	switch (dev->chip.usb_id) {
 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
-		input_report_abs(input_dev, ABS_X, (buf[4] << 8) | buf[5]);
-		input_report_abs(input_dev, ABS_Y, (buf[0] << 8) | buf[1]);
-		input_report_abs(input_dev, ABS_Z, (buf[2] << 8) | buf[3]);
-		input_sync(input_dev);
+		snd_caiaq_input_report_abs(dev, ABS_X, buf, 2);
+		snd_caiaq_input_report_abs(dev, ABS_Y, buf, 0);
+		snd_caiaq_input_report_abs(dev, ABS_Z, buf, 1);
 		break;
 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
-		input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]);
-		input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]);
-		input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]);
-		input_sync(input_dev);
-		break;
 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
-		input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]);
-		input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]);
-		input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]);
-		input_sync(input_dev);
+		snd_caiaq_input_report_abs(dev, ABS_X, buf, 0);
+		snd_caiaq_input_report_abs(dev, ABS_Y, buf, 1);
+		snd_caiaq_input_report_abs(dev, ABS_Z, buf, 2);
 		break;
 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
-		input_report_abs(input_dev, ABS_HAT0X, (buf[8] << 8)  | buf[9]);
-		input_report_abs(input_dev, ABS_HAT0Y, (buf[4] << 8)  | buf[5]);
-		input_report_abs(input_dev, ABS_HAT1X, (buf[12] << 8) | buf[13]);
-		input_report_abs(input_dev, ABS_HAT1Y, (buf[2] << 8)  | buf[3]);
-		input_report_abs(input_dev, ABS_HAT2X, (buf[14] << 8) | buf[15]);
-		input_report_abs(input_dev, ABS_HAT2Y, (buf[0] << 8)  | buf[1]);
-		input_report_abs(input_dev, ABS_HAT3X, (buf[10] << 8) | buf[11]);
-		input_report_abs(input_dev, ABS_HAT3Y, (buf[6] << 8)  | buf[7]);
-		input_sync(input_dev);
+		snd_caiaq_input_report_abs(dev, ABS_HAT0X, buf, 4);
+		snd_caiaq_input_report_abs(dev, ABS_HAT0Y, buf, 2);
+		snd_caiaq_input_report_abs(dev, ABS_HAT1X, buf, 6);
+		snd_caiaq_input_report_abs(dev, ABS_HAT1Y, buf, 1);
+		snd_caiaq_input_report_abs(dev, ABS_HAT2X, buf, 7);
+		snd_caiaq_input_report_abs(dev, ABS_HAT2Y, buf, 0);
+		snd_caiaq_input_report_abs(dev, ABS_HAT3X, buf, 5);
+		snd_caiaq_input_report_abs(dev, ABS_HAT3Y, buf, 3);
 		break;
 	}
+
+	input_sync(input_dev);
 }
 
 static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev,
@@ -250,6 +256,150 @@
 	input_sync(input_dev);
 }
 
+#define TKS4_MSGBLOCK_SIZE	16
+
+static void snd_usb_caiaq_tks4_dispatch(struct snd_usb_caiaqdev *dev,
+					const unsigned char *buf,
+					unsigned int len)
+{
+	while (len) {
+		unsigned int i, block_id = (buf[0] << 8) | buf[1];
+
+		switch (block_id) {
+		case 0:
+			/* buttons */
+			for (i = 0; i < KONTROLS4_BUTTONS; i++)
+				input_report_key(dev->input_dev, KONTROLS4_BUTTON(i),
+						 (buf[4 + (i / 8)] >> (i % 8)) & 1);
+			break;
+
+		case 1:
+			/* left wheel */
+			input_report_abs(dev->input_dev, KONTROLS4_ABS(36), buf[9] | ((buf[8] & 0x3) << 8));
+			/* right wheel */
+			input_report_abs(dev->input_dev, KONTROLS4_ABS(37), buf[13] | ((buf[12] & 0x3) << 8));
+
+			/* rotary encoders */
+			input_report_abs(dev->input_dev, KONTROLS4_ABS(38), buf[3] & 0xf);
+			input_report_abs(dev->input_dev, KONTROLS4_ABS(39), buf[4] >> 4);
+			input_report_abs(dev->input_dev, KONTROLS4_ABS(40), buf[4] & 0xf);
+			input_report_abs(dev->input_dev, KONTROLS4_ABS(41), buf[5] >> 4);
+			input_report_abs(dev->input_dev, KONTROLS4_ABS(42), buf[5] & 0xf);
+			input_report_abs(dev->input_dev, KONTROLS4_ABS(43), buf[6] >> 4);
+			input_report_abs(dev->input_dev, KONTROLS4_ABS(44), buf[6] & 0xf);
+			input_report_abs(dev->input_dev, KONTROLS4_ABS(45), buf[7] >> 4);
+			input_report_abs(dev->input_dev, KONTROLS4_ABS(46), buf[7] & 0xf);
+
+			break;
+		case 2:
+			/* Volume Fader Channel D */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(0), buf, 1);
+			/* Volume Fader Channel B */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(1), buf, 2);
+			/* Volume Fader Channel A */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(2), buf, 3);
+			/* Volume Fader Channel C */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(3), buf, 4);
+			/* Loop Volume */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(4), buf, 6);
+			/* Crossfader */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(7), buf, 7);
+
+			break;
+
+		case 3:
+			/* Tempo Fader R */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(6), buf, 3);
+			/* Tempo Fader L */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(5), buf, 4);
+			/* Mic Volume */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(8), buf, 6);
+			/* Cue Mix */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(9), buf, 7);
+
+			break;
+
+		case 4:
+			/* Wheel distance sensor L */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(10), buf, 1);
+			/* Wheel distance sensor R */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(11), buf, 2);
+			/* Channel D EQ - Filter */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(12), buf, 3);
+			/* Channel D EQ - Low */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(13), buf, 4);
+			/* Channel D EQ - Mid */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(14), buf, 5);
+			/* Channel D EQ - Hi */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(15), buf, 6);
+			/* FX2 - dry/wet */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(16), buf, 7);
+
+			break;
+
+		case 5:
+			/* FX2 - 1 */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(17), buf, 1);
+			/* FX2 - 2 */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(18), buf, 2);
+			/* FX2 - 3 */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(19), buf, 3);
+			/* Channel B EQ - Filter */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(20), buf, 4);
+			/* Channel B EQ - Low */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(21), buf, 5);
+			/* Channel B EQ - Mid */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(22), buf, 6);
+			/* Channel B EQ - Hi */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(23), buf, 7);
+
+			break;
+
+		case 6:
+			/* Channel A EQ - Filter */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(24), buf, 1);
+			/* Channel A EQ - Low */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(25), buf, 2);
+			/* Channel A EQ - Mid */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(26), buf, 3);
+			/* Channel A EQ - Hi */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(27), buf, 4);
+			/* Channel C EQ - Filter */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(28), buf, 5);
+			/* Channel C EQ - Low */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(29), buf, 6);
+			/* Channel C EQ - Mid */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(30), buf, 7);
+
+			break;
+
+		case 7:
+			/* Channel C EQ - Hi */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(31), buf, 1);
+			/* FX1 - wet/dry */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(32), buf, 2);
+			/* FX1 - 1 */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(33), buf, 3);
+			/* FX1 - 2 */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(34), buf, 4);
+			/* FX1 - 3 */
+			snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(35), buf, 5);
+
+			break;
+
+		default:
+			debug("%s(): bogus block (id %d)\n",
+				__func__, block_id);
+			return;
+		}
+
+		len -= TKS4_MSGBLOCK_SIZE;
+		buf += TKS4_MSGBLOCK_SIZE;
+	}
+
+	input_sync(dev->input_dev);
+}
+
 static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb)
 {
 	struct snd_usb_caiaqdev *dev = urb->context;
@@ -259,11 +409,11 @@
 	if (urb->status || !dev || urb != dev->ep4_in_urb)
 		return;
 
-	if (urb->actual_length < 24)
-		goto requeue;
-
 	switch (dev->chip.usb_id) {
 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+		if (urb->actual_length < 24)
+			goto requeue;
+
 		if (buf[0] & 0x3)
 			snd_caiaq_input_read_io(dev, buf + 1, 7);
 
@@ -271,6 +421,10 @@
 			snd_caiaq_input_read_analog(dev, buf + 8, 16);
 
 		break;
+
+	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
+		snd_usb_caiaq_tks4_dispatch(dev, buf, urb->actual_length);
+		break;
 	}
 
 requeue:
@@ -289,6 +443,7 @@
 
 	switch (dev->chip.usb_id) {
 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
 		if (usb_submit_urb(dev->ep4_in_urb, GFP_KERNEL) != 0)
 			return -EIO;
 		break;
@@ -306,6 +461,7 @@
 
 	switch (dev->chip.usb_id) {
 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
 		usb_kill_urb(dev->ep4_in_urb);
 		break;
 	}
@@ -456,6 +612,46 @@
 		snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
 
 		break;
+
+	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
+		input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+		BUILD_BUG_ON(sizeof(dev->keycode) < KONTROLS4_BUTTONS);
+		for (i = 0; i < KONTROLS4_BUTTONS; i++)
+			dev->keycode[i] = KONTROLS4_BUTTON(i);
+		input->keycodemax = KONTROLS4_BUTTONS;
+
+		for (i = 0; i < KONTROLS4_AXIS; i++) {
+			int axis = KONTROLS4_ABS(i);
+			input->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);
+		}
+
+		/* 36 analog potentiometers and faders */
+		for (i = 0; i < 36; i++)
+			input_set_abs_params(input, KONTROLS4_ABS(i), 0, 0xfff, 0, 10);
+
+		/* 2 encoder wheels */
+		input_set_abs_params(input, KONTROLS4_ABS(36), 0, 0x3ff, 0, 1);
+		input_set_abs_params(input, KONTROLS4_ABS(37), 0, 0x3ff, 0, 1);
+
+		/* 9 rotary encoders */
+		for (i = 0; i < 9; i++)
+			input_set_abs_params(input, KONTROLS4_ABS(38+i), 0, 0xf, 0, 1);
+
+		dev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!dev->ep4_in_urb) {
+			ret = -ENOMEM;
+			goto exit_free_idev;
+		}
+
+		usb_fill_bulk_urb(dev->ep4_in_urb, usb_dev,
+				  usb_rcvbulkpipe(usb_dev, 0x4),
+				  dev->ep4_in_buf, EP4_BUFSIZE,
+				  snd_usb_caiaq_ep4_reply_dispatch, dev);
+
+		snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
+
+		break;
+
 	default:
 		/* no input methods supported on this device */
 		goto exit_free_idev;
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 4eabafa..800f7cb 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -300,9 +300,13 @@
 
 	*rchip = NULL;
 
-	if (snd_usb_get_speed(dev) != USB_SPEED_LOW &&
-	    snd_usb_get_speed(dev) != USB_SPEED_FULL &&
-	    snd_usb_get_speed(dev) != USB_SPEED_HIGH) {
+	switch (snd_usb_get_speed(dev)) {
+	case USB_SPEED_LOW:
+	case USB_SPEED_FULL:
+	case USB_SPEED_HIGH:
+	case USB_SPEED_SUPER:
+		break;
+	default:
 		snd_printk(KERN_ERR "unknown device speed %d\n", snd_usb_get_speed(dev));
 		return -ENXIO;
 	}
@@ -378,11 +382,22 @@
 	if (len < sizeof(card->longname))
 		usb_make_path(dev, card->longname + len, sizeof(card->longname) - len);
 
-	strlcat(card->longname,
-		snd_usb_get_speed(dev) == USB_SPEED_LOW ? ", low speed" :
-		snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" :
-		", high speed",
-		sizeof(card->longname));
+	switch (snd_usb_get_speed(dev)) {
+	case USB_SPEED_LOW:
+		strlcat(card->longname, ", low speed", sizeof(card->longname));
+		break;
+	case USB_SPEED_FULL:
+		strlcat(card->longname, ", full speed", sizeof(card->longname));
+		break;
+	case USB_SPEED_HIGH:
+		strlcat(card->longname, ", high speed", sizeof(card->longname));
+		break;
+	case USB_SPEED_SUPER:
+		strlcat(card->longname, ", super speed", sizeof(card->longname));
+		break;
+	default:
+		break;
+	}
 
 	snd_usb_audio_create_proc(chip);
 
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index ef0a07e..b0ef9f5 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -405,8 +405,6 @@
 			break;
 		case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
 		case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
-		case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8 */
-		case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
 			/* doesn't set the sample rate attribute, but supports it */
 			fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE;
 			break;
diff --git a/sound/usb/helper.c b/sound/usb/helper.c
index d48d6f8..f280c19 100644
--- a/sound/usb/helper.c
+++ b/sound/usb/helper.c
@@ -103,11 +103,16 @@
 unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
 					 struct usb_host_interface *alts)
 {
-	if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH &&
-	    get_endpoint(alts, 0)->bInterval >= 1 &&
-	    get_endpoint(alts, 0)->bInterval <= 4)
-		return get_endpoint(alts, 0)->bInterval - 1;
-	else
-		return 0;
+	switch (snd_usb_get_speed(chip->dev)) {
+	case USB_SPEED_HIGH:
+	case USB_SPEED_SUPER:
+		if (get_endpoint(alts, 0)->bInterval >= 1 &&
+		    get_endpoint(alts, 0)->bInterval <= 4)
+			return get_endpoint(alts, 0)->bInterval - 1;
+		break;
+	default:
+		break;
+	}
+	return 0;
 }
 
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index b9c2bc6..156cd07 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -834,7 +834,14 @@
 
 	if (!ep->ports[0].active)
 		return;
-	count = snd_usb_get_speed(ep->umidi->dev) == USB_SPEED_HIGH ? 1 : 2;
+	switch (snd_usb_get_speed(ep->umidi->dev)) {
+	case USB_SPEED_HIGH:
+	case USB_SPEED_SUPER:
+		count = 1;
+		break;
+	default:
+		count = 2;
+	}
 	count = snd_rawmidi_transmit(ep->ports[0].substream,
 				     urb->transfer_buffer,
 				     count);
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 3ed3901..f2d74d6 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -759,8 +759,6 @@
  */
 static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
 {
-	struct snd_usb_audio *chip = cval->mixer->chip;
-
 	/* for failsafe */
 	cval->min = default_min;
 	cval->max = cval->min + 1;
@@ -783,7 +781,7 @@
 		if (get_ctl_value(cval, UAC_GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 ||
 		    get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) {
 			snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n",
-				   cval->id, snd_usb_ctrl_intf(chip), cval->control, cval->id);
+				   cval->id, snd_usb_ctrl_intf(cval->mixer->chip), cval->control, cval->id);
 			return -EINVAL;
 		}
 		if (get_ctl_value(cval, UAC_GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) {
@@ -1642,9 +1640,10 @@
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
 	uinfo->value.enumerated.items = cval->max;
-	if ((int)uinfo->value.enumerated.item >= cval->max)
+	if (uinfo->value.enumerated.item >= cval->max)
 		uinfo->value.enumerated.item = cval->max - 1;
-	strcpy(uinfo->value.enumerated.name, itemlist[uinfo->value.enumerated.item]);
+	strlcpy(uinfo->value.enumerated.name, itemlist[uinfo->value.enumerated.item],
+		sizeof(uinfo->value.enumerated.name));
 	return 0;
 }
 
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 3b5135c..f49756c 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -466,7 +466,7 @@
 		return 0;
 	}
 	/* check whether the period time is >= the data packet interval */
-	if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) {
+	if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL) {
 		ptime = 125 * (1 << fp->datainterval);
 		if (ptime > pt->max || (ptime == pt->max && pt->openmax)) {
 			hwc_debug("   > check: ptime %u > max %u\n", ptime, pt->max);
@@ -734,7 +734,7 @@
 	}
 
 	param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME;
-	if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH)
+	if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
 		/* full speed devices have fixed data packet interval */
 		ptmin = 1000;
 	if (ptmin == 1000)
diff --git a/sound/usb/proc.c b/sound/usb/proc.c
index f5e3f35..3c650ab 100644
--- a/sound/usb/proc.c
+++ b/sound/usb/proc.c
@@ -107,7 +107,7 @@
 			}
 			snd_iprintf(buffer, "\n");
 		}
-		if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
+		if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL)
 			snd_iprintf(buffer, "    Data packet interval: %d us\n",
 				    125 * (1 << fp->datainterval));
 		// snd_iprintf(buffer, "    Max Packet Size = %d\n", fp->maxpacksize);
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 2e8003f..682e3e0 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -240,9 +240,21 @@
 YAMAHA_DEVICE(0x1050, NULL),
 YAMAHA_DEVICE(0x1051, NULL),
 YAMAHA_DEVICE(0x1052, NULL),
+YAMAHA_INTERFACE(0x1053, 0, NULL),
+YAMAHA_INTERFACE(0x1054, 0, NULL),
+YAMAHA_DEVICE(0x1055, NULL),
+YAMAHA_DEVICE(0x1056, NULL),
+YAMAHA_DEVICE(0x1057, NULL),
+YAMAHA_DEVICE(0x1058, NULL),
+YAMAHA_DEVICE(0x1059, NULL),
+YAMAHA_DEVICE(0x105a, NULL),
+YAMAHA_DEVICE(0x105b, NULL),
+YAMAHA_DEVICE(0x105c, NULL),
+YAMAHA_DEVICE(0x105d, NULL),
 YAMAHA_DEVICE(0x2000, "DGP-7"),
 YAMAHA_DEVICE(0x2001, "DGP-5"),
 YAMAHA_DEVICE(0x2002, NULL),
+YAMAHA_DEVICE(0x2003, NULL),
 YAMAHA_DEVICE(0x5000, "CS1D"),
 YAMAHA_DEVICE(0x5001, "DSP1D"),
 YAMAHA_DEVICE(0x5002, "DME32"),
@@ -1136,11 +1148,34 @@
 	}
 },
 {
+	/* has ID 0x0066 when not in "Advanced Driver" mode */
+	USB_DEVICE(0x0582, 0x0064),
+	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+		/* .vendor_name = "EDIROL", */
+		/* .product_name = "PCR-1", */
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const struct snd_usb_audio_quirk[]) {
+			{
+				.ifnum = 1,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = -1
+			}
+		}
+	}
+},
+{
 	/* has ID 0x0067 when not in "Advanced Driver" mode */
 	USB_DEVICE(0x0582, 0x0065),
 	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
-		.vendor_name = "EDIROL",
-		.product_name = "PCR-1",
+		/* .vendor_name = "EDIROL", */
+		/* .product_name = "PCR-1", */
 		.ifnum = 0,
 		.type = QUIRK_MIDI_FIXED_ENDPOINT,
 		.data = & (const struct snd_usb_midi_endpoint_info) {
@@ -1525,6 +1560,50 @@
 		}
 	}
 },
+{
+	/* has ID 0x0110 when not in Advanced Driver mode */
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x010f),
+	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+		/* .vendor_name = "Roland", */
+		/* .product_name = "A-PRO", */
+		.ifnum = 1,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const struct snd_usb_midi_endpoint_info) {
+			.out_cables = 0x0003,
+			.in_cables  = 0x0007
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x0113),
+	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+		/* .vendor_name = "BOSS", */
+		/* .product_name = "ME-25", */
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const struct snd_usb_audio_quirk[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const struct snd_usb_midi_endpoint_info) {
+					.out_cables = 0x0001,
+					.in_cables  = 0x0001
+				}
+			},
+			{
+				.ifnum = -1
+			}
+		}
+	}
+},
 
 /* Guillemot devices */
 {
@@ -1830,7 +1909,7 @@
 	USB_DEVICE(0x0763, 0x2080),
 	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
 		/* .vendor_name = "M-Audio", */
-		/* .product_name = "Fast Track Ultra 8", */
+		/* .product_name = "Fast Track Ultra", */
 		.ifnum = QUIRK_ANY_INTERFACE,
 		.type = QUIRK_COMPOSITE,
 		.data = & (const struct snd_usb_audio_quirk[]) {
@@ -1840,11 +1919,51 @@
 			},
 			{
 				.ifnum = 1,
-				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+				.type = QUIRK_AUDIO_FIXED_ENDPOINT,
+				.data = & (const struct audioformat) {
+					.formats = SNDRV_PCM_FMTBIT_S24_3LE,
+					.channels = 8,
+					.iface = 1,
+					.altsetting = 1,
+					.altset_idx = 1,
+					.attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+					.endpoint = 0x01,
+					.ep_attr = 0x09,
+					.rates = SNDRV_PCM_RATE_44100 |
+						 SNDRV_PCM_RATE_48000 |
+						 SNDRV_PCM_RATE_88200 |
+						 SNDRV_PCM_RATE_96000,
+					.rate_min = 44100,
+					.rate_max = 96000,
+					.nr_rates = 4,
+					.rate_table = (unsigned int[]) {
+						44100, 48000, 88200, 96000
+					}
+				}
 			},
 			{
 				.ifnum = 2,
-				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+				.type = QUIRK_AUDIO_FIXED_ENDPOINT,
+				.data = & (const struct audioformat) {
+					.formats = SNDRV_PCM_FMTBIT_S24_3LE,
+					.channels = 8,
+					.iface = 2,
+					.altsetting = 1,
+					.altset_idx = 1,
+					.attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+					.endpoint = 0x81,
+					.ep_attr = 0x05,
+					.rates = SNDRV_PCM_RATE_44100 |
+						 SNDRV_PCM_RATE_48000 |
+						 SNDRV_PCM_RATE_88200 |
+						 SNDRV_PCM_RATE_96000,
+					.rate_min = 44100,
+					.rate_max = 96000,
+					.nr_rates = 4,
+					.rate_table = (unsigned int[]) {
+						44100, 48000, 88200, 96000
+					}
+				}
 			},
 			/* interface 3 (MIDI) is standard compliant */
 			{
@@ -1867,11 +1986,51 @@
 			},
 			{
 				.ifnum = 1,
-				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+				.type = QUIRK_AUDIO_FIXED_ENDPOINT,
+				.data = & (const struct audioformat) {
+					.formats = SNDRV_PCM_FMTBIT_S24_3LE,
+					.channels = 8,
+					.iface = 1,
+					.altsetting = 1,
+					.altset_idx = 1,
+					.attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+					.endpoint = 0x01,
+					.ep_attr = 0x09,
+					.rates = SNDRV_PCM_RATE_44100 |
+						 SNDRV_PCM_RATE_48000 |
+						 SNDRV_PCM_RATE_88200 |
+						 SNDRV_PCM_RATE_96000,
+					.rate_min = 44100,
+					.rate_max = 96000,
+					.nr_rates = 4,
+					.rate_table = (unsigned int[]) {
+							44100, 48000, 88200, 96000
+					}
+				}
 			},
 			{
 				.ifnum = 2,
-				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+				.type = QUIRK_AUDIO_FIXED_ENDPOINT,
+				.data = & (const struct audioformat) {
+					.formats = SNDRV_PCM_FMTBIT_S24_3LE,
+					.channels = 8,
+					.iface = 2,
+					.altsetting = 1,
+					.altset_idx = 1,
+					.attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+					.endpoint = 0x81,
+					.ep_attr = 0x05,
+					.rates = SNDRV_PCM_RATE_44100 |
+						 SNDRV_PCM_RATE_48000 |
+						 SNDRV_PCM_RATE_88200 |
+						 SNDRV_PCM_RATE_96000,
+					.rate_min = 44100,
+					.rate_max = 96000,
+					.nr_rates = 4,
+					.rate_table = (unsigned int[]) {
+						44100, 48000, 88200, 96000
+					}
+				}
 			},
 			/* interface 3 (MIDI) is standard compliant */
 			{
diff --git a/sound/usb/urb.c b/sound/usb/urb.c
index de607d4..8deeaad 100644
--- a/sound/usb/urb.c
+++ b/sound/usb/urb.c
@@ -244,7 +244,7 @@
 	else
 		subs->curpacksize = maxsize;
 
-	if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
+	if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL)
 		packs_per_ms = 8 >> subs->datainterval;
 	else
 		packs_per_ms = 1;