You can import this changeset into BK by piping this whole message to:
'| bk receive [path to repository]' or apply the patch as usual.

===================================================================


ChangeSet@1.650, 2002-10-01 14:17:46+02:00, perex@suse.cz
  ALSA update 2002/08/26 :
    - AC'97 codec
      - added ac97_can_amap() condition
      - removed powerup/powerdown sequence when sample rate is changed
      - added ac97_is_rev22 check function
      - added AC97_EI_* defines
      - available rates are in array
    - CS46xx
      - improved the SCB link mechanism
      - SMP deadlock should have been fixed now
    - OSS mixer emulation
      - added the proc interface for the configuration of OSS mixer volumes
    - rawmidi midlevel
      - removed unused snd_rawmidi_transmit_reset and snd_rawmidi_receive_reset functions
    - USB MIDI driver
      - integration of USB MIDI driver into USB audio driver by Clemens
    - intel8x0 - the big intel8x0 driver update
      - added support for ICH4
      - rewrited I/O (the third AC'97 codec registers are available only through memory)
      - code cleanups
      - ALI5455 specific code
      - added proc interface
    - VIA686, VIA8233
      - set the max periods to 128


 include/sound/ac97_codec.h          |   65 -
 include/sound/cs46xx.h              |    2 
 include/sound/cs46xx_dsp_spos.h     |    4 
 include/sound/mixer_oss.h           |    6 
 include/sound/mpu401.h              |    1 
 include/sound/sndmagic.h            |    6 
 include/sound/version.h             |    2 
 sound/core/Makefile                 |    1 
 sound/core/oss/mixer_oss.c          |  212 +++-
 sound/core/pcm_native.c             |    4 
 sound/core/rawmidi.c                |   12 
 sound/core/seq/Makefile             |    1 
 sound/isa/gus/gus_uart.c            |    2 
 sound/pci/ac97/ac97_codec.c         |   66 -
 sound/pci/ac97/ac97_patch.c         |    1 
 sound/pci/cs46xx/cs46xx_lib.c       |   65 -
 sound/pci/cs46xx/cs46xx_lib.h       |   12 
 sound/pci/cs46xx/dsp_spos.c         |   51 
 sound/pci/cs46xx/dsp_spos_scb_lib.c |  195 +--
 sound/pci/ice1712.c                 |   14 
 sound/pci/intel8x0.c                | 1856 ++++++++++++++++++++++++++----------
 sound/pci/via686.c                  |   33 
 sound/pci/via8233.c                 |   27 
 sound/usb/Config.help               |    5 
 sound/usb/Config.in                 |    3 
 sound/usb/Makefile                  |    6 
 sound/usb/usbaudio.c                |  118 +-
 sound/usb/usbaudio.h                |   53 +
 sound/usb/usbmidi.c                 |  835 +++-------------
 sound/usb/usbquirks.h               |  337 ++++++
 30 files changed, 2515 insertions(+), 1480 deletions(-)


diff -Nru a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h
--- a/include/sound/ac97_codec.h	Tue Oct  1 17:08:04 2002
+++ b/include/sound/ac97_codec.h	Tue Oct  1 17:08:04 2002
@@ -58,7 +58,7 @@
 #define AC97_PCM_FRONT_DAC_RATE 0x2c	/* PCM Front DAC Rate */
 #define AC97_PCM_SURR_DAC_RATE	0x2e	/* PCM Surround DAC Rate */
 #define AC97_PCM_LFE_DAC_RATE	0x30	/* PCM LFE DAC Rate */
-#define AC97_PCM_LR_ADC_RATE	0x32	/* PCM LR DAC Rate */
+#define AC97_PCM_LR_ADC_RATE	0x32	/* PCM LR ADC Rate */
 #define AC97_PCM_MIC_ADC_RATE	0x34	/* PCM MIC ADC Rate */
 #define AC97_CENTER_LFE_MASTER	0x36	/* Center + LFE Master Volume */
 #define AC97_SURROUND_MASTER	0x38	/* Surround (Rear) Master Volume */
@@ -68,25 +68,43 @@
 #define AC97_VENDOR_ID1		0x7c	/* Vendor ID1 */
 #define AC97_VENDOR_ID2		0x7e	/* Vendor ID2 / revision */
 
+/* extended audio ID bit defines */
+#define AC97_EI_VRA		0x0001	/* Variable bit rate supported */
+#define AC97_EI_DRA		0x0002	/* Double rate supported */
+#define AC97_EI_SPDIF		0x0004	/* S/PDIF out supported */
+#define AC97_EI_VRM		0x0008	/* Variable bit rate supported for MIC */
+#define AC97_EI_DACS_SLOT_MASK	0x0030	/* DACs slot assignment */
+#define AC97_EI_DACS_SLOT_SHIFT	4
+#define AC97_EI_CDAC		0x0040	/* PCM Center DAC available */
+#define AC97_EI_SDAC		0x0080	/* PCM Surround DACs available */
+#define AC97_EI_LDAC		0x0100	/* PCM LFE DAC available */
+#define AC97_EI_AMAP		0x0200	/* indicates optional slot/DAC mapping based on codec ID */
+#define AC97_EI_REV_MASK	0x0c00	/* AC'97 revision mask */
+#define AC97_EI_REV_22		0x0100	/* AC'97 revision 2.2 */
+#define AC97_EI_REV_SHIFT	8
+#define AC97_EI_ADDR_MASK	0xc000	/* physical codec ID (address) */
+#define AC97_EI_ADDR_SHIFT	14
+
 /* extended audio status and control bit defines */
 #define AC97_EA_VRA		0x0001	/* Variable bit rate enable bit */
 #define AC97_EA_DRA		0x0002	/* Double-rate audio enable bit */
-#define AC97_EA_SPDIF		0x0004	/* S/PDIF Enable bit */
+#define AC97_EA_SPDIF		0x0004	/* S/PDIF out enable bit */
 #define AC97_EA_VRM		0x0008	/* Variable bit rate for MIC enable bit */
+#define AC97_EA_SPSA_SLOT_MASK	0x0030	/* Mask for slot assignment bits */
+#define AC97_EA_SPSA_SLOT_SHIFT 4
+#define AC97_EA_SPSA_3_4	0x0000	/* Slot assigned to 3 & 4 */
+#define AC97_EA_SPSA_7_8	0x0010	/* Slot assigned to 7 & 8 */
+#define AC97_EA_SPSA_6_9	0x0020	/* Slot assigned to 6 & 9 */
+#define AC97_EA_SPSA_10_11	0x0030	/* Slot assigned to 10 & 11 */
 #define AC97_EA_CDAC		0x0040	/* PCM Center DAC is ready (Read only) */
-#define AC97_EA_SDAC		0x0040	/* PCM Surround DACs are ready (Read only) */
-#define AC97_EA_LDAC		0x0080	/* PCM LFE DAC is ready (Read only) */
-#define AC97_EA_MDAC		0x0100	/* MIC ADC is ready (Read only) */
+#define AC97_EA_SDAC		0x0080	/* PCM Surround DACs are ready (Read only) */
+#define AC97_EA_LDAC		0x0100	/* PCM LFE DAC is ready (Read only) */
+#define AC97_EA_MDAC		0x0200	/* MIC ADC is ready (Read only) */
 #define AC97_EA_SPCV		0x0400	/* S/PDIF configuration valid (Read only) */
 #define AC97_EA_PRI		0x0800	/* Turns the PCM Center DAC off */
 #define AC97_EA_PRJ		0x1000	/* Turns the PCM Surround DACs off */
 #define AC97_EA_PRK		0x2000	/* Turns the PCM LFE DAC off */
 #define AC97_EA_PRL		0x4000	/* Turns the MIC ADC off */
-#define AC97_EA_SLOT_MASK	0xffcf	/* Mask for slot assignment bits */
-#define AC97_EA_SPSA_3_4	0x0000	/* Slot assigned to 3 & 4 */
-#define AC97_EA_SPSA_7_8	0x0010	/* Slot assigned to 7 & 8 */
-#define AC97_EA_SPSA_6_9	0x0020	/* Slot assigned to 6 & 9 */
-#define AC97_EA_SPSA_10_11	0x0030	/* Slot assigned to 10 & 11 */
 
 /* S/PDIF control bit defines */
 #define AC97_SC_PRO		0x0001	/* Professional status */
@@ -145,8 +163,16 @@
 #define AC97_CS_SPDIF		(1<<2)	/* Cirrus Logic uses funky SPDIF */
 #define AC97_CX_SPDIF		(1<<3)	/* Conexant's spdif interface */
 
-/*
+/* rates indexes */
+#define AC97_RATES_FRONT_DAC	0
+#define AC97_RATES_SURR_DAC	1
+#define AC97_RATES_LFE_DAC	2
+#define AC97_RATES_ADC		3
+#define AC97_RATES_MIC_ADC	4
+#define AC97_RATES_SPDIF	5
 
+/*
+ *
  */
 
 typedef struct _snd_ac97 ac97_t;
@@ -172,11 +198,7 @@
 	unsigned int scaps;	/* driver capabilities */
 	unsigned int flags;	/* specific code */
 	unsigned int clock;	/* AC'97 clock (usually 48000Hz) */
-	unsigned int rates_front_dac;
-	unsigned int rates_surr_dac;
-	unsigned int rates_lfe_dac;
-	unsigned int rates_adc;
-	unsigned int rates_mic_adc;
+	unsigned int rates[6];	/* see AC97_RATES_* defines */
 	unsigned int spdif_status;
 	unsigned short regs[0x80]; /* register cache */
 	unsigned int limited_regs; /* allow limited registers only */
@@ -192,6 +214,17 @@
 	} spec;
 };
 
+/* conditions */
+static inline int ac97_is_rev22(ac97_t * ac97)
+{
+	return (ac97->ext_id & AC97_EI_REV_MASK) == AC97_EI_REV_22;
+}
+static inline int ac97_can_amap(ac97_t * ac97)
+{
+	return (ac97->ext_id & AC97_EI_AMAP) != 0;
+}
+
+/* functions */
 int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97);
 
 void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value);
diff -Nru a/include/sound/cs46xx.h b/include/sound/cs46xx.h
--- a/include/sound/cs46xx.h	Tue Oct  1 17:08:04 2002
+++ b/include/sound/cs46xx.h	Tue Oct  1 17:08:04 2002
@@ -1817,6 +1817,8 @@
   int current_gpio;
 #endif
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
+	struct semaphore spos_mutex;
+
 	dsp_spos_instance_t * dsp_spos_instance;
 #else /* for compatibility */
 	cs46xx_pcm_t *playback_pcm;
diff -Nru a/include/sound/cs46xx_dsp_spos.h b/include/sound/cs46xx_dsp_spos.h
--- a/include/sound/cs46xx_dsp_spos.h	Tue Oct  1 17:08:04 2002
+++ b/include/sound/cs46xx_dsp_spos.h	Tue Oct  1 17:08:04 2002
@@ -109,6 +109,7 @@
 
 	snd_info_entry_t *proc_info;
 	int ref_count;
+	spinlock_t lock;
 
 	int deleted;
 } dsp_scb_descriptor_t;
@@ -141,8 +142,6 @@
 	segment_desc_t code;
 
 	/* PCM playback */
-	struct semaphore pcm_mutex;
-
 	dsp_scb_descriptor_t * master_mix_scb;
 	int npcm_channels;
 	int nsrc_scb;
@@ -162,7 +161,6 @@
 	snd_info_entry_t * proc_sample_dump_info_entry;
 
 	/* SCB's descriptors */
-	struct semaphore scb_mutex;
 	int nscb;
 	int scb_highest_frag_index;
 	dsp_scb_descriptor_t scbs[DSP_MAX_SCB_DESC];
diff -Nru a/include/sound/mixer_oss.h b/include/sound/mixer_oss.h
--- a/include/sound/mixer_oss.h	Tue Oct  1 17:08:04 2002
+++ b/include/sound/mixer_oss.h	Tue Oct  1 17:08:04 2002
@@ -34,6 +34,8 @@
 typedef int (*snd_mixer_oss_get_recsrce_t)(snd_mixer_oss_file_t *fmixer, int *active_index);
 typedef int (*snd_mixer_oss_put_recsrce_t)(snd_mixer_oss_file_t *fmixer, int active_index);
 
+#define SNDRV_OSS_MAX_MIXERS	32
+
 struct _snd_oss_mixer_slot {
 	int number;
 	int stereo: 1;
@@ -50,12 +52,14 @@
 	snd_card_t *card;
 	char id[16];
 	char name[32];
-	snd_mixer_oss_slot_t slots[32];		/* OSS mixer slots */
+	snd_mixer_oss_slot_t slots[SNDRV_OSS_MAX_MIXERS]; /* OSS mixer slots */
 	unsigned int mask_recsrc;		/* exclusive recsrc mask */
 	snd_mixer_oss_get_recsrce_t get_recsrc;
 	snd_mixer_oss_put_recsrce_t put_recsrc;
 	void *private_data_recsrc;
 	void (*private_free_recsrc)(snd_mixer_oss_t *mixer);
+	struct semaphore reg_mutex;
+	snd_info_entry_t *proc_entry;
 	/* --- */
 	int oss_recsrc;
 };
diff -Nru a/include/sound/mpu401.h b/include/sound/mpu401.h
--- a/include/sound/mpu401.h	Tue Oct  1 17:08:04 2002
+++ b/include/sound/mpu401.h	Tue Oct  1 17:08:04 2002
@@ -40,6 +40,7 @@
 #define MPU401_HW_YMFPCI		14	/* YMF DS-XG PCI */
 #define MPU401_HW_CMIPCI		15	/* CMIPCI MPU-401 UART */
 #define MPU401_HW_ALS4000		16	/* Avance Logic ALS4000 */
+#define MPU401_HW_INTEL8X0		17	/* Intel8x0 driver */
 
 #define MPU401_MODE_BIT_INPUT		0
 #define MPU401_MODE_BIT_OUTPUT		1
diff -Nru a/include/sound/sndmagic.h b/include/sound/sndmagic.h
--- a/include/sound/sndmagic.h	Tue Oct  1 17:08:04 2002
+++ b/include/sound/sndmagic.h	Tue Oct  1 17:08:04 2002
@@ -140,9 +140,9 @@
 #define snd_usb_audio_t_magic			0xa15a3e01
 #define usb_mixer_elem_info_t_magic		0xa15a3e02
 #define snd_usb_stream_t_magic			0xa15a3e03
-#define usbmidi_t_magic				0xa15a3f01
-#define usbmidi_out_endpoint_t_magic		0xa15a3f02
-#define usbmidi_in_endpoint_t_magic		0xa15a3f03
+#define snd_usb_midi_t_magic			0xa15a3f01
+#define snd_usb_midi_out_endpoint_t_magic	0xa15a3f02
+#define snd_usb_midi_in_endpoint_t_magic	0xa15a3f03
 
 
 #else
diff -Nru a/include/sound/version.h b/include/sound/version.h
--- a/include/sound/version.h	Tue Oct  1 17:08:04 2002
+++ b/include/sound/version.h	Tue Oct  1 17:08:04 2002
@@ -1,3 +1,3 @@
 /* include/version.h.  Generated automatically by configure.  */
 #define CONFIG_SND_VERSION "0.9.0rc3"
-#define CONFIG_SND_DATE " (Wed Aug 21 14:00:18 2002 UTC)"
+#define CONFIG_SND_DATE " (Mon Aug 26 16:28:35 2002 UTC)"
diff -Nru a/sound/core/Makefile b/sound/core/Makefile
--- a/sound/core/Makefile	Tue Oct  1 17:08:04 2002
+++ b/sound/core/Makefile	Tue Oct  1 17:08:04 2002
@@ -95,6 +95,7 @@
   obj-$(CONFIG_SND_SB16) += snd-hwdep.o
   obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o
 endif
+obj-$(CONFIG_SND_USB_AUDIO) += snd-pcm.o snd-timer.o snd.o
 
 obj-m := $(sort $(obj-m))
 
diff -Nru a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
--- a/sound/core/oss/mixer_oss.c	Tue Oct  1 17:08:04 2002
+++ b/sound/core/oss/mixer_oss.c	Tue Oct  1 17:08:04 2002
@@ -472,6 +472,8 @@
 	int channels;
 	snd_kcontrol_t *kcontrol[SNDRV_MIXER_OSS_ITEM_COUNT];
 	unsigned int capture_item;
+	struct snd_mixer_oss_assign_table *assigned;
+	unsigned int allocated: 1;
 };
 
 static snd_kcontrol_t *snd_mixer_oss_test_id(snd_mixer_oss_t *mixer, const char *name, int index)
@@ -809,10 +811,26 @@
 
 static void snd_mixer_oss_slot_free(snd_mixer_oss_slot_t *chn)
 {
-	kfree(chn->private_data);
+	struct slot *p = (struct slot *)chn->private_data;
+	if (p) {
+		if (p->allocated && p->assigned) {
+			kfree(p->assigned->name);
+			kfree(p->assigned);
+		}
+		kfree(p);
+	}
+}
+
+static void mixer_slot_clear(snd_mixer_oss_slot_t *rslot)
+{
+	int idx = rslot->number; /* remember this */
+	if (rslot->private_free)
+		rslot->private_free(rslot);
+	memset(rslot, 0, sizeof(*rslot));
+	rslot->number = idx;
 }
 
-static void snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_oss_assign_table *ptr)
+static int snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_oss_assign_table *ptr, int ptr_allocated)
 {
 	struct slot slot;
 	struct slot *pslot;
@@ -823,49 +841,49 @@
 	memset(&slot, 0, sizeof(slot));
 	if (snd_mixer_oss_build_test(mixer, &slot, ptr->name, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_GLOBAL))
-		return;
+		return 0;
 	sprintf(str, "%s Switch", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_GSWITCH))
-		return;
+		return 0;
 	sprintf(str, "%s Route", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_GROUTE))
-		return;
+		return 0;
 	sprintf(str, "%s Volume", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_GVOLUME))
-		return;
+		return 0;
 	sprintf(str, "%s Playback Switch", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_PSWITCH))
-		return;
+		return 0;
 	sprintf(str, "%s Playback Route", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_PROUTE))
-		return;
+		return 0;
 	sprintf(str, "%s Playback Volume", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_PVOLUME))
-		return;
+		return 0;
 	sprintf(str, "%s Capture Switch", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_CSWITCH))
-		return;
+		return 0;
 	sprintf(str, "%s Capture Route", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_CROUTE))
-		return;
+		return 0;
 	sprintf(str, "%s Capture Volume", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_CVOLUME))
-		return;
+		return 0;
 	if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) {
 		snd_ctl_elem_info_t uinfo;
 
 		memset(&uinfo, 0, sizeof(uinfo));
 		if (kctl->info(kctl, &uinfo))
-			return;
+			return 0;
 		strcpy(str, ptr->name);
 		if (!strcmp(str, "Master"))
 			strcpy(str, "Mix");
@@ -878,7 +896,7 @@
 			for (slot.capture_item = 1; slot.capture_item < uinfo.value.enumerated.items; slot.capture_item++) {
 				uinfo.value.enumerated.item = slot.capture_item;
 				if (kctl->info(kctl, &uinfo))
-					return;
+					return 0;
 				if (!strcmp(uinfo.value.enumerated.name, str)) {
 					slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
 					break;
@@ -888,10 +906,13 @@
 	}
 	if (slot.present != 0) {
 		pslot = (struct slot *)kmalloc(sizeof(slot), GFP_KERNEL);
-		snd_runtime_check(pslot != NULL, return);
+		snd_runtime_check(pslot != NULL, return -ENOMEM);
 		*pslot = slot;
 		pslot->signature = SNDRV_MIXER_OSS_SIGNATURE;
+		pslot->assigned = ptr;
+		pslot->allocated = ptr_allocated;
 		rslot = &mixer->slots[ptr->oss_id];
+		mixer_slot_clear(rslot);
 		rslot->stereo = slot.channels > 1 ? 1 : 0;
 		rslot->get_volume = snd_mixer_oss_get_volume1;
 		rslot->put_volume = snd_mixer_oss_put_volume1;
@@ -907,6 +928,156 @@
 		}
 		rslot->private_data = pslot;
 		rslot->private_free = snd_mixer_oss_slot_free;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ */
+#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name
+static char *oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = {
+	MIXER_VOL(VOLUME),
+	MIXER_VOL(BASS),
+	MIXER_VOL(TREBLE),
+	MIXER_VOL(SYNTH),
+	MIXER_VOL(PCM),
+	MIXER_VOL(SPEAKER),
+	MIXER_VOL(LINE),
+	MIXER_VOL(MIC),
+	MIXER_VOL(CD),
+	MIXER_VOL(IMIX),
+	MIXER_VOL(ALTPCM),
+	MIXER_VOL(RECLEV),
+	MIXER_VOL(IGAIN),
+	MIXER_VOL(OGAIN),
+	MIXER_VOL(LINE1),
+	MIXER_VOL(LINE2),
+	MIXER_VOL(LINE3),
+	MIXER_VOL(DIGITAL1),
+	MIXER_VOL(DIGITAL2),
+	MIXER_VOL(DIGITAL3),
+	MIXER_VOL(PHONEIN),
+	MIXER_VOL(PHONEOUT),
+	MIXER_VOL(VIDEO),
+	MIXER_VOL(RADIO),
+	MIXER_VOL(MONITOR),
+};
+	
+/*
+ *  /proc interface
+ */
+
+static void snd_mixer_oss_proc_read(snd_info_entry_t *entry,
+				    snd_info_buffer_t * buffer)
+{
+	snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, entry->private_data, return);
+	int i;
+
+	down(&mixer->reg_mutex);
+	for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) {
+		struct slot *p;
+
+		if (! oss_mixer_names[i])
+			continue;
+		p = (struct slot *)mixer->slots[i].private_data;
+		snd_iprintf(buffer, "%s ", oss_mixer_names[i]);
+		if (p && p->assigned)
+			snd_iprintf(buffer, "\"%s\" %d\n",
+				    p->assigned->name,
+				    p->assigned->index);
+		else
+			snd_iprintf(buffer, "\"\" 0\n");
+	}
+	up(&mixer->reg_mutex);
+}
+
+static void snd_mixer_oss_proc_write(snd_info_entry_t *entry,
+				     snd_info_buffer_t * buffer)
+{
+	snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, entry->private_data, return);
+	char line[128], str[32], idxstr[16], *cptr;
+	int ch, idx;
+	struct snd_mixer_oss_assign_table *tbl;
+	struct slot *slot;
+
+	while (!snd_info_get_line(buffer, line, sizeof(line))) {
+		cptr = snd_info_get_str(str, line, sizeof(str));
+		for (ch = 0; ch < SNDRV_OSS_MAX_MIXERS; ch++)
+			if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0)
+				break;
+		if (ch >= SNDRV_OSS_MAX_MIXERS) {
+			snd_printk(KERN_ERR "mixer_oss: invalid OSS volume '%s'\n", str);
+			continue;
+		}
+		cptr = snd_info_get_str(str, cptr, sizeof(str));
+		if (! *str) {
+			/* remove the entry */
+			down(&mixer->reg_mutex);
+			mixer_slot_clear(&mixer->slots[ch]);
+			up(&mixer->reg_mutex);
+			continue;
+		}
+		snd_info_get_str(idxstr, cptr, sizeof(idxstr));
+		idx = simple_strtoul(idxstr, NULL, 10);
+		if (idx >= 0x4000) { /* too big */
+			snd_printk(KERN_ERR "mixer_oss: invalid index %d\n", idx);
+			continue;
+		}
+		down(&mixer->reg_mutex);
+		slot = (struct slot *)mixer->slots[ch].private_data;
+		if (slot && slot->assigned &&
+		    slot->assigned->index == idx && ! strcmp(slot->assigned->name, str))
+			/* not changed */
+			goto __unlock;
+		tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
+		if (! tbl) {
+			snd_printk(KERN_ERR "mixer_oss: no memory\n");
+			goto __unlock;
+		}
+		tbl->oss_id = ch;
+		tbl->name = snd_kmalloc_strdup(str, GFP_KERNEL);
+		if (! tbl->name) {
+			kfree(tbl);
+			goto __unlock;
+		}
+		tbl->index = idx;
+		if (snd_mixer_oss_build_input(mixer, tbl, 1) <= 0) {
+			kfree(tbl->name);
+			kfree(tbl);
+		}
+	__unlock:
+		up(&mixer->reg_mutex);
+	}
+}
+
+static void snd_mixer_oss_proc_init(snd_mixer_oss_t *mixer)
+{
+	snd_info_entry_t *entry;
+
+	entry = snd_info_create_card_entry(mixer->card, "oss_mixer",
+					   mixer->card->proc_root);
+	if (! entry)
+		return;
+	entry->content = SNDRV_INFO_CONTENT_TEXT;
+	entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+	entry->c.text.read_size = 8192;
+	entry->c.text.read = snd_mixer_oss_proc_read;
+	entry->c.text.write_size = 8192;
+	entry->c.text.write = snd_mixer_oss_proc_write;
+	entry->private_data = mixer;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		entry = NULL;
+	}
+	mixer->proc_entry = entry;
+}
+
+static void snd_mixer_oss_proc_done(snd_mixer_oss_t *mixer)
+{
+	if (mixer->proc_entry) {
+		snd_info_unregister(mixer->proc_entry);
+		mixer->proc_entry = NULL;
 	}
 }
 
@@ -945,9 +1116,9 @@
 	int idx;
 	
 	for (idx = 0; idx < sizeof(table) / sizeof(struct snd_mixer_oss_assign_table); idx++)
-		snd_mixer_oss_build_input(mixer, &table[idx]);
+		snd_mixer_oss_build_input(mixer, &table[idx], 0);
 	if (mixer->slots[SOUND_MIXER_SYNTH].get_volume == NULL)
-		snd_mixer_oss_build_input(mixer, &fm_table);
+		snd_mixer_oss_build_input(mixer, &fm_table, 0);
 	if (mixer->mask_recsrc) {
 		mixer->get_recsrc = snd_mixer_oss_get_recsrc2;
 		mixer->put_recsrc = snd_mixer_oss_put_recsrc2;
@@ -968,7 +1139,7 @@
 	card = mixer->card;
 	snd_assert(mixer == card->mixer_oss, return -ENXIO);
 	card->mixer_oss = NULL;
-	for (idx = 0; idx < 31; idx++) {
+	for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) {
 		snd_mixer_oss_slot_t *chn = &mixer->slots[idx];
 		if (chn->private_free)
 			chn->private_free(chn);
@@ -987,6 +1158,7 @@
 		mixer = snd_magic_kcalloc(snd_mixer_oss_t, sizeof(snd_mixer_oss_t), GFP_KERNEL);
 		if (mixer == NULL)
 			return -ENOMEM;
+		init_MUTEX(&mixer->reg_mutex);
 		sprintf(name, "mixer%i%i", card->number, 0);
 		if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
 						   card, 0,
@@ -1001,16 +1173,18 @@
 		snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS,
 				      card->number,
 				      name);
-		for (idx = 0; idx < 31; idx++)
+		for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++)
 			mixer->slots[idx].number = idx;
 		card->mixer_oss = mixer;
 		snd_mixer_oss_build(mixer);
+		snd_mixer_oss_proc_init(mixer);
 	} else {
 		snd_mixer_oss_t *mixer = card->mixer_oss;
 		if (mixer == NULL)
 			return 0;
 		snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);
 		snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
+		snd_mixer_oss_proc_done(mixer);
 		return snd_mixer_oss_free1(mixer);
 	}
 	return 0;
diff -Nru a/sound/core/pcm_native.c b/sound/core/pcm_native.c
--- a/sound/core/pcm_native.c	Tue Oct  1 17:08:04 2002
+++ b/sound/core/pcm_native.c	Tue Oct  1 17:08:04 2002
@@ -173,11 +173,11 @@
 			continue;
 #ifdef RULES_DEBUG
 		printk("%s = ", snd_pcm_hw_param_names[k]);
-		printk("%x -> ", *m);
+		printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
 #endif
 		changed = snd_mask_refine(m, constrs_mask(constrs, k));
 #ifdef RULES_DEBUG
-		printk("%x\n", *m);
+		printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
 #endif
 		if (changed)
 			params->cmask |= 1 << k;
diff -Nru a/sound/core/rawmidi.c b/sound/core/rawmidi.c
--- a/sound/core/rawmidi.c	Tue Oct  1 17:08:04 2002
+++ b/sound/core/rawmidi.c	Tue Oct  1 17:08:04 2002
@@ -820,11 +820,6 @@
 	return -ENOIOCTLCMD;
 }
 
-void snd_rawmidi_receive_reset(snd_rawmidi_substream_t * substream)
-{
-	/* TODO: reset current state */
-}
-
 int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, const unsigned char *buffer, int count)
 {
 	unsigned long flags;
@@ -969,11 +964,6 @@
 	return result;
 }
 
-void snd_rawmidi_transmit_reset(snd_rawmidi_substream_t * substream)
-{
-	/* TODO: reset current state */
-}
-
 int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream)
 {
 	snd_rawmidi_runtime_t *runtime = substream->runtime;
@@ -1564,9 +1554,7 @@
 EXPORT_SYMBOL(snd_rawmidi_drop_output);
 EXPORT_SYMBOL(snd_rawmidi_drain_output);
 EXPORT_SYMBOL(snd_rawmidi_drain_input);
-EXPORT_SYMBOL(snd_rawmidi_receive_reset);
 EXPORT_SYMBOL(snd_rawmidi_receive);
-EXPORT_SYMBOL(snd_rawmidi_transmit_reset);
 EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
 EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
 EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
diff -Nru a/sound/core/seq/Makefile b/sound/core/seq/Makefile
--- a/sound/core/seq/Makefile	Tue Oct  1 17:08:04 2002
+++ b/sound/core/seq/Makefile	Tue Oct  1 17:08:04 2002
@@ -73,6 +73,7 @@
 obj-$(CONFIG_SND_EMU10K1) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-virmidi.o
 obj-$(CONFIG_SND_TRIDENT) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
 obj-$(CONFIG_SND_YMFPCI) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_USB_AUDIO) += snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-virmidi.o
 
 obj-m := $(sort $(obj-m))
 
diff -Nru a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c
--- a/sound/isa/gus/gus_uart.c	Tue Oct  1 17:08:04 2002
+++ b/sound/isa/gus/gus_uart.c	Tue Oct  1 17:08:04 2002
@@ -50,7 +50,6 @@
 		if (stat & 0x10) {	/* framing error */
 			gus->gf1.uart_framing++;
 			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
-			snd_rawmidi_receive_reset(gus->midi_substream_input);
 			continue;
 		}
 		byte = snd_gf1_uart_get(gus);
@@ -58,7 +57,6 @@
 		snd_rawmidi_receive(gus->midi_substream_input, &byte, 1);
 		if (stat & 0x20) {
 			gus->gf1.uart_overrun++;
-			snd_rawmidi_receive_reset(gus->midi_substream_input);
 		}
 	}
 }
diff -Nru a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
--- a/sound/pci/ac97/ac97_codec.c	Tue Oct  1 17:08:04 2002
+++ b/sound/pci/ac97/ac97_codec.c	Tue Oct  1 17:08:04 2002
@@ -1501,7 +1501,7 @@
 				goto __access_ok;
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout(HZ/100);
-		} while (end_time - (signed long)jiffies >= 0);
+		} while (time_after_eq(end_time, jiffies));
 		snd_printd("AC'97 %d:%d does not respond - RESET [REC_GAIN = 0x%x]\n", ac97->num, ac97->addr, err);
 		snd_ac97_free(ac97);
 		return -ENXIO;
@@ -1536,31 +1536,38 @@
 			goto __ready_ok;
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout(HZ/10);
-	} while (end_time - (signed long)jiffies >= 0);
+	} while (time_after_eq(end_time, jiffies));
 	snd_printk("AC'97 %d:%d analog subsections not ready\n", ac97->num, ac97->addr);
       __ready_ok:
 	if (ac97->clock == 0)
 		ac97->clock = 48000;	/* standard value */
+	ac97->addr = (ac97->ext_id & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT;	
 	if (ac97->ext_id & 0x0189)	/* L/R, MIC, SDAC, LDAC VRA support */
 		snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, ac97->ext_id & 0x0189);
-	if (ac97->ext_id & 0x0001) {	/* VRA support */
-		snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates_front_dac);
-		snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates_adc);
+	if (ac97->ext_id & AC97_EI_VRA) {	/* VRA support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_FRONT_DAC]);
+		snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates[AC97_RATES_ADC]);
 	} else {
-		ac97->rates_front_dac = SNDRV_PCM_RATE_48000;
-		ac97->rates_adc = SNDRV_PCM_RATE_48000;
+		ac97->rates[AC97_RATES_FRONT_DAC] = SNDRV_PCM_RATE_48000;
+		ac97->rates[AC97_RATES_ADC] = SNDRV_PCM_RATE_48000;
 	}
-	if (ac97->ext_id & 0x0008) {	/* MIC VRA support */
-		snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates_mic_adc);
+	if (ac97->ext_id & AC97_EI_SPDIF) {
+		/* codec specific code (patch) should override these values */
+		ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_44100 |
+						SNDRV_PCM_RATE_32000;
+	}
+	if (ac97->ext_id & AC97_EI_VRM) {	/* MIC VRA support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates[AC97_RATES_MIC_ADC]);
 	} else {
-		ac97->rates_mic_adc = SNDRV_PCM_RATE_48000;
+		ac97->rates[AC97_RATES_MIC_ADC] = SNDRV_PCM_RATE_48000;
 	}
-	if (ac97->ext_id & 0x0080) {	/* SDAC support */
-		snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates_surr_dac);
+	if (ac97->ext_id & AC97_EI_SDAC) {	/* SDAC support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]);
 		ac97->scaps |= AC97_SCAP_SURROUND_DAC;
 	}
-	if (ac97->ext_id & 0x0100) {	/* LDAC support */
-		snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates_lfe_dac);
+	if (ac97->ext_id & AC97_EI_LDAC) {	/* LDAC support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]);
 		ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC;
 	}
 	/* additional initializations */
@@ -1849,54 +1856,35 @@
 {
 	unsigned short mask;
 	unsigned int tmp;
-	signed long end_time;
 	
 	switch (reg) {
 	case AC97_PCM_MIC_ADC_RATE:
 		mask = 0x0000;
-		if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0008) == 0)	/* MIC VRA */
+		if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0)	/* MIC VRA */
 			if (rate != 48000)
 				return -EINVAL;
 		break;
 	case AC97_PCM_FRONT_DAC_RATE:
 		mask = 0x0200;
-		if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0001) == 0)	/* VRA */
+		if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRA) == 0)	/* VRA */
 			if (rate != 48000)
 				return -EINVAL;
 		break;
 	case AC97_PCM_LR_ADC_RATE:
 		mask = 0x0100;
-		if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0001) == 0)	/* VRA */
+		if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRA) == 0)	/* VRA */
 			if (rate != 48000)
 				return -EINVAL;
 		break;
+	case AC97_SPDIF:
+		return 0;
 	default: return -EINVAL;
 	}
 	tmp = ((unsigned int)rate * ac97->clock) / 48000;
 	if (tmp > 65535)
 		return -EINVAL;
-	snd_ac97_update_bits(ac97, AC97_POWERDOWN, mask, mask);
-	end_time = jiffies + (HZ / 50);
-	do {
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	} while (end_time - (signed long)jiffies >= 0);
 	snd_ac97_update(ac97, reg, tmp & 0xffff);
-	udelay(10);
-	// XXXX update spdif rate here too?
-	snd_ac97_update_bits(ac97, AC97_POWERDOWN, mask, 0);
-	end_time = jiffies + (HZ / 50);
-	do {
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	} while (end_time - (signed long)jiffies >= 0);
-	end_time = jiffies + (HZ / 10);
-	do {
-		if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0003) == 0x0003)
-			break;
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	} while (end_time - (signed long)jiffies >= 0);
+	snd_ac97_read(ac97, reg);
 	return 0;
 }
 
diff -Nru a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
--- a/sound/pci/ac97/ac97_patch.c	Tue Oct  1 17:08:04 2002
+++ b/sound/pci/ac97/ac97_patch.c	Tue Oct  1 17:08:04 2002
@@ -166,6 +166,7 @@
 	*/
 
 	ac97->flags |= AC97_CS_SPDIF; 
+	ac97->rates[AC97_RATES_SPDIF] &= ~SNDRV_PCM_RATE_32000;
         ac97->ext_id |= AC97_EA_SPDIF;	/* force the detection of spdif */
 	snd_ac97_write_cache(ac97, AC97_CSR_ACMODE, 0x0080);
 	return 0;
diff -Nru a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
--- a/sound/pci/cs46xx/cs46xx_lib.c	Tue Oct  1 17:08:04 2002
+++ b/sound/pci/cs46xx/cs46xx_lib.c	Tue Oct  1 17:08:04 2002
@@ -855,6 +855,7 @@
 
 	if (copy_from_user(hwbuf, src, bytes))
 		return -EFAULT;
+
 	spin_lock_irq(&runtime->lock);
 	snd_cs46xx_playback_transfer(substream, frames);
 	spin_unlock_irq(&runtime->lock);
@@ -885,15 +886,17 @@
 {
 	cs46xx_t *chip = snd_pcm_substream_chip(substream);
 	/*snd_pcm_runtime_t *runtime = substream->runtime;*/
+	int result = 0;
+
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
 	cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO);
+#else
+	spin_lock(&chip->reg_lock);
 #endif
-	int result = 0;
 
-	spin_lock(&chip->reg_lock);
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
+
 	if (! cpcm->pcm_channel) {
-		spin_unlock(&chip->reg_lock);
 		return -ENXIO;
 	}
 #endif
@@ -905,6 +908,7 @@
 			cs46xx_dsp_pcm_link(chip,cpcm->pcm_channel);
 		if (substream->runtime->periods != CS46XX_FRAGS)
 			snd_cs46xx_playback_transfer(substream, 0);
+
 		/* raise playback volume */
 		snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 0xE) << 2, 0x80008000);
 #else
@@ -920,6 +924,8 @@
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
+        /* mute channel */
+		snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 0xE) << 2, 0xffffffff);
 		if (!cpcm->pcm_channel->unlinked)
 			cs46xx_dsp_pcm_unlink(chip,cpcm->pcm_channel);
 #else
@@ -934,7 +940,11 @@
 		result = -EINVAL;
 		break;
 	}
+
+#ifndef CONFIG_SND_CS46XX_NEW_DSP
 	spin_unlock(&chip->reg_lock);
+#endif
+
 	return result;
 }
 
@@ -964,6 +974,7 @@
 		break;
 	}
 	spin_unlock(&chip->reg_lock);
+
 	return result;
 }
 
@@ -994,6 +1005,7 @@
 			return err;
 		substream->ops = &snd_cs46xx_playback_indirect_ops;
 	}
+
 	return 0;
 }
 
@@ -1011,6 +1023,7 @@
 	runtime->dma_area = NULL;
 	runtime->dma_addr = 0;
 	runtime->dma_bytes = 0;
+
 	return 0;
 }
 
@@ -1025,7 +1038,8 @@
 	cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO);
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
-	down (&chip->dsp_spos_instance->pcm_mutex);
+    down (&chip->spos_mutex);
+
 	if ( cpcm->pcm_channel->src_scb->ref_count == 1 &&
 	     cpcm->pcm_channel->sample_rate != runtime->rate) {
 		/* sample rate not set or we can reuse
@@ -1034,23 +1048,25 @@
 		cs46xx_dsp_set_src_sample_rate (chip,cpcm->pcm_channel->src_scb,runtime->rate);
 		cpcm->pcm_channel->sample_rate = runtime->rate;
 	} 
-	up (&chip->dsp_spos_instance->pcm_mutex);
 
 	if (cpcm->pcm_channel->sample_rate != runtime->rate &&
 	    cpcm->pcm_channel->src_scb->ref_count != 1) {
+		int unlinked = cpcm->pcm_channel->unlinked;
 		cs46xx_dsp_destroy_pcm_channel (chip,cpcm->pcm_channel);
 
 		if ( (cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, runtime->rate, cpcm, cpcm->hw_addr)) == NULL) {
 			snd_printk(KERN_ERR "cs46xx: failed to re-create virtual PCM channel\n");
+			up (&chip->spos_mutex);
 			return -ENXIO;
 		}
 
-		cs46xx_dsp_pcm_unlink (chip,cpcm->pcm_channel);
+		if (!unlinked) cs46xx_dsp_pcm_link (chip,cpcm->pcm_channel);
 		cpcm->pcm_channel->sample_rate = runtime->rate;
 	}
 
 	pfie = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2 );
 	pfie &= ~0x0000f03f;
+	up (&chip->spos_mutex);
 #else
 	/* old dsp */
 	pfie = snd_cs46xx_peek(chip, BA1_PFIE);
@@ -1130,6 +1146,7 @@
 			return err;
 		substream->ops = &snd_cs46xx_capture_indirect_ops;
 	}
+
 	return 0;
 }
 
@@ -1143,6 +1160,7 @@
 	runtime->dma_area = NULL;
 	runtime->dma_addr = 0;
 	runtime->dma_bytes = 0;
+
 	return 0;
 }
 
@@ -1158,6 +1176,7 @@
 	chip->capt.hw_data = chip->capt.hw_io = chip->capt.hw_ready = 0;
 	chip->capt.appl_ptr = 0;
 	snd_cs46xx_set_capture_sample_rate(chip, runtime->rate);
+
 	return 0;
 }
 
@@ -1183,6 +1202,7 @@
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
 	status2 = snd_cs46xx_peekBA0(chip, BA0_HSR0);
+
 	for (i = 0; i < DSP_MAX_PCM_CHANNELS; ++i) {
 		if (i <= 15) {
 			if ( status1 & (1 << i) ) {
@@ -1193,7 +1213,7 @@
 					if (ins->pcm_channels[i].active &&
 					    ins->pcm_channels[i].private_data &&
 					    !ins->pcm_channels[i].unlinked) {
-						cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, goto _invalid_pointer);
+						cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, continue);
 						snd_pcm_period_elapsed(cpcm->substream);
 					}
 				}
@@ -1203,16 +1223,13 @@
 				if (ins->pcm_channels[i].active && 
 				    ins->pcm_channels[i].private_data &&
 				    !ins->pcm_channels[i].unlinked) {
-					cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, goto _invalid_pointer);
+					cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, continue);
 					snd_pcm_period_elapsed(cpcm->substream);
 				}
 			}
 		}
-
-		continue;
-	_invalid_pointer:
-		printk (KERN_ERR "cs46xx: (interrupt) invalid pointer at pcm_channel[%d]\n",i);
 	}
+
 #else
 	/* old dsp */
 	if ((status1 & HISR_VC0) && chip->playback_pcm) {
@@ -1324,16 +1341,18 @@
 
 	cpcm->substream = substream;
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
+	down (&chip->spos_mutex);
 	cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, runtime->rate, cpcm, cpcm->hw_addr);
 
 	if (cpcm->pcm_channel == NULL) {
 		snd_printk(KERN_ERR "cs46xx: failed to create virtual PCM channel\n");
 		snd_free_pci_pages(chip->pci, cpcm->hw_size, cpcm->hw_area, cpcm->hw_addr);
 		snd_magic_kfree(cpcm);
+		up (&chip->spos_mutex);
 		return -ENOMEM;
 	}
 
-	cs46xx_dsp_pcm_unlink (chip,cpcm->pcm_channel);
+	up (&chip->spos_mutex);
 #else
 	chip->playback_pcm = cpcm; /* HACK */
 #endif
@@ -1342,6 +1361,7 @@
 		substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
 	chip->active_ctrl(chip, 1);
 	chip->amplifier_ctrl(chip, 1);
+
 	return 0;
 }
 
@@ -1353,10 +1373,13 @@
 		return -ENOMEM;
 	chip->capt.substream = substream;
 	substream->runtime->hw = snd_cs46xx_capture;
+
 	if (chip->accept_valid)
-		substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
+      substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
+
 	chip->active_ctrl(chip, 1);
 	chip->amplifier_ctrl(chip, 1);
+
 	return 0;
 }
 
@@ -1369,10 +1392,12 @@
 	cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO);
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
+	down (&chip->spos_mutex);
 	if (cpcm->pcm_channel) {
 		cs46xx_dsp_destroy_pcm_channel(chip,cpcm->pcm_channel);
 		cpcm->pcm_channel = NULL;
 	}
+	up (&chip->spos_mutex);
 #else
 	chip->playback_pcm = NULL;
 #endif
@@ -1381,6 +1406,7 @@
 	snd_free_pci_pages(chip->pci, cpcm->hw_size, cpcm->hw_area, cpcm->hw_addr);
 	chip->active_ctrl(chip, -1);
 	chip->amplifier_ctrl(chip, -1);
+
 	return 0;
 }
 
@@ -1392,6 +1418,7 @@
 	snd_free_pci_pages(chip->pci, chip->capt.hw_size, chip->capt.hw_area, chip->capt.hw_addr);
 	chip->active_ctrl(chip, -1);
 	chip->amplifier_ctrl(chip, -1);
+
 	return 0;
 }
 
@@ -1550,6 +1577,7 @@
 		}
 #endif
 	}
+
 	return change;
 }
 
@@ -1575,6 +1603,7 @@
 		ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_out;
 	else
 		ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_in;
+
 	return 0;
 }
 
@@ -1719,7 +1748,6 @@
 
 #endif /* CONFIG_SND_CS46XX_NEW_DSP */
 
-
 #ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
 static int snd_cs46xx_egpio_select_info(snd_kcontrol_t *kcontrol, 
                                         snd_ctl_elem_info_t *uinfo)
@@ -1979,6 +2007,7 @@
     
 	/* add cs4630 mixer controls */
  _end:
+
 	/* dosoundcard specific mixer setup */
 	if (chip->mixer_init) {
 		snd_printdd ("calling chip->mixer_init(chip);\n");
@@ -2447,7 +2476,7 @@
 		chip->active_ctrl(chip, -chip->amplifier);
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
-	cs46xx_dsp_spos_destroy(chip->dsp_spos_instance);
+	cs46xx_dsp_spos_destroy(chip);
 #endif
 	snd_magic_kfree(chip);
 	return 0;
@@ -2920,7 +2949,6 @@
 #endif
 }
 
-
 static void hercules_init(cs46xx_t *chip) 
 {
 	/* default: AMP off, and SPDIF input optical */
@@ -2928,6 +2956,7 @@
 	snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIODR_GPOE0);
 }
 
+
 /*
  *	Game Theatre XP card - EGPIO[2] is used to enable the external amp.
  */ 
@@ -2940,6 +2969,7 @@
 	chip->amplifier += change;
 	if (chip->amplifier && !old) {
 		snd_printdd ("Hercules amplifier ON\n");
+
 		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, 
 				   EGPIODR_GPOE2 | val1);     /* enable EGPIO2 output */
 		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, 
@@ -2979,6 +3009,7 @@
 	}
 #endif
 }
+
 
 #if 0
 /*
diff -Nru a/sound/pci/cs46xx/cs46xx_lib.h b/sound/pci/cs46xx/cs46xx_lib.h
--- a/sound/pci/cs46xx/cs46xx_lib.h	Tue Oct  1 17:08:04 2002
+++ b/sound/pci/cs46xx/cs46xx_lib.h	Tue Oct  1 17:08:04 2002
@@ -28,11 +28,11 @@
  *  constants
  */
 
-#define CS46XX_BA0_SIZE		0x1000
-#define CS46XX_BA1_DATA0_SIZE	0x3000
-#define CS46XX_BA1_DATA1_SIZE	0x3800
-#define CS46XX_BA1_PRG_SIZE	0x7000
-#define CS46XX_BA1_REG_SIZE	0x0100
+#define CS46XX_BA0_SIZE		  0x1000
+#define CS46XX_BA1_DATA0_SIZE 0x3000
+#define CS46XX_BA1_DATA1_SIZE 0x3800
+#define CS46XX_BA1_PRG_SIZE	  0x7000
+#define CS46XX_BA1_REG_SIZE	  0x0100
 
 
 #define CS46XX_PERIOD_SIZE 2048
@@ -84,7 +84,7 @@
 }
 
 dsp_spos_instance_t *  cs46xx_dsp_spos_create (cs46xx_t * chip);
-void                   cs46xx_dsp_spos_destroy (dsp_spos_instance_t * instance);
+void                   cs46xx_dsp_spos_destroy (cs46xx_t * chip);
 int                    cs46xx_dsp_load_module (cs46xx_t * chip,dsp_module_desc_t * module);
 symbol_entry_t *       cs46xx_dsp_lookup_symbol (cs46xx_t * chip,char * symbol_name,int symbol_type);
 symbol_entry_t *       cs46xx_dsp_lookup_symbol_addr (cs46xx_t * chip,u32 address,int symbol_type);
diff -Nru a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
--- a/sound/pci/cs46xx/dsp_spos.c	Tue Oct  1 17:08:04 2002
+++ b/sound/pci/cs46xx/dsp_spos.c	Tue Oct  1 17:08:04 2002
@@ -222,16 +222,13 @@
 		return NULL;
 	memset(ins, 0, sizeof(*ins));
 
-	init_MUTEX(&ins->scb_mutex);
-	init_MUTEX(&ins->pcm_mutex);
-
 	/* better to use vmalloc for this big table */
 	ins->symbol_table.nsymbols = 0;
 	ins->symbol_table.symbols = vmalloc(sizeof(symbol_entry_t) * DSP_MAX_SYMBOLS);
 	ins->symbol_table.highest_frag_index = 0;
 
 	if (ins->symbol_table.symbols == NULL) {
-		cs46xx_dsp_spos_destroy(ins);
+		cs46xx_dsp_spos_destroy(chip);
 		return NULL;
 	}
 
@@ -240,7 +237,7 @@
 	ins->code.data = kmalloc(DSP_CODE_BYTE_SIZE, GFP_KERNEL);
 
 	if (ins->code.data == NULL) {
-		cs46xx_dsp_spos_destroy(ins);
+		cs46xx_dsp_spos_destroy(chip);
 		return NULL;
 	}
 
@@ -251,7 +248,7 @@
 	ins->modules = kmalloc(sizeof(dsp_module_desc_t) * DSP_MAX_MODULES, GFP_KERNEL);
 
 	if (ins->modules == NULL) {
-		cs46xx_dsp_spos_destroy(ins);
+		cs46xx_dsp_spos_destroy(chip);
 		return NULL;
 	}
 
@@ -265,19 +262,19 @@
 	return ins;
 }
 
-void  cs46xx_dsp_spos_destroy (dsp_spos_instance_t * ins)
+void  cs46xx_dsp_spos_destroy (cs46xx_t * chip)
 {
 	int i;
+	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 
 	snd_assert(ins != NULL, return);
 
-	down(&ins->scb_mutex);
+	down(&chip->spos_mutex);
 	for (i = 0; i < ins->nscb; ++i) {
 		if (ins->scbs[i].deleted) continue;
 
 		cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
 	}
-	up(&ins->scb_mutex);
 
 	if (ins->code.data)
 		kfree(ins->code.data);
@@ -286,6 +283,7 @@
 	if (ins->modules)
 		kfree(ins->modules);
 	kfree(ins);  
+	up(&chip->spos_mutex);
 }
 
 int cs46xx_dsp_load_module (cs46xx_t * chip, dsp_module_desc_t * module)
@@ -479,6 +477,7 @@
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 	int i,j;
 
+	down(&chip->spos_mutex);
 	snd_iprintf(buffer, "MODULES:\n");
 	for ( i = 0; i < ins->nmodules; ++i ) {
 		snd_iprintf(buffer, "\n%s:\n", ins->modules[i].module_name);
@@ -491,6 +490,7 @@
 				    desc->segment_type,desc->offset, desc->size);
 		}
 	}
+	up(&chip->spos_mutex);
 }
 
 static void cs46xx_dsp_proc_task_tree_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
@@ -500,6 +500,7 @@
 	int i,j,col;
 	unsigned long dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET;
 
+	down(&chip->spos_mutex);
 	snd_iprintf(buffer, "TASK TREES:\n");
 	for ( i = 0; i < ins->ntask; ++i) {
 		snd_iprintf(buffer,"\n%04x %s:\n",ins->tasks[i].address,ins->tasks[i].task_name);
@@ -516,6 +517,7 @@
 	}
 
 	snd_iprintf(buffer,"\n");  
+	up(&chip->spos_mutex);
 }
 
 static void cs46xx_dsp_proc_scb_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
@@ -524,6 +526,7 @@
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 	int i;
 
+	down(&chip->spos_mutex);
 	snd_iprintf(buffer, "SCB's:\n");
 	for ( i = 0; i < ins->nscb; ++i) {
 		if (ins->scbs[i].deleted)
@@ -546,6 +549,7 @@
 	}
 
 	snd_iprintf(buffer,"\n");
+	up(&chip->spos_mutex);
 }
 
 static void cs46xx_dsp_proc_parameter_dump_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
@@ -809,12 +813,14 @@
 	}
 	ins->proc_scb_info_entry = entry;
 
+	down(&chip->spos_mutex);
 	/* register/update SCB's entries on proc */
 	for (i = 0; i < ins->nscb; ++i) {
 		if (ins->scbs[i].deleted) continue;
 
 		cs46xx_dsp_proc_register_scb_desc (chip, (ins->scbs + i));
 	}
+	up(&chip->spos_mutex);
 
 	return 0;
 }
@@ -854,12 +860,12 @@
 		ins->proc_task_info_entry = NULL;
 	}
 
-	down(&ins->scb_mutex);
+	down(&chip->spos_mutex);
 	for (i = 0; i < ins->nscb; ++i) {
 		if (ins->scbs[i].deleted) continue;
 		cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
 	}
-	up(&ins->scb_mutex);
+	up(&chip->spos_mutex);
 
 	if (ins->proc_dsp_dir) {
 		snd_info_unregister (ins->proc_dsp_dir);
@@ -930,6 +936,7 @@
 	ins->scbs[index].proc_info = NULL;
 	ins->scbs[index].ref_count = 1;
 	ins->scbs[index].deleted = 0;
+	spin_lock_init(&ins->scbs[index].lock);
 
 	desc = (ins->scbs + index);
 	ins->scbs[index].scb_symbol = add_symbol (chip, name, dest, SYMBOL_PARAMETER);
@@ -1313,9 +1320,11 @@
 							     codec_out_scb,
 							     sec_codec_out_scb,
 							     SCB_ON_PARENT_SUBLIST_SCB);
+
 	if (!magic_snoop_scb) goto _fail_end;
 	ins->ref_snoop_scb = magic_snoop_scb;
 
+
 	/* The asynch. transfer task */
 	asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR,
 							   SPDIFO_SCB_INST,
@@ -1576,6 +1585,8 @@
 
 	snd_assert (ins->asynch_rx_scb == NULL,return -EINVAL);
 	snd_assert (ins->spdif_in_src != NULL,return -EINVAL);
+
+	down(&chip->spos_mutex);
 	/* create and start the asynchronous receiver SCB */
 	ins->asynch_rx_scb = cs46xx_dsp_create_asynch_fg_rx_scb(chip,"AsynchFGRxSCB",
 								ASYNCRX_SCB_ADDR,
@@ -1586,6 +1597,7 @@
 
 	save_flags(flags);
 	cli();
+
 	/* reset SPDIF input sample buffer pointer */
 	snd_cs46xx_poke (chip, (SPDIFI_SCB_INST + 0x0c) << 2,
 			 (SPDIFI_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC);
@@ -1605,6 +1617,7 @@
 
 	/* monitor state */
 	ins->spdif_status_in = 1;
+	up(&chip->spos_mutex);
 
 	return 0;
 }
@@ -1616,6 +1629,7 @@
 	snd_assert (ins->asynch_rx_scb != NULL, return -EINVAL);
 	snd_assert (ins->spdif_in_src != NULL,return -EINVAL);
 
+	down(&chip->spos_mutex);
 	/* Remove the asynchronous receiver SCB */
 	cs46xx_dsp_remove_scb (chip,ins->asynch_rx_scb);
 	ins->asynch_rx_scb = NULL;
@@ -1624,6 +1638,7 @@
 
 	/* monitor state */
 	ins->spdif_status_in = 0;
+	up(&chip->spos_mutex);
 
 	/* restore amplifier */
 	chip->active_ctrl(chip, -1);
@@ -1639,9 +1654,11 @@
 	snd_assert (ins->pcm_input == NULL,return -EINVAL);
 	snd_assert (ins->ref_snoop_scb != NULL,return -EINVAL);
 
+	down(&chip->spos_mutex);
 	ins->pcm_input = cs46xx_add_record_source(chip,ins->ref_snoop_scb,PCMSERIALIN_PCM_SCB_ADDR,
                                                   "PCMSerialInput_Wave");
-                                                  
+	up(&chip->spos_mutex);
+
 	return 0;
 }
 
@@ -1651,8 +1668,10 @@
 
 	snd_assert (ins->pcm_input != NULL,return -EINVAL);
 
+	down(&chip->spos_mutex);
 	cs46xx_dsp_remove_scb (chip,ins->pcm_input);
 	ins->pcm_input = NULL;
+	up(&chip->spos_mutex);
 
 	return 0;
 }
@@ -1664,9 +1683,11 @@
 	snd_assert (ins->adc_input == NULL,return -EINVAL);
 	snd_assert (ins->codec_in_scb != NULL,return -EINVAL);
 
+	down(&chip->spos_mutex);
 	ins->adc_input = cs46xx_add_record_source(chip,ins->codec_in_scb,PCMSERIALIN_SCB_ADDR,
 						  "PCMSerialInput_ADC");
-                                                  
+	up(&chip->spos_mutex);
+
 	return 0;
 }
 
@@ -1676,8 +1697,10 @@
 
 	snd_assert (ins->adc_input != NULL,return -EINVAL);
 
+    down(&chip->spos_mutex);
 	cs46xx_dsp_remove_scb (chip,ins->adc_input);
 	ins->adc_input = NULL;
+    up(&chip->spos_mutex);
 
 	return 0;
 }
@@ -1697,7 +1720,7 @@
 
 	snd_cs46xx_poke(chip,( SPIOWRITE_SCB_ADDR      << 2), temp);
 	snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 1) << 2), data); /* offset 1 <-- data1 */
-	snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 2) << 2), data); /* offset 1 <-- data2*/
+	snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 2) << 2), data); /* offset 1 <-- data2 */
     
 	/* Poke this location to tell the task to start */
 	snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 6) << 2), SPIOWRITE_SCB_ADDR << 0x10);
diff -Nru a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c
--- a/sound/pci/cs46xx/dsp_spos_scb_lib.c	Tue Oct  1 17:08:04 2002
+++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c	Tue Oct  1 17:08:04 2002
@@ -75,7 +75,7 @@
 
 	ins = chip->dsp_spos_instance;
 
-	down(&ins->scb_mutex);
+	down(&chip->spos_mutex);
 	snd_iprintf(buffer,"%04x %s:\n",scb->address,scb->scb_name);
 
 	for (col = 0,j = 0;j < 0x10; j++,col++) {
@@ -103,7 +103,7 @@
 		    scb->task_entry->address);
 
 	snd_iprintf(buffer,"index [%d] ref_count [%d]\n",scb->index,scb->ref_count);  
-	up(&ins->scb_mutex);
+	up(&chip->spos_mutex);
 }
 
 static void _dsp_unlink_scb (cs46xx_t *chip,dsp_scb_descriptor_t * scb)
@@ -180,11 +180,10 @@
 {
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 
-	down(&ins->scb_mutex);
 	/* check integrety */
 	snd_assert ( (scb->index >= 0 && 
 		      scb->index < ins->nscb && 
-		      (ins->scbs + scb->index) == scb), goto _end );
+		      (ins->scbs + scb->index) == scb), return );
 
 #if 0
 	/* cant remove a SCB with childs before 
@@ -194,10 +193,12 @@
 		     goto _end);
 #endif
 
+	spin_lock(&scb->lock);
 	_dsp_unlink_scb (chip,scb);
-  
+	spin_unlock(&scb->lock);
+
 	cs46xx_dsp_proc_free_scb_desc(scb);
-	snd_assert (scb->scb_symbol != NULL, goto _end);
+	snd_assert (scb->scb_symbol != NULL, return );
 	remove_symbol (chip,scb->scb_symbol);
 
 	ins->scbs[scb->index].deleted = 1;
@@ -219,11 +220,6 @@
 		ins->scbs[i - 1].index = i - 1;
 	}
 #endif
-
-#ifdef CONFIG_SND_DEBUG
- _end:
-#endif
-	up(&ins->scb_mutex);
 }
 
 
@@ -248,7 +244,6 @@
 	snd_info_entry_t * entry;
 	proc_scb_info_t * scb_info;
 
-	down(&ins->scb_mutex);
 	/* register to proc */
 	if (ins->snd_card != NULL && ins->proc_dsp_dir != NULL &&
 	    scb->proc_info == NULL) {
@@ -275,7 +270,6 @@
 
 		scb->proc_info = entry;
 	}
-	up(&ins->scb_mutex);
 }
 
 static dsp_scb_descriptor_t * 
@@ -289,8 +283,7 @@
   
 	unsigned long flags;
 
-	down(&ins->scb_mutex);
-	snd_assert (ins->the_null_scb != NULL,goto _fail_end);
+	snd_assert (ins->the_null_scb != NULL,return NULL);
 
 	/* fill the data that will be wroten to DSP */
 	scb_data[SCBsubListPtr] = 
@@ -321,17 +314,17 @@
 		/* link to  parent SCB */
 		if (scb_child_type == SCB_ON_PARENT_NEXT_SCB) {
 			snd_assert ( (scb->parent_scb_ptr->next_scb_ptr == ins->the_null_scb),
-				     goto _fail_end);
+				     return NULL);
 
 			scb->parent_scb_ptr->next_scb_ptr = scb;
 
 		} else if (scb_child_type == SCB_ON_PARENT_SUBLIST_SCB) {
 			snd_assert ( (scb->parent_scb_ptr->sub_list_ptr == ins->the_null_scb),
-				     goto _fail_end);
+				     return NULL);
 
 			scb->parent_scb_ptr->sub_list_ptr = scb;
 		} else {
-			snd_assert (0,goto _fail_end);
+			snd_assert (0,return NULL);
 		}
 
 		spin_lock_irqsave(&chip->reg_lock, flags);
@@ -345,17 +338,9 @@
 	}
 
 
-	up(&ins->scb_mutex);
-
 	cs46xx_dsp_proc_register_scb_desc (chip,scb);
 
 	return scb;
-#ifdef CONFIG_SND_DEBUG
- _fail_end:
-
-	up(&ins->scb_mutex);
-	return NULL;
-#endif
 }
 
 dsp_scb_descriptor_t * 
@@ -914,6 +899,7 @@
 		/* There is no correct initial value, it will depend upon the detected
 		   rate etc  */
 		0x18000000,         
+
 		/* Mute stream */
 		0x8000,0x8000,       
 		0xffff,0xffff
@@ -1111,13 +1097,11 @@
 {
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 	dsp_scb_descriptor_t * src_scb = NULL,* pcm_scb;
-	dsp_scb_descriptor_t * pcm_parent_scb;
+	/*dsp_scb_descriptor_t * pcm_parent_scb;*/
 	char scb_name[DSP_MAX_SCB_NAME];
 	int i,pcm_index = -1, insert_point, src_index = -1;
 	unsigned long flags;
 
-	down(&ins->pcm_mutex); 
-
 	/* default sample rate is 44100 */
 	if (!sample_rate) sample_rate = 44100;
 
@@ -1142,7 +1126,7 @@
 
 	if (pcm_index == -1) {
 		snd_printk (KERN_ERR "dsp_spos: no free PCM channel\n");
-		goto _end;
+		return NULL;
 	}
 
 	if (src_scb == NULL) {
@@ -1150,7 +1134,7 @@
 
 		if (ins->nsrc_scb >= DSP_MAX_SRC_NR) {
 			snd_printk(KERN_ERR "dsp_spos: to many SRC instances\n!");
-			goto _end;
+			return NULL;
 		}
 
 		/* find a free slot */
@@ -1161,7 +1145,7 @@
 				break;
 			}
 		}
-		snd_assert (src_index != -1,goto _end);
+		snd_assert (src_index != -1,return NULL);
 
 		/* we need to create a new SRC SCB */
 		if (ins->master_mix_scb->sub_list_ptr == ins->the_null_scb) {
@@ -1185,32 +1169,13 @@
 
 		if (!src_scb) {
 			snd_printk (KERN_ERR "dsp_spos: failed to create SRCtaskSCB\n");
-			goto _end;
+			return NULL;
 		}
 
 		cs46xx_dsp_set_src_sample_rate(chip,src_scb,sample_rate);
 
 		ins->nsrc_scb ++;
-
-		/* insert point for the PCMreader task */
-		pcm_parent_scb = src_scb;
-		insert_point = SCB_ON_PARENT_SUBLIST_SCB;
-	} else {
-      
-      /* if channel is unlinked then src_scb->sub_list_ptr == null_scb, and
-         that's a correct state.
-        snd_assert (src_scb->sub_list_ptr != ins->the_null_scb, goto _end); 
-      */
-      if (src_scb->sub_list_ptr != ins->the_null_scb) {
-		pcm_parent_scb = find_next_free_scb(chip,src_scb->sub_list_ptr);
-        
-		insert_point = SCB_ON_PARENT_NEXT_SCB;
-      } else {
-        pcm_parent_scb = src_scb;
-		insert_point = SCB_ON_PARENT_SUBLIST_SCB;
-      }
-	}
-  
+	} 
   
   
 	snprintf (scb_name,DSP_MAX_SCB_NAME,"PCMReader_SCB%d",pcm_index);
@@ -1221,21 +1186,22 @@
 						   pcm_reader_buffer_addr[pcm_index],
 						   /* 0x200 - 400 PCMreader SCBs */
 						   (pcm_index * 0x10) + 0x200,
-						   pcm_index, /* virtual channel 0-31 */
-						   hw_dma_addr, /* pcm hw addr */
-						   pcm_parent_scb,
-						   insert_point);
+						   pcm_index,    /* virtual channel 0-31 */
+						   hw_dma_addr,  /* pcm hw addr */
+                           NULL,         /* parent SCB ptr */
+                           0             /* insert point */ 
+                           );
 
 	if (!pcm_scb) {
 		snd_printk (KERN_ERR "dsp_spos: failed to create PCMreaderSCB\n");
-		goto _end;
+		return NULL;
 	}
 
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	ins->pcm_channels[pcm_index].sample_rate = sample_rate;
 	ins->pcm_channels[pcm_index].pcm_reader_scb = pcm_scb;
 	ins->pcm_channels[pcm_index].src_scb = src_scb;
-	ins->pcm_channels[pcm_index].unlinked = 0;  
+	ins->pcm_channels[pcm_index].unlinked = 1;
 	ins->pcm_channels[pcm_index].private_data = private_data;
 	ins->pcm_channels[pcm_index].src_slot = src_index;
 	ins->pcm_channels[pcm_index].active = 1;
@@ -1243,12 +1209,7 @@
 	ins->npcm_channels ++;
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 
-	up(&ins->pcm_mutex);  
 	return (ins->pcm_channels + pcm_index);
- _end:
-
-	up(&ins->pcm_mutex);
-	return NULL;
 }
 
 void cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel)
@@ -1256,11 +1217,9 @@
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 	unsigned long flags;
 
-	down(&ins->pcm_mutex);
-  
-	snd_assert(pcm_channel->active,goto _end);
-	snd_assert(ins->npcm_channels > 0,goto _end);
-	snd_assert(pcm_channel->src_scb->ref_count > 0,goto _end);
+	snd_assert(pcm_channel->active, return );
+	snd_assert(ins->npcm_channels > 0, return );
+	snd_assert(pcm_channel->src_scb->ref_count > 0, return );
 
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	pcm_channel->unlinked = 1;
@@ -1276,18 +1235,11 @@
 		cs46xx_dsp_remove_scb(chip,pcm_channel->src_scb);
 
 		snd_assert (pcm_channel->src_slot >= 0 && pcm_channel->src_slot <= DSP_MAX_SRC_NR,
-			    goto _end);
+			    return );
 
 		ins->src_scb_slots[pcm_channel->src_slot] = 0;
 		ins->nsrc_scb --;
 	}
-
-
-#ifdef CONFIG_SND_DEBUG
- _end:
-#endif
-
-	up(&ins->pcm_mutex);
 }
 
 int cs46xx_dsp_pcm_unlink (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel)
@@ -1295,14 +1247,15 @@
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 	unsigned long flags;
 
-	down(&ins->pcm_mutex);
-	down(&ins->scb_mutex);
+	snd_assert(pcm_channel->active,return -EIO);
+	snd_assert(ins->npcm_channels > 0,return -EIO);
 
-	snd_assert(pcm_channel->active,goto _end);
-	snd_assert(ins->npcm_channels > 0,goto _end);
+	spin_lock(&pcm_channel->src_scb->lock);
 
-	if (pcm_channel->unlinked)
-		goto _end;
+	if (pcm_channel->unlinked) {
+		spin_unlock(&pcm_channel->src_scb->lock);
+		return -EIO;
+	}
 
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	pcm_channel->unlinked = 1;
@@ -1310,10 +1263,7 @@
 
 	_dsp_unlink_scb (chip,pcm_channel->pcm_reader_scb);
 
- _end:
-	up(&ins->scb_mutex);
-	up(&ins->pcm_mutex);
-
+	spin_unlock(&pcm_channel->src_scb->lock);
 	return 0;
 }
 
@@ -1324,26 +1274,33 @@
 	dsp_scb_descriptor_t * src_scb = pcm_channel->src_scb;
 	unsigned long flags;
 
-	down(&ins->pcm_mutex);
-	down(&ins->scb_mutex);
+	spin_lock(&pcm_channel->src_scb->lock);
 
-	if (pcm_channel->unlinked == 0)
-		goto _end;
+	if (pcm_channel->unlinked == 0) {
+		spin_unlock(&pcm_channel->src_scb->lock);
+		return -EIO;
+	}
 
-	if (src_scb->sub_list_ptr == ins->the_null_scb) {
-		parent_scb = src_scb;
-		parent_scb->sub_list_ptr = pcm_channel->pcm_reader_scb;
-	} else {
-		parent_scb = find_next_free_scb(chip,src_scb->sub_list_ptr);
-		parent_scb->next_scb_ptr = pcm_channel->pcm_reader_scb;
+	parent_scb = src_scb;
+
+	if (src_scb->sub_list_ptr != ins->the_null_scb) {
+		src_scb->sub_list_ptr->parent_scb_ptr = pcm_channel->pcm_reader_scb;
+		pcm_channel->pcm_reader_scb->next_scb_ptr = src_scb->sub_list_ptr;
 	}
-  
+
+	src_scb->sub_list_ptr = pcm_channel->pcm_reader_scb;
+
 	snd_assert (pcm_channel->pcm_reader_scb->parent_scb_ptr == NULL, ; );
 	pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb;
 
 	/* update entry in DSP RAM */
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	snd_cs46xx_poke(chip,
+			(pcm_channel->pcm_reader_scb->address + SCBsubListPtr) << 2,
+			(pcm_channel->pcm_reader_scb->sub_list_ptr->address << 0x10) |
+			(pcm_channel->pcm_reader_scb->next_scb_ptr->address));
+    
+	snd_cs46xx_poke(chip,
 			(parent_scb->address + SCBsubListPtr) << 2,
 			(parent_scb->sub_list_ptr->address << 0x10) |
 			(parent_scb->next_scb_ptr->address));
@@ -1351,13 +1308,11 @@
 	pcm_channel->unlinked = 0;
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 
- _end:
-	up(&ins->scb_mutex);
-	up(&ins->pcm_mutex);
-
+	spin_unlock(&pcm_channel->src_scb->lock);
 	return 0;
 }
 
+
 #define GOF_PER_SEC 200
   
 void cs46xx_dsp_set_src_sample_rate(cs46xx_t *chip,dsp_scb_descriptor_t * src, u32 rate)
@@ -1414,9 +1369,8 @@
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 }
 
-dsp_scb_descriptor_t *
-cs46xx_add_record_source (cs46xx_t *chip,dsp_scb_descriptor_t * source,
-			  u16 addr,char * scb_name)
+dsp_scb_descriptor_t * cs46xx_add_record_source (cs46xx_t *chip,dsp_scb_descriptor_t * source,
+                                                 u16 addr,char * scb_name)
 {
   	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 	dsp_scb_descriptor_t * parent;
@@ -1442,27 +1396,14 @@
 
 int cs46xx_src_unlink(cs46xx_t *chip,dsp_scb_descriptor_t * src)
 {
-	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
-	down(&ins->pcm_mutex);
-	down(&ins->scb_mutex);
-
-	snd_assert (src->parent_scb_ptr != NULL,goto _fail_end);
+	snd_assert (src->parent_scb_ptr != NULL,  return -EINVAL );
 
 	/* mute SCB */
 	snd_cs46xx_poke(chip, (src->address + 0xE) << 2, 0xffffffff);
 
 	_dsp_unlink_scb (chip,src);
 
-	up(&ins->scb_mutex);
-	up(&ins->pcm_mutex);
-
 	return 0;
-
- _fail_end:
-	up(&ins->scb_mutex);
-	up(&ins->pcm_mutex);
-
-	return -EINVAL;
 }
 
 int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src)
@@ -1471,11 +1412,8 @@
 	dsp_scb_descriptor_t * parent_scb;
 	unsigned int flags;
 
-	down(&ins->pcm_mutex);
-	down(&ins->scb_mutex);
-
-	snd_assert (src->parent_scb_ptr == NULL,goto _fail_end);
-	snd_assert(ins->master_mix_scb !=NULL,goto _fail_end);
+	snd_assert (src->parent_scb_ptr == NULL,   return -EINVAL );
+	snd_assert(ins->master_mix_scb !=NULL,   return -EINVAL );
 
 	if (ins->master_mix_scb->sub_list_ptr != ins->the_null_scb) {
 		parent_scb = find_next_free_scb (chip,ins->master_mix_scb->sub_list_ptr);
@@ -1497,14 +1435,5 @@
 
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
   
-	up(&ins->scb_mutex);
-	up(&ins->pcm_mutex);
-
 	return 0;
-
- _fail_end:
-	up(&ins->scb_mutex);
-	up(&ins->pcm_mutex);
- 
-	return -EINVAL;
 }
diff -Nru a/sound/pci/ice1712.c b/sound/pci/ice1712.c
--- a/sound/pci/ice1712.c	Tue Oct  1 17:08:04 2002
+++ b/sound/pci/ice1712.c	Tue Oct  1 17:08:04 2002
@@ -1628,7 +1628,7 @@
 
 	ice->capture_con_substream = substream;
 	runtime->hw = snd_ice1712_capture;
-	runtime->hw.rates = ice->ac97->rates_adc;
+	runtime->hw.rates = ice->ac97->rates[AC97_RATES_ADC];
 	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
 		runtime->hw.rate_min = 48000;
 	return 0;
@@ -1660,8 +1660,6 @@
 {
 	ice1712_t *ice = snd_pcm_substream_chip(substream);
 
-	/* disable ADC power */
-	snd_ac97_update_bits(ice->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
 	ice->capture_con_substream = NULL;
 	return 0;
 }
@@ -2396,14 +2394,6 @@
 	return 0;
 }
 
-static void snd_ice1712_ac97_init(ac97_t *ac97)
-{
-	// ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return);
-
-        /* disable center DAC/surround DAC/LFE DAC/MIC ADC */
-        snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
-}
-
 static void snd_ice1712_mixer_free_ac97(ac97_t *ac97)
 {
 	ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return);
@@ -2419,7 +2409,6 @@
 		memset(&ac97, 0, sizeof(ac97));
 		ac97.write = snd_ice1712_ac97_write;
 		ac97.read = snd_ice1712_ac97_read;
-		ac97.init = snd_ice1712_ac97_init;
 		ac97.private_data = ice;
 		ac97.private_free = snd_ice1712_mixer_free_ac97;
 		if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) {
@@ -2437,7 +2426,6 @@
 		memset(&ac97, 0, sizeof(ac97));
 		ac97.write = snd_ice1712_pro_ac97_write;
 		ac97.read = snd_ice1712_pro_ac97_read;
-		ac97.init = snd_ice1712_ac97_init;
 		ac97.private_data = ice;
 		ac97.private_free = snd_ice1712_mixer_free_ac97;
 		if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) {
diff -Nru a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
--- a/sound/pci/intel8x0.c	Tue Oct  1 17:08:04 2002
+++ b/sound/pci/intel8x0.c	Tue Oct  1 17:08:04 2002
@@ -22,6 +22,7 @@
  *   You should have received a copy of the GNU General Public License
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+
  *
  */      
 
@@ -41,7 +42,7 @@
 #include <sound/initval.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
-MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,MX440; SiS 7012");
+MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455");
 MODULE_LICENSE("GPL");
 MODULE_CLASSES("{sound}");
 MODULE_DEVICES("{{Intel,82801AA-ICH},"
@@ -53,7 +54,8 @@
 		"{SiS,SI7012},"
 		"{NVidia,NForce Audio},"
 		"{AMD,AMD768},"
-		"{AMD,AMD8111}}");
+		"{AMD,AMD8111},"
+	        "{ALI,M5455}}");
 
 #define SUPPORT_JOYSTICK 1
 #define SUPPORT_MIDI 1
@@ -126,50 +128,58 @@
 #define PCI_DEVICE_ID_NVIDIA_MCP_AUDIO	0x01b1
 #endif
 
-#define DEVICE_INTEL	0
-#define DEVICE_SIS	1
+enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI };
+
+#define ICHREG(x) ICH_REG_##x
+
+#define DEFINE_REGSET(name,base) \
+enum { \
+	ICH_REG_##name##_BDBAR	= base + 0x0,	/* dword - buffer descriptor list base address */ \
+	ICH_REG_##name##_CIV	= base + 0x04,	/* byte - current index value */ \
+	ICH_REG_##name##_LVI	= base + 0x05,	/* byte - last valid index */ \
+	ICH_REG_##name##_SR	= base + 0x06,	/* byte - status register */ \
+	ICH_REG_##name##_PICB	= base + 0x08,	/* word - position in current buffer */ \
+	ICH_REG_##name##_PIV	= base + 0x0a,	/* byte - prefetched index value */ \
+	ICH_REG_##name##_CR	= base + 0x0b,	/* byte - control register */ \
+};
+
+/* busmaster blocks */
+DEFINE_REGSET(OFF, 0);		/* offset */
+DEFINE_REGSET(PI, 0x00);	/* PCM in */
+DEFINE_REGSET(PO, 0x10);	/* PCM out */
+DEFINE_REGSET(MC, 0x20);	/* Mic in */
+
+/* ICH4 busmater blocks */
+DEFINE_REGSET(MC2, 0x40);	/* Mic in 2 */
+DEFINE_REGSET(PI2, 0x50);	/* PCM in 2 */
+DEFINE_REGSET(SP, 0x60);	/* SPDIF out */
+
+/* values for each busmaster block */
+
+/* LVI */
+#define ICH_REG_LVI_MASK		0x1f
+
+/* SR */
+#define ICH_FIFOE			0x10	/* FIFO error */
+#define ICH_BCIS			0x08	/* buffer completion interrupt status */
+#define ICH_LVBCI			0x04	/* last valid buffer completion interrupt */
+#define ICH_CELV			0x02	/* current equals last valid */
+#define ICH_DCH				0x01	/* DMA controller halted */
+
+/* PIV */
+#define ICH_REG_PIV_MASK		0x1f	/* mask */
+
+/* CR */
+#define ICH_IOCE			0x10	/* interrupt on completion enable */
+#define ICH_FEIE			0x08	/* fifo error interrupt enable */
+#define ICH_LVBIE			0x04	/* last valid buffer interrupt enable */
+#define ICH_RESETREGS			0x02	/* reset busmaster registers */
+#define ICH_STARTBM			0x01	/* start busmaster operation */
 
-#define ICHREG(ice, x) ((ice)->bmport + ICH_REG_##x)
-#define ICHREG2(ice, x) ((ice)->bmport + x)
 
-/* capture block */
-#define ICH_REG_PI_BDBAR		0x00	/* dword - buffer descriptor list base address */
-#define ICH_REG_PI_CIV			0x04	/* byte - current index value */
-#define ICH_REG_PI_LVI			0x05	/* byte - last valid index */
-#define   ICH_REG_LVI_MASK		0x1f
-#define ICH_REG_PI_SR			0x06	/* byte - status register */
-#define   ICH_FIFOE			0x10	/* FIFO error */
-#define   ICH_BCIS			0x08	/* buffer completion interrupt status */
-#define   ICH_LVBCI			0x04	/* last valid buffer completion interrupt */
-#define   ICH_CELV			0x02	/* current equals last valid */
-#define   ICH_DCH			0x01	/* DMA controller halted */
-#define ICH_REG_PI_PICB			0x08	/* word - position in current buffer */
-#define ICH_REG_PI_PIV			0x0a	/* byte - prefetched index value */
-#define   ICH_REG_PIV_MASK		0x1f	/* mask */
-#define ICH_REG_PI_CR			0x0b	/* byte - control register */
-#define   ICH_IOCE			0x10	/* interrupt on completion enable */
-#define   ICH_FEIE			0x08	/* fifo error interrupt enable */
-#define   ICH_LVBIE			0x04	/* last valid buffer interrupt enable */
-#define   ICH_RESETREGS			0x02	/* reset busmaster registers */
-#define   ICH_STARTBM			0x01	/* start busmaster operation */
-/* playback block */
-#define ICH_REG_PO_BDBAR		0x10	/* dword - buffer descriptor list base address */
-#define ICH_REG_PO_CIV			0x14	/* byte - current index value */
-#define ICH_REG_PO_LVI			0x15	/* byte - last valid command */
-#define ICH_REG_PO_SR			0x16	/* byte - status register */
-#define ICH_REG_PO_PICB			0x18	/* word - position in current buffer */
-#define ICH_REG_PO_PIV			0x1a	/* byte - prefetched index value */
-#define ICH_REG_PO_CR			0x1b	/* byte - control register */
-/* mic capture block */
-#define ICH_REG_MC_BDBAR		0x20	/* dword - buffer descriptor list base address */
-#define ICH_REG_MC_CIV			0x24	/* byte - current index value */
-#define ICH_REG_MC_LVI			0x25	/* byte - last valid command */
-#define ICH_REG_MC_SR			0x26	/* byte - status register */
-#define ICH_REG_MC_PICB			0x28	/* word - position in current buffer */
-#define ICH_REG_MC_PIV			0x2a	/* byte - prefetched index value */
-#define ICH_REG_MC_CR			0x2b	/* byte - control register */
 /* global block */
 #define ICH_REG_GLOB_CNT		0x2c	/* dword - global control */
+#define   ICH_PCM_20BIT		0x00400000	/* 20-bit samples (ICH4) */
 #define   ICH_PCM_246_MASK	0x00300000	/* 6 channels (not all chips) */
 #define   ICH_PCM_6		0x00200000	/* 6 channels (not all chips) */
 #define   ICH_PCM_4		0x00100000	/* 4 channels (not all chips) */
@@ -181,16 +191,24 @@
 #define   ICH_AC97COLD		0x00000002	/* AC'97 cold reset */
 #define   ICH_GIE		0x00000001	/* GPI interrupt enable */
 #define ICH_REG_GLOB_STA		0x30	/* dword - global status */
+#define   ICH_TRI		0x20000000	/* ICH4: tertiary (AC_SDIN2) resume interrupt */
+#define   ICH_TCR		0x10000000	/* ICH4: tertiary (AC_SDIN2) codec ready */
+#define   ICH_BCS		0x08000000	/* ICH4: bit clock stopped */
+#define   ICH_SPINT		0x04000000	/* ICH4: S/PDIF interrupt */
+#define   ICH_P2INT		0x02000000	/* ICH4: PCM2-In interrupt */
+#define   ICH_M2INT		0x01000000	/* ICH4: Mic2-In interrupt */
+#define   ICH_SAMPLE_CAP	0x00c00000	/* ICH4: sample capability bits (RO) */
+#define   ICH_MULTICHAN_CAP	0x00300000	/* ICH4: multi-channel capability bits (RO) */
 #define   ICH_MD3		0x00020000	/* modem power down semaphore */
 #define   ICH_AD3		0x00010000	/* audio power down semaphore */
 #define   ICH_RCS		0x00008000	/* read completion status */
 #define   ICH_BIT3		0x00004000	/* bit 3 slot 12 */
 #define   ICH_BIT2		0x00002000	/* bit 2 slot 12 */
 #define   ICH_BIT1		0x00001000	/* bit 1 slot 12 */
-#define   ICH_SRI		0x00000800	/* secondary resume interrupt */
-#define   ICH_PRI		0x00000400	/* primary resume interrupt */
-#define   ICH_SCR		0x00000200	/* secondary codec ready */
-#define   ICH_PCR		0x00000100	/* primary codec ready */
+#define   ICH_SRI		0x00000800	/* secondary (AC_SDIN1) resume interrupt */
+#define   ICH_PRI		0x00000400	/* primary (AC_SDIN0) resume interrupt */
+#define   ICH_SCR		0x00000200	/* secondary (AC_SDIN1) codec ready */
+#define   ICH_PCR		0x00000100	/* primary (AC_SDIN0) codec ready */
 #define   ICH_MCINT		0x00000080	/* MIC capture interrupt */
 #define   ICH_POINT		0x00000040	/* playback interrupt */
 #define   ICH_PIINT		0x00000020	/* capture interrupt */
@@ -198,16 +216,79 @@
 #define   ICH_MIINT		0x00000002	/* modem capture interrupt */
 #define   ICH_GSCI		0x00000001	/* GPI status change interrupt */
 #define ICH_REG_ACC_SEMA		0x34	/* byte - codec write semaphore */
-#define   ICH_CAS			0x01	/* codec access semaphore */
+#define   ICH_CAS		0x01		/* codec access semaphore */
+#define ICH_REG_SDM		0x80
+#define   ICH_DI2L_MASK		0x000000c0	/* PCM In 2, Mic In 2 data in line */
+#define   ICH_DI2L_SHIFT	6
+#define   ICH_DI1L_MASK		0x00000030	/* PCM In 1, Mic In 1 data in line */
+#define   ICH_DI1L_SHIFT	4
+#define   ICH_SE		0x00000008	/* steer enable */
+#define   ICH_LDI_MASK		0x00000003	/* last codec read data input */
 
 #define ICH_MAX_FRAGS		32		/* max hw frags */
 
+
+/*
+ * registers for Ali5455
+ */
+
+/* ALi 5455 busmaster blocks */
+DEFINE_REGSET(AL_PI, 0x40);	/* ALi PCM in */
+DEFINE_REGSET(AL_PO, 0x50);	/* Ali PCM out */
+DEFINE_REGSET(AL_MC, 0x60);	/* Ali Mic in */
+DEFINE_REGSET(AL_CDC_SPO, 0x70);	/* Ali Codec SPDIF out */
+DEFINE_REGSET(AL_CLR_SPI, 0xa0);	/* Ali Controller SPDIF in */
+DEFINE_REGSET(AL_CLR_SPO, 0xb0);	/* Ali Controller SPDIF out */
+
+enum {
+	ICH_REG_ALI_SCR = 0x00,		/* System Control Register */
+	ICH_REG_ALI_SSR = 0x04,		/* System Status Register  */
+	ICH_REG_ALI_DMACR = 0x08,	/* DMA Control Register    */
+	ICH_REG_ALI_FIFOCR1 = 0x0c,	/* FIFO Control Register 1  */
+	ICH_REG_ALI_INTERFACECR = 0x10,	/* Interface Control Register */
+	ICH_REG_ALI_INTERRUPTCR = 0x14,	/* Interrupt control Register */
+	ICH_REG_ALI_INTERRUPTSR = 0x18,	/* Interrupt  Status Register */
+	ICH_REG_ALI_FIFOCR2 = 0x1c,	/* FIFO Control Register 2   */
+	ICH_REG_ALI_CPR = 0x20,		/* Command Port Register     */
+	ICH_REG_ALI_SPR = 0x24,		/* Status Port Register      */
+	ICH_REG_ALI_FIFOCR3 = 0x2c,	/* FIFO Control Register 3  */
+	ICH_REG_ALI_TTSR = 0x30,	/* Transmit Tag Slot Register */
+	ICH_REG_ALI_RTSR = 0x34,	/* Receive Tag Slot  Register */
+	ICH_REG_ALI_CSPSR = 0x38,	/* Command/Status Port Status Register */
+	ICH_REG_ALI_CAS = 0x3c,		/* Codec Write Semaphore Register */
+	ICH_REG_ALI_SPDIFCSR = 0xf8,	/* spdif channel status register  */
+	ICH_REG_ALI_SPDIFICS = 0xfc	/* spdif interface control/status  */
+};
+
+/* interrupts for the whole chip by interrupt status register finish */
+ 
+#define ALI_INT_SPDIFOUT	(1<<23)	/* controller spdif out INTERRUPT */
+#define ALI_INT_SPDIFIN		(1<<22)
+#define ALI_INT_CODECSPDIFOUT	(1<<19)
+#define ALI_INT_MICIN		(1<<18)
+#define ALI_INT_PCMOUT		(1<<17)
+#define ALI_INT_PCMIN		(1<<16)
+#define ALI_INT_CPRAIS		(1<<7)
+#define ALI_INT_SPRAIS		(1<<5)
+#define ALI_INT_GPIO		(1<<1)
+#define ALI_INT_MASK		(ALI_INT_SPDIFOUT|ALI_INT_CODECSPDIFOUT|ALI_INT_MICIN|ALI_INT_PCMOUT|ALI_INT_PCMIN)
+
+#define ALI_PCM_CH4		0x100
+#define ALI_PCM_CH6		0x200
+#define ALI_PCM_MASK		(ALI_PCM_CH4 | ALI_PCM_CH6)
+
 /*
  *  
  */
 
+enum { ICHD_PCMIN, ICHD_PCMOUT, ICHD_MIC, ICHD_MIC2, ICHD_PCM2IN, ICHD_SPBAR, ICHD_LAST = ICHD_SPBAR };
+enum { ALID_PCMIN, ALID_PCMOUT, ALID_MIC, ALID_AC97SPDIFOUT, ALID_SPDIFIN, ALID_SPDIFOUT, ALID_LAST = ALID_SPDIFOUT };
+
+#define get_ichdev(substream) (ichdev_t *)(substream->runtime->private_data)
+
 typedef struct {
-	unsigned long reg_offset;
+	unsigned int ichd;			/* ich device number */
+	unsigned long reg_offset;		/* offset to bmaddr */
 	u32 *bdbar;				/* CPU address (32bit) */
 	unsigned int bdbar_addr;		/* PCI bus address (32bit) */
 	snd_pcm_substream_t *substream;
@@ -221,11 +302,20 @@
         int lvi_frag;
 	int ack;
 	int ack_reload;
+	unsigned int ack_bit;
+	unsigned int roff_sr;
+	unsigned int roff_picb;
+	unsigned int int_sta_mask;		/* interrupt status mask */
+	unsigned int ali_slot;			/* ALI DMA slot */
+	ac97_t *ac97;
+	unsigned short ac97_rate_regs[3];
+	int ac97_rates_idx;
 #ifdef CONFIG_PM
 	unsigned char civ_saved;
 	unsigned char piv_saved;
 	unsigned short picb_saved;
 #endif
+	snd_info_entry_t *proc_entry;
 } ichdev_t;
 
 typedef struct _snd_intel8x0 intel8x0_t;
@@ -242,27 +332,34 @@
 
 	int irq;
 
-	unsigned long port;
-	struct resource *res_port;
-	unsigned long bmport;
-	struct resource *res_bmport;
+	unsigned int mmio;
+	unsigned long addr;
+	unsigned long remap_addr;
+	struct resource *res;
+	unsigned int bm_mmio;
+	unsigned long bmaddr;
+	unsigned long remap_bmaddr;
+	struct resource *res_bm;
 
 	struct pci_dev *pci;
 	snd_card_t *card;
 
 	snd_pcm_t *pcm;
 	snd_pcm_t *pcm_mic;
-	ichdev_t playback;
-	ichdev_t capture;
-	ichdev_t capture_mic;
+	snd_pcm_t *pcm_mic2;
+	snd_pcm_t *pcm2;
+	snd_pcm_t *pcm_spdif;
+	snd_pcm_t *pcm_ac97spdif;
+	ichdev_t ichd[6];
 
 	int multi4: 1,
-	    multi6: 1;
-	int in_ac97_init: 1;
-	int no_codec_check: 1;
+	    multi6: 1,
+	    smp20bit: 1;
+	int in_ac97_init: 1,
+	    in_sdin_init: 1;
 
-	ac97_t *ac97;
-	ac97_t *ac97sec;
+	ac97_t *ac97[3];
+	unsigned int ac97_sdin[3];
 
 	snd_rawmidi_t *rmidi;
 
@@ -270,16 +367,12 @@
 	spinlock_t ac97_lock;
 	snd_info_entry_t *proc_entry;
 	
+	u32 bdbars_count;
 	u32 *bdbars;
 	dma_addr_t bdbars_addr;
+	u32 int_sta_reg;		/* interrupt status register */
+	u32 int_sta_mask;		/* interrupt status mask */
 	
-	unsigned int reg_pi_sr;
-	unsigned int reg_pi_picb;
-	unsigned int reg_po_sr;
-	unsigned int reg_po_picb;
-	unsigned int reg_mc_sr;
-	unsigned int reg_mc_picb;	
-
 #ifdef CONFIG_PM
 	int in_suspend;
 #endif
@@ -290,34 +383,131 @@
 	{ 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* 82901AB */
 	{ 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* 82801BA */
 	{ 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* ICH3 */
-	{ 0x8086, 0x24c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* ICH4 */
+	{ 0x8086, 0x24c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH4 */
 	{ 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* 440MX */
 	{ 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS },	/* SI7012 */
 	{ 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* NFORCE */
 	{ 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* AMD8111 */
 	{ 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* AMD768 */
+	{ 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI },   /* Ali5455 */
 	{ 0, }
 };
 
 MODULE_DEVICE_TABLE(pci, snd_intel8x0_ids);
 
 /*
+ *  Lowlevel I/O - busmaster
+ */
+
+static u8 igetbyte(intel8x0_t *chip, u32 offset)
+{
+	if (chip->bm_mmio)
+		return readb(chip->remap_bmaddr + offset);
+	else
+		return inb(chip->bmaddr + offset);
+}
+
+static u16 igetword(intel8x0_t *chip, u32 offset)
+{
+	if (chip->bm_mmio)
+		return readw(chip->remap_bmaddr + offset);
+	else
+		return inw(chip->bmaddr + offset);
+}
+
+static u32 igetdword(intel8x0_t *chip, u32 offset)
+{
+	if (chip->bm_mmio)
+		return readl(chip->remap_bmaddr + offset);
+	else
+		return inl(chip->bmaddr + offset);
+}
+
+static void iputbyte(intel8x0_t *chip, u32 offset, u8 val)
+{
+	if (chip->bm_mmio)
+		writeb(val, chip->remap_bmaddr + offset);
+	else
+		return outb(val, chip->bmaddr + offset);
+}
+
+static void iputword(intel8x0_t *chip, u32 offset, u16 val)
+{
+	if (chip->bm_mmio)
+		writew(val, chip->remap_bmaddr + offset);
+	else
+		return outw(val, chip->bmaddr + offset);
+}
+
+static void iputdword(intel8x0_t *chip, u32 offset, u32 val)
+{
+	if (chip->bm_mmio)
+		writel(val, chip->remap_bmaddr + offset);
+	else
+		return outl(val, chip->bmaddr + offset);
+}
+
+/*
+ *  Lowlevel I/O - AC'97 registers
+ */
+
+static u16 iagetword(intel8x0_t *chip, u32 offset)
+{
+	if (chip->mmio)
+		return readw(chip->remap_addr + offset);
+	else
+		return inw(chip->addr + offset);
+}
+
+static void iaputword(intel8x0_t *chip, u32 offset, u16 val)
+{
+	if (chip->mmio)
+		writew(val, chip->remap_addr + offset);
+	else
+		return outw(val, chip->addr + offset);
+}
+
+/*
  *  Basic I/O
  */
+
+/*
+ * access to AC97 codec via normal i/o (for ICH and SIS7012)
+ */
 static int snd_intel8x0_codec_semaphore(intel8x0_t *chip, unsigned int codec)
 {
 	int time;
+	
+	if (codec > 2)
+		return -EIO;
+	if (chip->in_sdin_init) {
+		/* we don't know the ready bit assignment at the moment */
+		/* so we check any */
+		codec = ICH_PCR | ICH_SCR | ICH_TCR;
+	} else {
+		if (chip->device_type == DEVICE_INTEL_ICH4) {
+			switch (chip->ac97_sdin[codec]) {
+			case 0: codec = ICH_PCR; break;
+			case 1: codec = ICH_SCR; break;
+			case 2: codec = ICH_TCR; break;
+			}
+		} else {
+			switch (codec) {
+			case 0: codec = ICH_PCR; break;
+			case 1: codec = ICH_SCR; break;
+			case 2: codec = ICH_TCR; break;
+			}
+		}
+	}
 
 	/* codec ready ? */
-	if (! chip->no_codec_check) {
-		if ((inl(ICHREG(chip, GLOB_STA)) & (codec ? ICH_SCR : ICH_PCR)) == 0)
-			return -EIO;
-	}
+	if ((igetdword(chip, ICHREG(GLOB_STA)) & codec) == 0)
+		return -EIO;
 
 	/* Anyone holding a semaphore for 1 msec should be shot... */
 	time = 100;
       	do {
-      		if (!(inb(ICHREG(chip, ACC_SEMA)) & ICH_CAS))
+      		if (!(igetbyte(chip, ICHREG(ACC_SEMA)) & ICH_CAS))
       			return 0;
 		udelay(10);
 	} while (time--);
@@ -326,8 +516,8 @@
 	 * reset the semaphore. So even if you don't get the semaphore, still
 	 * continue the access. We don't need the semaphore anyway. */
 	snd_printk("codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
-			inb(ICHREG(chip, ACC_SEMA)), inl(ICHREG(chip, GLOB_STA)));
-	inw(chip->port);	/* clear semaphore flag */
+			igetbyte(chip, ICHREG(ACC_SEMA)), igetdword(chip, ICHREG(GLOB_STA)));
+	iagetword(chip, 0);	/* clear semaphore flag */
 	/* I don't care about the semaphore */
 	return -EBUSY;
 }
@@ -343,7 +533,7 @@
 		if (! chip->in_ac97_init)
 			snd_printk("codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
 	}
-	outw(val, chip->port + reg + ac97->num * 0x80);
+	iaputword(chip, reg + ac97->num * 0x80, val);
 	spin_unlock(&chip->ac97_lock);
 }
 
@@ -360,10 +550,10 @@
 			snd_printk("codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
 		res = 0xffff;
 	} else {
-		res = inw(chip->port + reg + ac97->num * 0x80);
-		if ((tmp = inl(ICHREG(chip, GLOB_STA))) & ICH_RCS) {
+		res = iagetword(chip, reg + ac97->num * 0x80);
+		if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) {
 			/* reset RCS and preserve other R/WC bits */
-			outl(tmp & ~(ICH_SRI|ICH_PRI|ICH_GSCI), ICHREG(chip, GLOB_STA));
+			iputdword(chip, ICHREG(GLOB_STA), tmp & ~(ICH_SRI|ICH_PRI|ICH_TRI|ICH_GSCI));
 			if (! chip->in_ac97_init)
 				snd_printk("codec_read %d: read timeout for register 0x%x\n", ac97->num, reg);
 			res = 0xffff;
@@ -373,46 +563,85 @@
 	return res;
 }
 
-static int snd_intel8x0_trigger(intel8x0_t *chip, ichdev_t *ichdev, int cmd)
+/*
+ * access to AC97 for Ali5455
+ */
+static int snd_intel8x0_ali_codec_ready(intel8x0_t *chip, int mask)
 {
-	unsigned char val = 0;
-	unsigned long port = chip->bmport + ichdev->reg_offset;
-	
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_RESUME:
-		val = ICH_IOCE | ICH_STARTBM;
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-		val = 0;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		val = ICH_IOCE;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		val = ICH_IOCE | ICH_STARTBM;
-		break;
-	default:
-		return -EINVAL;
+	int count = 0;
+	for (count = 0; count < 0x7f; count++) {
+		int val = igetbyte(chip, ICHREG(ALI_CSPSR));
+		if (val & mask)
+			return 0;
 	}
-	outb(val, port + ICH_REG_PI_CR);
-	if (cmd == SNDRV_PCM_TRIGGER_STOP) {
-		/* reset whole DMA things */
-		while (!(inb(port + chip->reg_pi_sr) & ICH_DCH)) ;
-		outb(ICH_RESETREGS, port + ICH_REG_PI_CR);
+	snd_printd(KERN_WARNING "intel8x0: AC97 codec ready timeout.\n");
+	return -EBUSY;
+}
+
+static int snd_intel8x0_ali_codec_semaphore(intel8x0_t *chip)
+{
+	int time = 100;
+	do {
+		if (igetdword(chip, ICHREG(ALI_CAS)) & 0x80000000)
+			return snd_intel8x0_ali_codec_ready(chip, 0x08);
+		udelay(1);
+	} while (time--);
+	snd_printk(KERN_WARNING "intel8x0: AC97 codec semaphore timeout.\n");
+	return -EBUSY;
+}
+
+static unsigned short snd_intel8x0_ali_codec_read(ac97_t *ac97, unsigned short reg)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return ~0);
+	unsigned short data, reg2;
+
+	spin_lock(&chip->ac97_lock);
+	if (snd_intel8x0_ali_codec_semaphore(chip))
+		goto __err;
+	reg |= 0x0080;
+	iputword(chip, ICHREG(ALI_CPR) + 2, reg | 0x0080);
+	if (snd_intel8x0_ali_codec_ready(chip, 0x02))
+		goto __err;
+	data = igetword(chip, ICHREG(ALI_SPR));
+	reg2 = igetword(chip, ICHREG(ALI_SPR) + 2);
+	if (reg != reg2) {
+		snd_printd(KERN_WARNING "intel8x0: AC97 read not completed?\n");
+		goto __err;
 	}
-	return 0;
+	spin_unlock(&chip->ac97_lock);
+	return data;
+ __err:
+	spin_unlock(&chip->ac97_lock);
+	return 0xffff;
+}
+
+static void snd_intel8x0_ali_codec_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return);
+
+	spin_lock(&chip->ac97_lock);
+	if (snd_intel8x0_ali_codec_semaphore(chip)) {
+		spin_unlock(&chip->ac97_lock);
+		return;
+	}
+	iputword(chip, ICHREG(ALI_CPR), val);
+	iputbyte(chip, ICHREG(ALI_CPR) + 2, reg);
+	snd_intel8x0_ali_codec_ready(chip, 0x01);
+	spin_unlock(&chip->ac97_lock);
 }
 
+
+/*
+ * DMA I/O
+ */
 static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev) 
 {
 	int idx;
 	u32 *bdbar = ichdev->bdbar;
-	unsigned long port = chip->bmport + ichdev->reg_offset;
+	unsigned long port = ichdev->reg_offset;
 	int shiftlen = (chip->device_type == DEVICE_SIS) ? 0 : 1;
 
-	outl(ichdev->bdbar_addr, port + ICH_REG_PI_BDBAR);
+	iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
 	if (ichdev->size == ichdev->fragsize) {
 		ichdev->ack_reload = ichdev->ack = 2;
 		ichdev->fragsize1 = ichdev->fragsize >> 1;
@@ -436,7 +665,7 @@
 		}
 		ichdev->frags = ichdev->size / ichdev->fragsize;
 	}
-	outb(ichdev->lvi = ICH_REG_LVI_MASK, port + ICH_REG_PI_LVI);
+	iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi = ICH_REG_LVI_MASK);
 	ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
 	ichdev->position = 0;
 #if 0
@@ -444,7 +673,7 @@
 			ichdev->lvi_frag, ichdev->frags, ichdev->fragsize, ichdev->fragsize1);
 #endif
 	/* clear interrupts */
-	outb(ICH_FIFOE | ICH_BCIS | ICH_LVBCI, port + chip->reg_pi_sr);
+	iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
 }
 
 /*
@@ -453,7 +682,7 @@
 
 static inline void snd_intel8x0_update(intel8x0_t *chip, ichdev_t *ichdev)
 {
-	unsigned long port = chip->bmport + ichdev->reg_offset;
+	unsigned long port = ichdev->reg_offset;
 	int ack = 0;
 
 	spin_lock(&chip->reg_lock);
@@ -461,80 +690,116 @@
 	ichdev->position %= ichdev->size;
 	ichdev->lvi++;
 	ichdev->lvi &= ICH_REG_LVI_MASK;
-	outb(ichdev->lvi, port + ICH_REG_PI_LVI);
+	iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi);
 	ichdev->lvi_frag++;
 	ichdev->lvi_frag %= ichdev->frags;
-	ichdev->bdbar[ichdev->lvi * 2] = ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1;
-	// printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n", ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2], ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_PI_PIV + port), inl(port + 4), inb(port + ICH_REG_PI_CR));
+	ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1);
+	// printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n", ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2], ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port), inl(port + 4), inb(port + ICH_REG_OFF_CR));
 	if ((ack = (--ichdev->ack == 0)) != 0)
 		ichdev->ack = ichdev->ack_reload;
 	spin_unlock(&chip->reg_lock);
 	if (ack && ichdev->substream)
 		snd_pcm_period_elapsed(ichdev->substream);
-	outb(ICH_FIFOE | ICH_BCIS | ICH_LVBCI, port + chip->reg_pi_sr);
+	iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
 }
 
 static void snd_intel8x0_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	intel8x0_t *chip = snd_magic_cast(intel8x0_t, dev_id, return);
+	ichdev_t *ichdev;
 	unsigned int status;
+	int i;
 
 	spin_lock(&chip->reg_lock);
-	status = inl(ICHREG(chip, GLOB_STA));
-	if ((status & (ICH_MCINT | ICH_POINT | ICH_PIINT)) == 0) {
+	status = igetdword(chip, chip->int_sta_reg);
+	if ((status & chip->int_sta_mask) == 0) {
 		spin_unlock(&chip->reg_lock);
 		return;
 	}
 	/* ack first */
-	outl(status & (ICH_MCINT | ICH_POINT | ICH_PIINT), ICHREG(chip, GLOB_STA));
+	iputdword(chip, chip->int_sta_reg, status & ~chip->int_sta_mask);
 	spin_unlock(&chip->reg_lock);
 
-	if (status & ICH_POINT)
-		snd_intel8x0_update(chip, &chip->playback);
-	if (status & ICH_PIINT)
-		snd_intel8x0_update(chip, &chip->capture);
-	if (status & ICH_MCINT)
-		snd_intel8x0_update(chip, &chip->capture_mic);
+	for (i = 0; i < chip->bdbars_count; i++) {
+		ichdev = &chip->ichd[i];
+		if (status & ichdev->int_sta_mask)
+			snd_intel8x0_update(chip, ichdev);
+	}
 }
 
 /*
  *  PCM part
  */
 
-static int snd_intel8x0_playback_ioctl(snd_pcm_substream_t * substream,
-				       unsigned int cmd,
-				       void *arg)
-{
-	int result;
-	result = snd_pcm_lib_ioctl(substream, cmd, arg);
-	if (result < 0)
-		return result;
-	return 0;
-}
-
-static int snd_intel8x0_capture_ioctl(snd_pcm_substream_t * substream,
-				      unsigned int cmd,
-				      void *arg)
-{
-	int result;
-	result = snd_pcm_lib_ioctl(substream, cmd, arg);
-	if (result < 0)
-		return result;
-	return 0;
-}
-
-static int snd_intel8x0_playback_trigger(snd_pcm_substream_t *substream, int cmd)
+static int snd_intel8x0_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	ichdev_t *ichdev = get_ichdev(substream);
+	unsigned char val = 0;
+	unsigned long port = ichdev->reg_offset;
 
-	return snd_intel8x0_trigger(chip, &chip->playback, cmd);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		val = ICH_IOCE | ICH_STARTBM;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		val = 0;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		val = ICH_IOCE;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		val = ICH_IOCE | ICH_STARTBM;
+		break;
+	default:
+		return -EINVAL;
+	}
+	iputbyte(chip, port + ICH_REG_OFF_CR, val);
+	if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		/* reset whole DMA things */
+		while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH)) ;
+		iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
+	}
+	return 0;
 }
 
-static int snd_intel8x0_capture_trigger(snd_pcm_substream_t *substream, int cmd)
+static int snd_intel8x0_ali_trigger(snd_pcm_substream_t *substream, int cmd)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	ichdev_t *ichdev = get_ichdev(substream);
+	unsigned long port = ichdev->reg_offset;
 
-	return snd_intel8x0_trigger(chip, &chip->capture, cmd);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
+		iputbyte(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		iputbyte(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 8));
+		iputbyte(chip, port + ICH_REG_OFF_CR, 0);
+		/* reset whole DMA things */
+		while (!(igetbyte(chip, port + ICH_REG_OFF_CR)))
+			;
+		iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
+		/* clear interrupts */
+		iputbyte(chip, port + ICH_REG_OFF_SR, igetbyte(chip, port + ICH_REG_OFF_SR) | 0x1e);
+		iputdword(chip, ICHREG(ALI_INTERRUPTSR),
+			  igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & (1 << (ichdev->ali_slot + 8)));
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		iputbyte(chip, port + ICH_REG_OFF_CR, 0);
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		iputbyte(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
 }
 
 static int snd_intel8x0_hw_params(snd_pcm_substream_t * substream,
@@ -550,72 +815,52 @@
 
 static void snd_intel8x0_setup_multi_channels(intel8x0_t *chip, int channels)
 {
-	unsigned int cnt = inl(ICHREG(chip, GLOB_CNT)) & ~ICH_PCM_246_MASK;
+	unsigned int cnt = igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_PCM_246_MASK;
 	if (chip->multi4 && channels == 4)
 		cnt |= ICH_PCM_4;
 	else if (chip->multi6 && channels == 6)
 		cnt |= ICH_PCM_6;
-	outl(cnt, ICHREG(chip, GLOB_CNT));
-}
-
-static int snd_intel8x0_playback_prepare(snd_pcm_substream_t * substream)
-{
-	intel8x0_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-
-	chip->playback.physbuf = runtime->dma_addr;
-	chip->playback.size = snd_pcm_lib_buffer_bytes(substream);
-	chip->playback.fragsize = snd_pcm_lib_period_bytes(substream);
-	spin_lock(&chip->reg_lock);
-	snd_intel8x0_setup_multi_channels(chip, runtime->channels);
-	spin_unlock(&chip->reg_lock);
-	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
-	snd_intel8x0_setup_periods(chip, &chip->playback);
-	return 0;
+	iputdword(chip, ICHREG(GLOB_CNT), cnt);
 }
 
-static int snd_intel8x0_capture_prepare(snd_pcm_substream_t * substream)
+static int snd_intel8x0_pcm_prepare(snd_pcm_substream_t * substream)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
 	snd_pcm_runtime_t *runtime = substream->runtime;
+	ichdev_t *ichdev = get_ichdev(substream);
+	int i;
 
-	chip->capture.physbuf = runtime->dma_addr;
-	chip->capture.size = snd_pcm_lib_buffer_bytes(substream);
-	chip->capture.fragsize = snd_pcm_lib_period_bytes(substream);
-	snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
-	snd_intel8x0_setup_periods(chip, &chip->capture);
+	ichdev->physbuf = runtime->dma_addr;
+	ichdev->size = snd_pcm_lib_buffer_bytes(substream);
+	ichdev->fragsize = snd_pcm_lib_period_bytes(substream);
+	if (ichdev->ichd == ICHD_PCMOUT && chip->device_type != DEVICE_ALI) {
+		spin_lock(&chip->reg_lock);
+		snd_intel8x0_setup_multi_channels(chip, runtime->channels);
+		spin_unlock(&chip->reg_lock);
+	}
+	for (i = 0; i < 3; i++)
+		if (ichdev->ac97_rate_regs[i])
+			snd_ac97_set_rate(ichdev->ac97, ichdev->ac97_rate_regs[i], runtime->rate);
+	snd_intel8x0_setup_periods(chip, ichdev);
 	return 0;
 }
 
-static snd_pcm_uframes_t snd_intel8x0_playback_pointer(snd_pcm_substream_t * substream)
+static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(snd_pcm_substream_t * substream)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	ichdev_t *ichdev = get_ichdev(substream);
 	size_t ptr;
 
-	ptr = chip->playback.fragsize1;
+	ptr = ichdev->fragsize1;
 	if (chip->device_type == DEVICE_SIS)
-		ptr -= inw(ICHREG2(chip,chip->reg_po_picb));
+		ptr -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
 	else
-		ptr -= inw(ICHREG2(chip,chip->reg_po_picb)) << 1;
-	ptr += chip->playback.position;
+		ptr -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << 1;
+	ptr += ichdev->position;
 	return bytes_to_frames(substream->runtime, ptr);
 }
 
-static snd_pcm_uframes_t snd_intel8x0_capture_pointer(snd_pcm_substream_t * substream)
-{
-	intel8x0_t *chip = snd_pcm_substream_chip(substream);
-	size_t ptr;
-
-	ptr = chip->capture.fragsize1;
-	if (chip->device_type == DEVICE_SIS)
-		ptr -= inw(ICHREG2(chip,chip->reg_pi_picb));
-	else
-		ptr -= inw(ICHREG2(chip,chip->reg_pi_picb)) << 1;
-	ptr += chip->capture.position;
-	return bytes_to_frames(substream->runtime, ptr);
-}
-
-static snd_pcm_hardware_t snd_intel8x0_playback =
+static snd_pcm_hardware_t snd_intel8x0_stream =
 {
 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -623,27 +868,7 @@
 				 SNDRV_PCM_INFO_PAUSE |
 				 SNDRV_PCM_INFO_RESUME),
 	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
-	.rates =		0,
-	.rate_min =		8000,
-	.rate_max =		48000,
-	.channels_min =		2,
-	.channels_max =		2,
-	.buffer_bytes_max =	128 * 1024,
-	.period_bytes_min =	32,
-	.period_bytes_max =	128 * 1024,
-	.periods_min =		1,
-	.periods_max =		1024,
-	.fifo_size =		0,
-};
-
-static snd_pcm_hardware_t snd_intel8x0_capture =
-{
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_RESUME),
-	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
-	.rates =		0,
+	.rates =		SNDRV_PCM_RATE_48000,
 	.rate_min =		8000,
 	.rate_max =		48000,
 	.channels_min =		2,
@@ -680,15 +905,16 @@
 	.mask = 0,
 };
 
-static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream)
+static int snd_intel8x0_pcm_open(snd_pcm_substream_t * substream, ichdev_t *ichdev)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	int err;
 
-	chip->playback.substream = substream;
-	runtime->hw = snd_intel8x0_playback;
-	runtime->hw.rates = chip->ac97->rates_front_dac;
+	ichdev->substream = substream;
+	runtime->hw = snd_intel8x0_stream;
+	if (ichdev->ac97_rates_idx >= 0)
+		runtime->hw.rates = ichdev->ac97->rates[ichdev->ac97_rates_idx];
 	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
 		runtime->hw.rate_min = 48000;
 	if (chip->device_type == DEVICE_SIS) {
@@ -697,6 +923,17 @@
 	}
 	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
 		return err;
+	runtime->private_data = ichdev;
+	return 0;
+}
+
+static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	err = snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCMOUT]);
 	if (chip->multi6) {
 		runtime->hw.channels_max = 6;
 		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels6);
@@ -707,66 +944,264 @@
 	return 0;
 }
 
+static int snd_intel8x0_playback_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ICHD_PCMOUT].substream = NULL;
+	return 0;
+}
+
 static int snd_intel8x0_capture_open(snd_pcm_substream_t * substream)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	int err;
 
-	chip->capture.substream = substream;
-	runtime->hw = snd_intel8x0_capture;
-	runtime->hw.rates = chip->ac97->rates_adc;
-	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
-		runtime->hw.rate_min = 48000;
-	if (chip->device_type == DEVICE_SIS) {
-		runtime->hw.buffer_bytes_max = 64*1024;
-		runtime->hw.period_bytes_max = 64*1024;
-	}
-	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
-		return err;
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCMIN]);
+}
+
+static int snd_intel8x0_capture_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ICHD_PCMIN].substream = NULL;
 	return 0;
 }
 
-static int snd_intel8x0_playback_close(snd_pcm_substream_t * substream)
+static int snd_intel8x0_mic_open(snd_pcm_substream_t * substream)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
 
-	chip->playback.substream = NULL;
-	/* disable DAC power */
-	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_MIC]);
+}
+
+static int snd_intel8x0_mic_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ICHD_MIC].substream = NULL;
 	return 0;
 }
 
-static int snd_intel8x0_capture_close(snd_pcm_substream_t * substream)
+static int snd_intel8x0_mic2_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_MIC2]);
+}
+
+static int snd_intel8x0_mic2_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ICHD_MIC2].substream = NULL;
+	return 0;
+}
+
+static int snd_intel8x0_capture2_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCM2IN]);
+}
+
+static int snd_intel8x0_capture2_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ICHD_PCM2IN].substream = NULL;
+	return 0;
+}
+
+static int snd_intel8x0_spdif_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_SPBAR]);
+}
+
+static int snd_intel8x0_spdif_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ICHD_SPBAR].substream = NULL;
+	return 0;
+}
+
+static int snd_intel8x0_ali_ac97spdifout_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_AC97SPDIFOUT]);
+}
+
+static int snd_intel8x0_ali_ac97spdifout_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ALID_AC97SPDIFOUT].substream = NULL;
+	return 0;
+}
+
+static int snd_intel8x0_ali_spdifin_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_SPDIFIN]);
+}
+
+static int snd_intel8x0_ali_spdifin_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ALID_SPDIFIN].substream = NULL;
+	return 0;
+}
+
+static int snd_intel8x0_ali_spdifout_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_SPDIFOUT]);
+}
+
+static int snd_intel8x0_ali_spdifout_close(snd_pcm_substream_t * substream)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
 
-	chip->capture.substream = NULL;
-	/* disable ADC power */
-	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
+	chip->ichd[ALID_SPDIFOUT].substream = NULL;
 	return 0;
 }
 
 static snd_pcm_ops_t snd_intel8x0_playback_ops = {
 	.open =		snd_intel8x0_playback_open,
 	.close =	snd_intel8x0_playback_close,
-	.ioctl =	snd_intel8x0_playback_ioctl,
+	.ioctl =	snd_pcm_lib_ioctl,
 	.hw_params =	snd_intel8x0_hw_params,
 	.hw_free =	snd_intel8x0_hw_free,
-	.prepare =	snd_intel8x0_playback_prepare,
-	.trigger =	snd_intel8x0_playback_trigger,
-	.pointer =	snd_intel8x0_playback_pointer,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
 };
 
 static snd_pcm_ops_t snd_intel8x0_capture_ops = {
 	.open =		snd_intel8x0_capture_open,
 	.close =	snd_intel8x0_capture_close,
-	.ioctl =	snd_intel8x0_capture_ioctl,
+	.ioctl =	snd_pcm_lib_ioctl,
 	.hw_params =	snd_intel8x0_hw_params,
 	.hw_free =	snd_intel8x0_hw_free,
-	.prepare =	snd_intel8x0_capture_prepare,
-	.trigger =	snd_intel8x0_capture_trigger,
-	.pointer =	snd_intel8x0_capture_pointer,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture_mic_ops = {
+	.open =		snd_intel8x0_mic_open,
+	.close =	snd_intel8x0_mic_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture_mic2_ops = {
+	.open =		snd_intel8x0_mic2_open,
+	.close =	snd_intel8x0_mic2_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture2_ops = {
+	.open =		snd_intel8x0_capture2_open,
+	.close =	snd_intel8x0_capture2_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_spdif_ops = {
+	.open =		snd_intel8x0_spdif_open,
+	.close =	snd_intel8x0_spdif_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_playback_ops = {
+	.open =		snd_intel8x0_playback_open,
+	.close =	snd_intel8x0_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_ali_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_capture_ops = {
+	.open =		snd_intel8x0_capture_open,
+	.close =	snd_intel8x0_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_ali_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_capture_mic_ops = {
+	.open =		snd_intel8x0_mic_open,
+	.close =	snd_intel8x0_mic_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_ali_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_ac97spdifout_ops = {
+	.open =		snd_intel8x0_ali_ac97spdifout_open,
+	.close =	snd_intel8x0_ali_ac97spdifout_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_ali_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_spdifin_ops = {
+	.open =		snd_intel8x0_ali_spdifin_open,
+	.close =	snd_intel8x0_ali_spdifin_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_spdifout_ops = {
+	.open =		snd_intel8x0_ali_spdifout_open,
+	.close =	snd_intel8x0_ali_spdifout_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
 };
 
 static void snd_intel8x0_pcm_free(snd_pcm_t *pcm)
@@ -787,8 +1222,13 @@
 	if (err < 0)
 		return err;
 
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_playback_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_ops);
+	if (chip->device_type == DEVICE_ALI) {
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_playback_ops);
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_ali_capture_ops);
+	} else {
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_playback_ops);
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_ops);
+	}
 
 	pcm->private_data = chip;
 	pcm->private_free = snd_intel8x0_pcm_free;
@@ -807,198 +1247,432 @@
  *  PCM code - MIC
  */
 
-static int snd_intel8x0_capture_mic_ioctl(snd_pcm_substream_t * substream,
-					  unsigned int cmd,
-					  void *arg)
+static void snd_intel8x0_pcm_mic_free(snd_pcm_t *pcm)
 {
-	int result;
-	result = snd_pcm_lib_ioctl(substream, cmd, arg);
-	if (result < 0)
-		return result;
-	return 0;
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
+	chip->pcm_mic = NULL;
 }
 
-static int snd_intel8x0_capture_mic_trigger(snd_pcm_substream_t * substream,
-					    int cmd)
+static int __devinit snd_intel8x0_pcm_mic(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
 {
-	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_t *pcm;
+	int err;
 
-	return snd_intel8x0_trigger(chip, &chip->capture_mic, cmd);
-}
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "Intel ICH - MIC ADC", device, 0, 1, &pcm);
+	if (err < 0)
+		return err;
 
-static int snd_intel8x0_capture_mic_prepare(snd_pcm_substream_t * substream)
-{
-	intel8x0_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+			chip->device_type == DEVICE_ALI ?
+				&snd_intel8x0_ali_capture_mic_ops :
+				&snd_intel8x0_capture_mic_ops);
 
-	chip->capture_mic.physbuf = runtime->dma_addr;
-	chip->capture_mic.size = snd_pcm_lib_buffer_bytes(substream);
-	chip->capture_mic.fragsize = snd_pcm_lib_period_bytes(substream);
-	snd_ac97_set_rate(chip->ac97, AC97_PCM_MIC_ADC_RATE, runtime->rate);
-	snd_intel8x0_setup_periods(chip, &chip->capture_mic);
+	pcm->private_data = chip;
+	pcm->private_free = snd_intel8x0_pcm_mic_free;
+	pcm->info_flags = 0;
+	sprintf(pcm->name, "%s - MIC ADC", chip->card->shortname);
+
+	chip->pcm_mic = pcm;	
+	if (rpcm)
+		*rpcm = pcm;
 	return 0;
 }
 
-static snd_pcm_uframes_t snd_intel8x0_capture_mic_pointer(snd_pcm_substream_t * substream)
+/*
+ *  PCM code - MIC2
+ */
+
+static void snd_intel8x0_pcm_mic2_free(snd_pcm_t *pcm)
 {
-	intel8x0_t *chip = snd_pcm_substream_chip(substream);
-	size_t ptr;
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
+	chip->pcm_mic2 = NULL;
+}
 
-	ptr = chip->capture_mic.fragsize1;
-	if (chip->device_type == DEVICE_SIS)
-		ptr -= inw(ICHREG2(chip,chip->reg_mc_picb));
-	else
-		ptr -= inw(ICHREG2(chip,chip->reg_mc_picb)) << 1;
-	ptr += chip->capture_mic.position;
-	return bytes_to_frames(substream->runtime, ptr);
+static int __devinit snd_intel8x0_pcm_mic2(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "Intel ICH - MIC2 ADC", device, 0, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_mic2_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_intel8x0_pcm_mic2_free;
+	pcm->info_flags = 0;
+	sprintf(pcm->name, "%s - MIC2 ADC", chip->card->shortname);
+
+	chip->pcm_mic2 = pcm;	
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
 }
 
-static snd_pcm_hardware_t snd_intel8x0_capture_mic =
+/*
+ *  PCM code - capture2
+ */
+
+static void snd_intel8x0_pcm_capture2_free(snd_pcm_t *pcm)
 {
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_PAUSE),
-	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
-	.rates =		0,
-	.rate_min =		8000,
-	.rate_max =		48000,
-	.channels_min =		1,
-	.channels_max =		1,
-	.buffer_bytes_max =	128 * 1024,
-	.period_bytes_min =	32,
-	.period_bytes_max =	128 * 1024,
-	.periods_min =		1,
-	.periods_max =		1024,
-	.fifo_size =		0,
-};
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
+	chip->pcm2 = NULL;
+}
 
-static int snd_intel8x0_capture_mic_open(snd_pcm_substream_t * substream)
+static int __devinit snd_intel8x0_pcm_capture2(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
 {
-	intel8x0_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_t *pcm;
+	int err;
 
-	chip->capture_mic.substream = substream;
-	runtime->hw = snd_intel8x0_capture_mic;
-	runtime->hw.rates = chip->ac97->rates_mic_adc;
-	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
-		runtime->hw.rate_min = 48000;
-	if (chip->device_type == DEVICE_SIS) {
-		runtime->hw.buffer_bytes_max = 64*1024;
-		runtime->hw.period_bytes_max = 64*1024;
-	}
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "Intel ICH - ADC2", device, 0, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture2_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_intel8x0_pcm_capture2_free;
+	pcm->info_flags = 0;
+	sprintf(pcm->name, "%s - ADC2", chip->card->shortname);
+
+	chip->pcm2 = pcm;	
+	if (rpcm)
+		*rpcm = pcm;
 	return 0;
 }
 
-static int snd_intel8x0_capture_mic_close(snd_pcm_substream_t * substream)
+/*
+ *  PCM code - S/PDIF
+ */
+
+static void snd_intel8x0_pcm_spdif_free(snd_pcm_t *pcm)
 {
-	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
+	chip->pcm_spdif = NULL;
+}
+
+static int __devinit snd_intel8x0_pcm_spdif(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "Intel ICH - IEC958", device, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_spdif_ops);
 
-	chip->capture_mic.substream = NULL;
-	/* disable ADC power */
-	snd_ac97_update_bits(chip->ac97, AC97_EXTENDED_STATUS, 0x4000, 0x4000);
+	pcm->private_data = chip;
+	pcm->private_free = snd_intel8x0_pcm_spdif_free;
+	pcm->info_flags = 0;
+	sprintf(pcm->name, "%s - IEC958", chip->card->shortname);
+
+	chip->pcm_spdif = pcm;	
+	if (rpcm)
+		*rpcm = pcm;
 	return 0;
 }
 
-static snd_pcm_ops_t snd_intel8x0_capture_mic_ops = {
-	.open =		snd_intel8x0_capture_mic_open,
-	.close =	snd_intel8x0_capture_mic_close,
-	.ioctl =	snd_intel8x0_capture_mic_ioctl,
-	.hw_params =	snd_intel8x0_hw_params,
-	.hw_free =	snd_intel8x0_hw_free,
-	.prepare =	snd_intel8x0_capture_mic_prepare,
-	.trigger =	snd_intel8x0_capture_mic_trigger,
-	.pointer =	snd_intel8x0_capture_mic_pointer,
-};
+/*
+ *  PCM code - ALI S/PDIF
+ */
 
-static void snd_intel8x0_pcm_mic_free(snd_pcm_t *pcm)
+static void snd_intel8x0_ali_spdif_free(snd_pcm_t *pcm)
 {
 	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
-	chip->pcm_mic = NULL;
+	chip->pcm_spdif = NULL;
 }
 
-static int __devinit snd_intel8x0_pcm_mic(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
+static int __devinit snd_intel8x0_ali_spdif(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
 {
 	snd_pcm_t *pcm;
 	int err;
 
 	if (rpcm)
 		*rpcm = NULL;
-	err = snd_pcm_new(chip->card, "Intel ICH - MIC ADC", device, 1, 1, &pcm);
+	err = snd_pcm_new(chip->card, "Intel ICH - IEC958", device, 1, 1, &pcm);
 	if (err < 0)
 		return err;
 
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_mic_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_spdifout_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_ali_spdifin_ops);
 
 	pcm->private_data = chip;
-	pcm->private_free = snd_intel8x0_pcm_mic_free;
+	pcm->private_free = snd_intel8x0_ali_spdif_free;
 	pcm->info_flags = 0;
-	sprintf(pcm->name, "%s - MIC ADC", chip->card->shortname);
+	sprintf(pcm->name, "%s - IEC958", chip->card->shortname);
 
-	chip->pcm_mic = pcm;	
+	chip->pcm_spdif = pcm;	
 	if (rpcm)
 		*rpcm = pcm;
 	return 0;
 }
 
 /*
- *  Mixer part
+ *  PCM code - ALI AC'97 S/PDIF
  */
 
-static void snd_intel8x0_codec_init(ac97_t *ac97)
+static void snd_intel8x0_ali_ac97spdif_free(snd_pcm_t *pcm)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
+	chip->pcm_ac97spdif = NULL;
+}
+
+static int __devinit snd_intel8x0_ali_ac97spdif(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
 {
-	// intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return);
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "ALI - AC97 IEC958", device, 0, 1, &pcm);
+	if (err < 0)
+		return err;
 
-	/* disable DAC & ADC power */
-	snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300);
-	/* disable center DAC/surround DAC/LFE DAC/MIC ADC */
-	snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_ac97spdifout_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_intel8x0_ali_ac97spdif_free;
+	pcm->info_flags = 0;
+	sprintf(pcm->name, "%s - AC97 IEC958", chip->card->shortname);
+
+	chip->pcm_ac97spdif = pcm;	
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
 }
 
+/*
+ *  Mixer part
+ */
+
 static void snd_intel8x0_mixer_free_ac97(ac97_t *ac97)
 {
 	intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return);
-	if (ac97->num == 0) {
-		chip->ac97 = NULL;
-	} else {
-		chip->ac97sec = NULL;
-	}
+	chip->ac97[ac97->num] = NULL;
 }
 
+static struct _ac97_rate_regs {
+	unsigned int ichd;
+	unsigned short regs[3];
+	short rates_idx;
+} ac97_rate_regs[] = {
+	{ ICHD_PCMOUT, { AC97_PCM_FRONT_DAC_RATE, AC97_PCM_SURR_DAC_RATE, AC97_PCM_LFE_DAC_RATE }, AC97_RATES_FRONT_DAC },
+	{ ICHD_PCMIN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC },
+	{ ICHD_MIC, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC },
+	{ ICHD_MIC2, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC },
+	{ ICHD_PCM2IN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC },
+	{ ICHD_SPBAR, { AC97_SPDIF, 0, 0 }, AC97_RATES_SPDIF },
+};
+
+static struct _ac97_ali_rate_regs {
+	unsigned int ichd;
+	unsigned short regs[3];
+	short rates_idx;
+} ac97_ali_rate_regs[] = {
+	{ ALID_PCMOUT, { AC97_PCM_FRONT_DAC_RATE, AC97_PCM_SURR_DAC_RATE, AC97_PCM_LFE_DAC_RATE }, AC97_RATES_FRONT_DAC },
+	{ ALID_PCMIN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC },
+	{ ALID_MIC, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC },
+	{ ALID_AC97SPDIFOUT, { AC97_SPDIF, 0, 0 }, AC97_RATES_SPDIF },
+	{ ALID_SPDIFOUT, { 0, 0, 0 }, -1 },
+	{ ALID_SPDIFIN, { 0, 0, 0 }, -1 },
+};
+
+
 static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock)
 {
-	ac97_t ac97;
-	int err;
-
+	ac97_t ac97, *x97;
+	ichdev_t *ichdev;
+	int err, i, channels = 2, codecs;
+	unsigned int glob_sta = 0;
+
+	for (i = 0; i <= ICHD_LAST; i++) {
+		if (chip->device_type != DEVICE_ALI) {
+			struct _ac97_rate_regs *aregs;
+			aregs = &ac97_rate_regs[i];
+			ichdev = &chip->ichd[aregs->ichd];
+			ichdev->ac97_rate_regs[0] = aregs->regs[0];
+			ichdev->ac97_rate_regs[1] = aregs->regs[1];
+			ichdev->ac97_rate_regs[2] = aregs->regs[2];
+			ichdev->ac97_rates_idx = aregs->rates_idx;
+		} else {
+			struct _ac97_ali_rate_regs *aregs;
+			aregs = &ac97_ali_rate_regs[i];
+			ichdev = &chip->ichd[aregs->ichd];
+			ichdev->ac97_rate_regs[0] = aregs->regs[0];
+			ichdev->ac97_rate_regs[1] = aregs->regs[1];
+			ichdev->ac97_rate_regs[2] = aregs->regs[2];
+			ichdev->ac97_rates_idx = aregs->rates_idx;
+		}
+	}
 	chip->in_ac97_init = 1;
 	memset(&ac97, 0, sizeof(ac97));
-	ac97.write = snd_intel8x0_codec_write;
-	ac97.read = snd_intel8x0_codec_read;
-	ac97.init = snd_intel8x0_codec_init;
 	ac97.private_data = chip;
 	ac97.private_free = snd_intel8x0_mixer_free_ac97;
 	if (ac97_clock >= 8000 && ac97_clock <= 48000)
 		ac97.clock = ac97_clock;
 	else
 		ac97.clock = 48000;
-	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
+	if (chip->device_type != DEVICE_ALI) {
+		glob_sta = igetdword(chip, ICHREG(GLOB_STA));
+		ac97.write = snd_intel8x0_codec_write;
+		ac97.read = snd_intel8x0_codec_read;
+		if (glob_sta & ICH_PCM_6)
+			channels = 6;
+		else if (glob_sta & ICH_PCM_4)
+			channels = 4;
+		if (chip->device_type == DEVICE_INTEL_ICH4) {
+			codecs = 0;
+			if (glob_sta & ICH_PCR)
+				codecs++;
+			if (glob_sta & ICH_SCR)
+				codecs++;
+			if (glob_sta & ICH_TCR)
+				codecs++;
+			chip->in_sdin_init = 1;
+			for (i = 0; i < codecs; i++) {
+				ac97.num = i;
+				snd_intel8x0_codec_read(&ac97, 0);
+				chip->ac97_sdin[i] = igetbyte(chip, ICHREG(SDM)) & ICH_LDI_MASK;
+			}
+			ac97.num = 0;
+			chip->in_sdin_init = 0;
+		} else {
+			codecs = glob_sta & ICH_SCR ? 2 : 1;
+		}
+	} else {
+		ac97.write = snd_intel8x0_ali_codec_write;
+		ac97.read = snd_intel8x0_ali_codec_read;
+		channels = 6;
+		codecs = 1;
+	}
+	if ((err = snd_ac97_mixer(chip->card, &ac97, &x97)) < 0)
 		return err;
-#if 0 /* it seems that SDIN signals are mixed together (at least for AD CNR boards) */
-	if (inl(ICHREG(chip, GLOB_STA)) & ICH_SCR) {
-		ac97.num = 1;
-		ac97.addr = 1;
-		snd_ac97_mixer(chip->card, &ac97, &chip->ac97sec);
+	chip->ac97[0] = x97;
+	chip->ichd[ICHD_PCMOUT].ac97 = x97;
+	chip->ichd[ICHD_PCMIN].ac97 = x97;
+	if (x97->ext_id & AC97_EI_VRM)
+		chip->ichd[ICHD_MIC].ac97 = x97;
+	if (x97->ext_id & AC97_EI_SPDIF) {
+		if (chip->device_type != DEVICE_ALI)
+			chip->ichd[ICHD_SPBAR].ac97 = x97;
+		else
+			chip->ichd[ALID_AC97SPDIFOUT].ac97 = x97;
+	}
+	/* make sure, that we have DACs at right slot for rev2.2 */
+	if (ac97_is_rev22(x97))
+		snd_ac97_update_bits(x97, AC97_EXTENDED_ID, AC97_EI_DACS_SLOT_MASK, 0);
+	/* can we have more AC'97 codecs with ALI chipset? */
+	if (chip->device_type == DEVICE_ALI)
+		goto __end;
+	/* AnalogDevices CNR boards uses special codec chaining */
+	/* skip standard test method for secondary codecs in this case */
+	if (x97->flags & AC97_AD_MULTI) {
+		codecs = 1;
+		goto __skip_secondary;
+	}
+	if (codecs < 2)
+		goto __skip_secondary;
+	for (i = 1; i < codecs; i++) {
+		if ((err = snd_ac97_mixer(chip->card, &ac97, &x97)) < 0)
+			return err;
+		chip->ac97[i] = x97;
+		if (chip->device_type == DEVICE_INTEL_ICH4 && chip->ichd[ICHD_PCM2IN].ac97 == NULL)
+			chip->ichd[ICHD_PCM2IN].ac97 = x97;
+		if (x97->ext_id & AC97_EI_VRM) {
+			if (chip->ichd[ICHD_MIC].ac97 == NULL)
+				chip->ichd[ICHD_MIC].ac97 = x97;
+			else if (chip->device_type == DEVICE_INTEL_ICH4 &&
+				 chip->ichd[ICHD_MIC2].ac97 == NULL &&
+				 chip->ichd[ICHD_PCM2IN].ac97 == x97)
+				chip->ichd[ICHD_MIC2].ac97 = x97;
+		}
+		if (x97->ext_id & AC97_EI_SPDIF) {
+			if (chip->device_type != DEVICE_ALI) {
+				if (chip->ichd[ICHD_SPBAR].ac97 == NULL)
+					chip->ichd[ICHD_SPBAR].ac97 = x97;
+			} else {
+				if (chip->ichd[ALID_AC97SPDIFOUT].ac97 == NULL)
+					chip->ichd[ALID_AC97SPDIFOUT].ac97 = x97;
+			}
+		}
 	}
-#endif
-	if ((inl(ICHREG(chip, GLOB_STA)) & (ICH_PCM_4|ICH_PCM_6)) != (ICH_PCM_4|ICH_PCM_6))
-		return 0;
-	if ((chip->ac97->scaps & AC97_SCAP_SURROUND_DAC) ||
-	    (chip->ac97sec && (chip->ac97sec->scaps & AC97_SCAP_SURROUND_DAC))) {
-		chip->multi4 = 1;
-		if ((chip->ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) ||
-		    (chip->ac97sec && (chip->ac97sec->scaps & AC97_SCAP_CENTER_LFE_DAC)))
+	
+      __skip_secondary:
+	if (chip->device_type == DEVICE_INTEL_ICH4) {
+		u8 tmp = igetbyte(chip, ICHREG(SDM));
+		tmp &= ~(ICH_DI2L_MASK|ICH_DI1L_MASK);
+		if (chip->ichd[ICHD_PCM2IN].ac97) {
+			tmp |= ICH_SE;	/* steer enable for multiple SDINs */
+			tmp |= chip->ac97_sdin[0] << ICH_DI1L_SHIFT;
+			tmp |= chip->ac97_sdin[chip->ichd[ICHD_PCM2IN].ac97->num] << ICH_DI2L_SHIFT;
+		} else {
+			tmp &= ~ICH_SE;
+		}
+		iputbyte(chip, ICHREG(SDM), tmp);
+	}
+      	for (i = 0; i < 3; i++) {
+		if ((x97 = chip->ac97[i]) == NULL)
+			continue;
+		if (x97->scaps & AC97_SCAP_SURROUND_DAC)
+			chip->multi4 = 1;
+	}
+      	for (i = 0; i < 3 && chip->multi4; i++) {
+		if ((x97 = chip->ac97[i]) == NULL)
+			continue;
+		if (x97->scaps & AC97_SCAP_CENTER_LFE_DAC)
 			chip->multi6 = 1;
 	}
+	if (codecs > 1) {
+		/* assign right slots for rev2.2 codecs */
+		i = 1;
+		if (chip->multi4)
+			goto __6ch;
+		for ( ; i < codecs; i++) {
+			if (ac97_is_rev22(x97 = chip->ac97[i])) {
+				snd_ac97_update_bits(x97, AC97_EXTENDED_ID, AC97_EI_DACS_SLOT_MASK, 1);
+				chip->multi4 = 1;
+				break;
+			}
+		}
+	      __6ch:
+		for ( ; i < codecs && chip->multi4; i++) {
+			if (ac97_is_rev22(x97 = chip->ac97[i])) {
+				snd_ac97_update_bits(x97, AC97_EXTENDED_ID, AC97_EI_DACS_SLOT_MASK, 2);
+				chip->multi6 = 1;
+				break;
+			}
+		}
+		/* ok, some older codecs might support only AMAP */
+		if (!chip->multi4) {
+			for (i = 1; i < codecs; i++) {
+				if (ac97_can_amap(x97 = chip->ac97[i])) {
+					if (x97->addr == 1) {
+						chip->multi4 = 1;
+						break;
+					}
+				}
+			}
+			for ( ; i < codecs && chip->multi4; i++) {
+				if (ac97_can_amap(x97 = chip->ac97[i])) {
+					if (x97->addr == 2) {
+						chip->multi6 = 1;
+						break;
+					}
+				}
+			}
+		}
+	}
+      __end:
 	chip->in_ac97_init = 0;
 	return 0;
 }
@@ -1008,6 +1682,16 @@
  *
  */
 
+static void do_ali_reset(intel8x0_t *chip)
+{
+	iputdword(chip, ICHREG(ALI_SCR), 0x8000000);
+	iputdword(chip, ICHREG(ALI_FIFOCR1), 0x83838383);
+	iputdword(chip, ICHREG(ALI_FIFOCR2), 0x83838383);
+	iputdword(chip, ICHREG(ALI_INTERFACECR), 0x04080002); /* no spdif? */
+	iputdword(chip, ICHREG(ALI_INTERRUPTCR), 0x00000000);
+	iputdword(chip, ICHREG(ALI_INTERRUPTSR), 0x00000000);
+}
+
 static void do_delay(intel8x0_t *chip)
 {
 #ifdef CONFIG_PM
@@ -1020,109 +1704,168 @@
 	schedule_timeout(1);
 }
 
-static int snd_intel8x0_chip_init(intel8x0_t *chip)
+static int snd_intel8x0_ich_chip_init(intel8x0_t *chip)
 {
 	signed long end_time;
-	unsigned int cnt;
+	unsigned int cnt, status, nstatus;
 	
 	/* put logic to right state */
 	/* first clear status bits */
-	cnt = inl(ICHREG(chip, GLOB_STA));
-	outl(cnt & (ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT), ICHREG(chip, GLOB_STA));
+	cnt = igetdword(chip, ICHREG(GLOB_STA));
+	iputdword(chip, ICHREG(GLOB_STA), cnt & (ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT));
 
 	/* ACLink on, 2 channels */
-	cnt = inl(ICHREG(chip, GLOB_CNT));
+	cnt = igetdword(chip, ICHREG(GLOB_CNT));
 	cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK);
 	/* finish cold or do warm reset */
 	cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
-	outl(cnt, ICHREG(chip, GLOB_CNT));
+	iputdword(chip, ICHREG(GLOB_CNT), cnt);
 	end_time = (jiffies + (HZ / 4)) + 1;
 	do {
-		if ((inl(ICHREG(chip, GLOB_CNT)) & ICH_AC97WARM) == 0)
+		if ((igetdword(chip, ICHREG(GLOB_CNT)) & ICH_AC97WARM) == 0)
 			goto __ok;
 		do_delay(chip);
-	} while (end_time - (signed long)jiffies >= 0);
-	snd_printk("AC'97 warm reset still in progress? [0x%x]\n", inl(ICHREG(chip, GLOB_CNT)));
+	} while (time_after_eq(end_time, jiffies));
+	snd_printk("AC'97 warm reset still in progress? [0x%x]\n", igetdword(chip, ICHREG(GLOB_CNT)));
 	return -EIO;
 
       __ok:
-	/* wait for primary codec ready status.
+	/* wait for any codec ready status.
 	 * Once it becomes ready it should remain ready
 	 * as long as we do not disable the ac97 link.
  	 */
-	end_time = jiffies + HZ / 10;
-	do {
-		if (inl(ICHREG(chip, GLOB_STA)) & ICH_PCR)
-			goto __ok1;
-		do_delay(chip);
-	} while (end_time - (signed long)jiffies >= 0);
-	if (chip->pci->device == PCI_DEVICE_ID_INTEL_ICH4) {
-		/* FIXME: ICH4 may have no PCR and SCR bits...  */
-		snd_printd(KERN_INFO "intel8x0: no codec is probed, perhaps PCR and SCR bits are deactivated.\n");
-		chip->no_codec_check = 1;
-		goto __ok2;
-	}
-	/* check a bit longer... */
 	end_time = jiffies + HZ;
 	do {
-		if (inl(ICHREG(chip, GLOB_STA)) & ICH_PCR)
+		status = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR);
+		if (status)
 			goto __ok1;
 		do_delay(chip);
-	} while (end_time - (signed long)jiffies >= 0);
-	snd_printk(KERN_ERR "codec_ready: primary codec is not ready [0x%x]\n", inl(ICHREG(chip, GLOB_STA)));
+	} while (time_after_eq(end_time, jiffies));
+	snd_printk(KERN_ERR "codec_ready: codec is not ready [0x%x]\n", igetdword(chip, ICHREG(GLOB_STA)));
 	return -EIO;
 
       __ok1:
-	/* wait for secondary codec ready status. No secondary codec? , ok */
-	/* the end_time variable is not initialized again */
+      	if (status == (ICH_PCR | ICH_SCR | ICH_TCR))
+      		goto __ok3;
+	/* wait for other codecs ready status. No secondary codecs? , ok */
+	end_time = jiffies + HZ / 4;
 	do {
-		if (inl(ICHREG(chip, GLOB_STA)) & ICH_SCR)
-			break;
+		nstatus = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR);
+		if (nstatus != status) {
+			status = nstatus;
+			goto __ok2;
+		}
 		do_delay(chip);
-	} while (end_time - (signed long)jiffies >= 0);
+	} while (time_after_eq(end_time, jiffies));
 
       __ok2:
-	inw(chip->port);	/* clear semaphore flag */
+      	if (status == (ICH_PCR | ICH_SCR | ICH_TCR))
+      		goto __ok3;
+	/* wait for other codecs ready status. No other secondary codecs? , ok */
+	/* end_time is not initialized here */
+	do {
+		nstatus = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR);
+		if (nstatus != status) {
+			status = nstatus;
+			goto __ok2;
+		}
+		do_delay(chip);
+	} while (time_after_eq(end_time, jiffies));
+
+      __ok3:      
+      	return 0;
+}
+
+static int snd_intel8x0_ali_chip_init(intel8x0_t *chip)
+{
+	u32 reg;
+	int i = 0;
+
+	reg = igetdword(chip, ICHREG(ALI_SCR));
+	if ((reg & 2) == 0)	/* Cold required */
+		reg |= 2;
+	else
+		reg |= 1;	/* Warm */
+	reg &= ~0x80000000;	/* ACLink on */
+	iputdword(chip, ICHREG(ALI_SCR), reg);
+
+	for (i = 0; i < HZ / 2; i++) {
+		if (! (igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & ALI_INT_GPIO))
+			goto __ok;
+		do_delay(chip);
+	}
+	snd_printk(KERN_ERR "AC'97 reset failed.\n");
+	return -EIO;
+
+ __ok:
+	for (i = 0; i < HZ / 2; i++) {
+		reg = igetdword(chip, ICHREG(ALI_RTSR));
+		if (reg & 0x80) /* primary codec */
+			break;
+		iputdword(chip, ICHREG(ALI_RTSR), reg | 0x80);
+		do_delay(chip);
+	}
+
+	do_ali_reset(chip);
+	return 0;
+}
+
+static int snd_intel8x0_chip_init(intel8x0_t *chip)
+{
+	int i, err;
+	
+	if (chip->device_type != DEVICE_ALI)
+		err = snd_intel8x0_ich_chip_init(chip);
+	else
+		err = snd_intel8x0_ali_chip_init(chip);
+	if (err < 0)
+		return err;
+
+	iagetword(chip, 0);	/* clear semaphore flag */
 
 	/* disable interrupts */
-	outb(0x00, ICHREG(chip, PI_CR));
-	outb(0x00, ICHREG(chip, PO_CR));
-	outb(0x00, ICHREG(chip, MC_CR));
+	for (i = 0; i < chip->bdbars_count; i++)
+		iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00);
 	/* reset channels */
-	outb(ICH_RESETREGS, ICHREG(chip, PI_CR));
-	outb(ICH_RESETREGS, ICHREG(chip, PO_CR));
-	outb(ICH_RESETREGS, ICHREG(chip, MC_CR));
+	for (i = 0; i < chip->bdbars_count; i++)
+		iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
 	/* initialize Buffer Descriptor Lists */
-	outl(chip->playback.bdbar_addr, ICHREG(chip, PO_BDBAR));
-	outl(chip->capture.bdbar_addr, ICHREG(chip, PI_BDBAR));
-	outl(chip->capture_mic.bdbar_addr, ICHREG(chip, MC_BDBAR));
+	for (i = 0; i < chip->bdbars_count; i++)
+		iputdword(chip, ICH_REG_OFF_BDBAR + chip->ichd[i].reg_offset, chip->ichd[i].bdbar_addr);
 	return 0;
 }
 
+static void snd_intel8x0_proc_done(intel8x0_t * chip);
+
 static int snd_intel8x0_free(intel8x0_t *chip)
 {
+	int i;
+
 	if (chip->irq < 0)
 		goto __hw_end;
 	/* disable interrupts */
-	outb(0x00, ICHREG(chip, PI_CR));
-	outb(0x00, ICHREG(chip, PO_CR));
-	outb(0x00, ICHREG(chip, MC_CR));
+	for (i = 0; i < chip->bdbars_count; i++)
+		iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00);
 	/* reset channels */
-	outb(ICH_RESETREGS, ICHREG(chip, PI_CR));
-	outb(ICH_RESETREGS, ICHREG(chip, PO_CR));
-	outb(ICH_RESETREGS, ICHREG(chip, MC_CR));
+	for (i = 0; i < chip->bdbars_count; i++)
+		iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
 	/* --- */
 	synchronize_irq(chip->irq);
       __hw_end:
+	snd_intel8x0_proc_done(chip);
 	if (chip->bdbars)
-		snd_free_pci_pages(chip->pci, 3 * sizeof(u32) * ICH_MAX_FRAGS * 2, chip->bdbars, chip->bdbars_addr);
-	if (chip->res_port) {
-		release_resource(chip->res_port);
-		kfree_nocheck(chip->res_port);
-	}
-	if (chip->res_bmport) {
-		release_resource(chip->res_bmport);
-		kfree_nocheck(chip->res_bmport);
+		snd_free_pci_pages(chip->pci, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, chip->bdbars, chip->bdbars_addr);
+	if (chip->remap_addr)
+		iounmap((void *) chip->remap_addr);
+	if (chip->remap_bmaddr)
+		iounmap((void *) chip->remap_bmaddr);
+	if (chip->res) {
+		release_resource(chip->res);
+		kfree_nocheck(chip->res);
+	}
+	if (chip->res_bm) {
+		release_resource(chip->res_bm);
+		kfree_nocheck(chip->res_bm);
 	}
 	if (chip->irq >= 0)
 		free_irq(chip->irq, (void *)chip);
@@ -1154,6 +1897,7 @@
 static void intel8x0_resume(intel8x0_t *chip)
 {
 	snd_card_t *card = chip->card;
+	int i;
 
 	snd_power_lock(card);
 	if (card->power_state == SNDRV_CTL_POWER_D0)
@@ -1161,7 +1905,9 @@
 
 	pci_enable_device(chip->pci);
 	snd_intel8x0_chip_init(chip);
-	snd_ac97_resume(chip->ac97);
+	for (i = 0; i < 3; i++)
+		if (chip->ac97[i])
+			snd_ac97_resume(chip->ac97[i]);
 
 	chip->in_suspend = 0;
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
@@ -1222,12 +1968,13 @@
 static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip)
 {
 	snd_pcm_substream_t *subs;
+	ichdev_t *ichdev;
 	unsigned long port;
 	unsigned long pos, t;
 	unsigned long flags;
 	struct timeval start_time, stop_time;
 
-	if (chip->ac97->clock != 48000)
+	if (chip->ac97[0]->clock != 48000)
 		return; /* specified in module option */
 
 	subs = chip->pcm->streams[0].substream;
@@ -1235,19 +1982,20 @@
 		snd_printk("no playback buffer allocated - aborting measure ac97 clock\n");
 		return;
 	}
-	chip->playback.physbuf = subs->dma_addr;
-	chip->playback.size = chip->playback.fragsize = INTEL8X0_TESTBUF_SIZE;
-	chip->playback.substream = NULL; /* don't process interrupts */
+	ichdev = &chip->ichd[ICHD_PCMOUT];
+	ichdev->physbuf = subs->dma_addr;
+	ichdev->size = chip->ichd[ICHD_PCMOUT].fragsize = INTEL8X0_TESTBUF_SIZE;
+	ichdev->substream = NULL; /* don't process interrupts */
 
 	/* set rate */
-	if (snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, 48000) < 0) {
-		snd_printk(KERN_ERR "cannot set ac97 rate: clock = %d\n", chip->ac97->clock);
+	if (snd_ac97_set_rate(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 48000) < 0) {
+		snd_printk(KERN_ERR "cannot set ac97 rate: clock = %d\n", chip->ac97[0]->clock);
 		return;
 	}
-	snd_intel8x0_setup_periods(chip, &chip->playback);
-	port = chip->bmport + chip->playback.reg_offset;
+	snd_intel8x0_setup_periods(chip, ichdev);
+	port = ichdev->reg_offset;
 	spin_lock_irqsave(&chip->reg_lock, flags);
-	outb(ICH_IOCE | ICH_STARTBM, port + ICH_REG_PI_CR); /* trigger */
+	iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM); /* trigger */
 	do_gettimeofday(&start_time);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 #if 0
@@ -1259,18 +2007,18 @@
 #endif
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	/* check the position */
-	pos = chip->playback.fragsize1;
+	pos = ichdev->fragsize1;
 	if (chip->device_type == DEVICE_SIS)
-		pos -= inw(ICHREG2(chip,chip->reg_po_picb));
+		pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
 	else
-		pos -= inw(ICHREG2(chip,chip->reg_po_picb)) << 1;
-	pos += chip->playback.position;
+		pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << 1;
+	pos += ichdev->position;
 	do_gettimeofday(&stop_time);
-	outb(0, port + ICH_REG_PI_CR); /* stop */
+	iputbyte(chip, port + ICH_REG_OFF_CR, 0); /* stop */
 	/* reset whole DMA things */
-	while (!(inb(port + chip->reg_pi_sr) & ICH_DCH))
+	while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH))
 		;
-	outb(ICH_RESETREGS, port + ICH_REG_PI_CR);
+	iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 
 	t = stop_time.tv_sec - start_time.tv_sec;
@@ -1290,8 +2038,60 @@
 		printk(KERN_INFO "intel8x0: measured clock %ld rejected\n", pos);
 	else if (pos < 47500 || pos > 48500)
 		/* not 48000Hz, tuning the clock.. */
-		chip->ac97->clock = (chip->ac97->clock * 48000) / pos;
-	printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97->clock);
+		chip->ac97[0]->clock = (chip->ac97[0]->clock * 48000) / pos;
+	printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97[0]->clock);
+}
+
+static void snd_intel8x0_proc_read(snd_info_entry_t * entry,
+				   snd_info_buffer_t * buffer)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, entry->private_data, return);
+	unsigned int tmp;
+
+	snd_iprintf(buffer, "Intel8x0\n\n");
+	if (chip->device_type == DEVICE_ALI)
+		return;
+	tmp = igetdword(chip, ICHREG(GLOB_STA));
+	snd_iprintf(buffer, "Global control        : 0x%08x\n", igetdword(chip, ICHREG(GLOB_CNT)));
+	snd_iprintf(buffer, "Global status         : 0x%08x\n", tmp);
+	if (chip->device_type == DEVICE_INTEL_ICH4)
+		snd_iprintf(buffer, "SDM                   : 0x%08x\n", igetdword(chip, ICHREG(SDM)));
+	snd_iprintf(buffer, "AC'97 codecs ready    :%s%s%s%s\n",
+			tmp & ICH_PCR ? " primary" : "",
+			tmp & ICH_SCR ? " secondary" : "",
+			tmp & ICH_TCR ? " tertiary" : "",
+			(tmp & (ICH_PCR | ICH_SCR | ICH_TCR)) == 0 ? " none" : "");
+	if (chip->device_type == DEVICE_INTEL_ICH4)
+		snd_iprintf(buffer, "AC'97 codecs SDIN     : %i %i %i\n",
+			chip->ac97_sdin[0],
+			chip->ac97_sdin[1],
+			chip->ac97_sdin[2]);
+}
+
+static void __devinit snd_intel8x0_proc_init(intel8x0_t * chip)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(chip->card, "intel8x0", chip->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = chip;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 2048;
+		entry->c.text.read = snd_intel8x0_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	chip->proc_entry = entry;
+}
+
+static void snd_intel8x0_proc_done(intel8x0_t * chip)
+{
+	if (chip->proc_entry) {
+		snd_info_unregister(chip->proc_entry);
+		chip->proc_entry = NULL;
+	}
 }
 
 static int snd_intel8x0_dev_free(snd_device_t *device)
@@ -1306,10 +2106,22 @@
 					 intel8x0_t ** r_intel8x0)
 {
 	intel8x0_t *chip;
-	int err;
+	int err, i;
+	unsigned int int_sta_masks;
+	ichdev_t *ichdev;
 	static snd_device_ops_t ops = {
 		.dev_free =	snd_intel8x0_dev_free,
 	};
+	static u32 intel_int_sta_masks[6] = {
+		ICH_PIINT, ICH_POINT, ICH_MCINT, ICH_M2INT, ICH_P2INT, ICH_SPINT
+	};
+	static u32 ali_int_sta_masks[6] = {
+		ALI_INT_PCMIN, ALI_INT_PCMOUT, ALI_INT_MICIN,
+		ALI_INT_CODECSPDIFOUT, ALI_INT_SPDIFIN, ALI_INT_SPDIFOUT
+	};
+	static u32 ali_reg_offsets[6] = {
+		0x40, 0x50, 0x60, 0x70, 0xa0, 0xb0
+	};
 
 	*r_intel8x0 = NULL;
 
@@ -1325,19 +2137,50 @@
 	chip->card = card;
 	chip->pci = pci;
 	chip->irq = -1;
-	chip->port = pci_resource_start(pci, 0);
-	sprintf(chip->ac97_name, "%s - AC'97", card->shortname);
-	if ((chip->res_port = request_region(chip->port, 256, chip->ac97_name)) == NULL) {
-		snd_intel8x0_free(chip);
-		snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1);
-		return -EBUSY;
-	}
-	sprintf(chip->ctrl_name, "%s - Controller", card->shortname);
-	chip->bmport = pci_resource_start(pci, 1);
-	if ((chip->res_bmport = request_region(chip->bmport, 64, chip->ctrl_name)) == NULL) {
-		snd_intel8x0_free(chip);
-		snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmport, chip->bmport + 64 - 1);
-		return -EBUSY;
+	snd_intel8x0_proc_init(chip);
+	if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) {	/* ICH4 and higher */
+		chip->mmio = chip->bm_mmio = 1;
+		chip->addr = pci_resource_start(pci, 2);
+		sprintf(chip->ac97_name, "%s - AC'97", card->shortname);
+		if ((chip->res = request_mem_region(chip->addr, 512, chip->ac97_name)) == NULL) {
+			snd_intel8x0_free(chip);
+			snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", chip->addr, chip->addr + 512 - 1);
+			return -EBUSY;
+		}
+		chip->remap_addr = (unsigned long) ioremap_nocache(chip->addr, 512);
+		if (chip->remap_addr == 0) {
+			snd_intel8x0_free(chip);
+			snd_printk("AC'97 space ioremap problem\n");
+			return -EIO;
+		}
+		sprintf(chip->ctrl_name, "%s - Controller", card->shortname);
+		chip->bmaddr = pci_resource_start(pci, 3);
+		if ((chip->res_bm = request_mem_region(chip->bmaddr, 256, chip->ctrl_name)) == NULL) {
+			snd_intel8x0_free(chip);
+			snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 512 - 1);
+			return -EBUSY;
+		}
+		chip->remap_bmaddr = (unsigned long) ioremap_nocache(chip->bmaddr, 256);
+		if (chip->remap_bmaddr == 0) {
+			snd_intel8x0_free(chip);
+			snd_printk("Controller space ioremap problem\n");
+			return -EIO;
+		}
+	} else {
+		chip->addr = pci_resource_start(pci, 0);
+		sprintf(chip->ac97_name, "%s - AC'97", card->shortname);
+		if ((chip->res = request_region(chip->addr, 256, chip->ac97_name)) == NULL) {
+			snd_intel8x0_free(chip);
+			snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->addr, chip->addr + 256 - 1);
+			return -EBUSY;
+		}
+		sprintf(chip->ctrl_name, "%s - Controller", card->shortname);
+		chip->bmaddr = pci_resource_start(pci, 1);
+		if ((chip->res_bm = request_region(chip->bmaddr, 64, chip->ctrl_name)) == NULL) {
+			snd_intel8x0_free(chip);
+			snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 64 - 1);
+			return -EBUSY;
+		}
 	}
 	if (request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) {
 		snd_intel8x0_free(chip);
@@ -1349,27 +2192,36 @@
 	synchronize_irq(chip->irq);
 
 	/* initialize offsets */
-	chip->reg_pi_sr = ICH_REG_PI_SR;
-	chip->reg_pi_picb = ICH_REG_PI_PICB;
-	chip->reg_po_sr = ICH_REG_PO_SR;
-	chip->reg_po_picb = ICH_REG_PO_PICB;
-	chip->reg_mc_sr = ICH_REG_MC_SR;
-	chip->reg_mc_picb = ICH_REG_MC_PICB;
-	if (device_type == DEVICE_SIS) {
-		chip->reg_pi_sr = ICH_REG_PI_PICB;
-		chip->reg_pi_picb = ICH_REG_PI_SR;
-		chip->reg_po_sr = ICH_REG_PO_PICB;
-		chip->reg_po_picb = ICH_REG_PO_SR;
-		chip->reg_mc_sr = ICH_REG_MC_PICB;
-		chip->reg_mc_picb = ICH_REG_MC_SR;
-	}
-	chip->playback.reg_offset = 0x10;
-	chip->capture.reg_offset = 0;
-	chip->capture_mic.reg_offset = 0x20;
+	for (i = 0; i <= ICHD_LAST; i++) {
+		ichdev = &chip->ichd[i];
+		ichdev->ichd = i;
+		ichdev->reg_offset = i * 0x10 + (i >= 0x30 ? 0x10 : 0);
+		ichdev->roff_sr = ICH_REG_OFF_SR;
+		ichdev->roff_picb = ICH_REG_OFF_PICB;
+		ichdev->int_sta_mask = device_type == DEVICE_ALI ? ali_int_sta_masks[i] : intel_int_sta_masks[i];
+	}
+	switch (device_type) {
+	case DEVICE_SIS:
+		for (i = 0; i <= ICHD_LAST; i++) {
+			ichdev = &chip->ichd[i];
+			ichdev->roff_sr = ICH_REG_OFF_PICB;
+			ichdev->roff_picb = ICH_REG_OFF_SR;
+		}
+		break;
+	case DEVICE_ALI:
+		for (i = 0; i <= ALID_LAST; i++) {
+			ichdev = &chip->ichd[i];
+			ichdev->reg_offset = ali_reg_offsets[i];
+			ichdev->ali_slot = i + 1;	/* is this right for last three devices? --jk */
+		}
+	}
 
 	/* allocate buffer descriptor lists */
 	/* the start of each lists must be aligned to 8 bytes */
-	chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, 3 * sizeof(unsigned int) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr);
+	chip->bdbars_count = 3;
+	if (device_type == DEVICE_INTEL_ICH4 || device_type == DEVICE_ALI)
+		chip->bdbars_count = 6;
+	chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, chip->bdbars_count * sizeof(unsigned int) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr);
 	if (chip->bdbars == NULL) {
 		snd_intel8x0_free(chip);
 		return -ENOMEM;
@@ -1384,12 +2236,15 @@
 		return -ENOMEM;
 	}
 #endif
-	chip->playback.bdbar = chip->bdbars; /* crop to 32bit */
-	chip->playback.bdbar_addr = (unsigned int)chip->bdbars_addr;
-	chip->capture.bdbar = chip->playback.bdbar + ICH_MAX_FRAGS * 2;
-	chip->capture.bdbar_addr = chip->playback.bdbar_addr + sizeof(u32) * ICH_MAX_FRAGS * 2;
-	chip->capture_mic.bdbar = chip->capture.bdbar + ICH_MAX_FRAGS * 2;
-	chip->capture_mic.bdbar_addr = chip->capture.bdbar_addr + sizeof(u32) * ICH_MAX_FRAGS * 2;
+	int_sta_masks = 0;
+	for (i = 0; i < chip->bdbars_count; i++) {
+		ichdev = &chip->ichd[i];
+		ichdev->bdbar = chip->bdbars + (i * ICH_MAX_FRAGS * 2);
+		ichdev->bdbar_addr = chip->bdbars_addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2);
+		int_sta_masks |= ichdev->int_sta_mask;
+	}
+	chip->int_sta_reg = device_type == DEVICE_ALI ? ICH_REG_ALI_INTERRUPTSR : ICH_REG_GLOB_STA;
+	chip->int_sta_mask = int_sta_masks;
 
 	if ((err = snd_intel8x0_chip_init(chip)) < 0) {
 		snd_intel8x0_free(chip);
@@ -1469,15 +2324,52 @@
 		snd_card_free(card);
 		return err;
 	}
-	if (chip->ac97->ext_id & 0x0008) {	/* MIC VRM */
+	/* activate MIC PCM only when associated AC'97 codec */
+	if (chip->ichd[ICHD_MIC].ac97) {
 		if ((err = snd_intel8x0_pcm_mic(chip, pcm_dev++, NULL)) < 0) {
 			snd_card_free(card);
 			return err;
 		}
 	}
+	if (chip->device_type == DEVICE_ALI) {
+		if ((err = snd_intel8x0_ali_spdif(chip, pcm_dev++, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	/* activate AC'97 S/PDIF only when associated AC'97 codec */
+	if (chip->bdbars_count > 3) {
+		err = 0;
+		if (chip->device_type == DEVICE_ALI) {
+			if (chip->ichd[ALID_AC97SPDIFOUT].ac97)
+				err = snd_intel8x0_ali_ac97spdif(chip, pcm_dev++, NULL);
+		} else {
+			if (chip->ichd[ICHD_SPBAR].ac97)
+				err = snd_intel8x0_pcm_spdif(chip, pcm_dev++, NULL);
+		}
+		if (err < 0) {
+			snd_card_free(card);
+			return err;
+		}
+		if (chip->device_type != DEVICE_ALI) {
+			/* activate MIC2 only when associated AC'97 codec */
+			if (chip->ichd[ICHD_MIC2].ac97)
+				if ((err = snd_intel8x0_pcm_mic2(chip, pcm_dev++, NULL)) < 0) {
+					snd_card_free(card);
+					return err;
+				}
+			/* activate PCM2IN only when associated AC'97 codec */
+			if (chip->ichd[ICHD_PCM2IN].ac97)
+				if ((err = snd_intel8x0_pcm_capture2(chip, pcm_dev++, NULL)) < 0) {
+					snd_card_free(card);
+					return err;
+				}
+		}
+	}
+	
 	
 	if (snd_mpu_port[dev] == 0x300 || snd_mpu_port[dev] == 0x330) {
-		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_INTEL8X0,
 					       snd_mpu_port[dev], 0,
 					       -1, 0, &chip->rmidi)) < 0) {
 			printk(KERN_ERR "intel8x0: no UART401 device at 0x%x, skipping.\n", snd_mpu_port[dev]);
@@ -1487,7 +2379,7 @@
 		snd_mpu_port[dev] = 0;
 
 	sprintf(card->longname, "%s at 0x%lx, irq %i",
-		card->shortname, chip->port, chip->irq);
+		card->shortname, chip->addr, chip->irq);
 
 	if (! snd_ac97_clock[dev])
 		intel8x0_measure_ac97_clock(chip);
diff -Nru a/sound/pci/via686.c b/sound/pci/via686.c
--- a/sound/pci/via686.c	Tue Oct  1 17:08:04 2002
+++ b/sound/pci/via686.c	Tue Oct  1 17:08:04 2002
@@ -605,7 +605,7 @@
 	.period_bytes_min =	32,
 	.period_bytes_max =	128 * 1024,
 	.periods_min =		2,
-	.periods_max =		1024,
+	.periods_max =		128,
 	.fifo_size =		0,
 };
 
@@ -624,7 +624,7 @@
 	.period_bytes_min =	32,
 	.period_bytes_max =	128 * 1024,
 	.periods_min =		2,
-	.periods_max =		1024,
+	.periods_max =		128,
 	.fifo_size =		0,
 };
 
@@ -636,21 +636,17 @@
 
 	chip->playback.substream = substream;
 	runtime->hw = snd_via686a_playback;
-	runtime->hw.rates = chip->ac97->rates_front_dac;
+	runtime->hw.rates = chip->ac97->rates[AC97_RATES_FRONT_DAC];
 	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
 		runtime->hw.rate_min = 48000;
 	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
 		return err;
 	if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
 		return err;
-#if 0
-	/* applying the following constraint together with the power-of-2 rule
-	 * above may result in too narrow space.
-	 * this one is not strictly necessary, so let's disable it.
-	 */
+	/* we may remove following constaint when we modify table entries
+	   in interrupt */
 	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
 		return err;
-#endif
 	return 0;
 }
 
@@ -662,17 +658,15 @@
 
 	chip->capture.substream = substream;
 	runtime->hw = snd_via686a_capture;
-	runtime->hw.rates = chip->ac97->rates_adc;
+	runtime->hw.rates = chip->ac97->rates[AC97_RATES_ADC];
 	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
 		runtime->hw.rate_min = 48000;
 	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
 		return err;
 	if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
 		return err;
-#if 0
 	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
 		return err;
-#endif
 	return 0;
 }
 
@@ -683,8 +677,6 @@
 	clean_via_table(&chip->playback, substream, chip->pci);
 	snd_pcm_sgbuf_delete(substream);
 	chip->playback.substream = NULL;
-	/* disable DAC power */
-	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
 	return 0;
 }
 
@@ -695,8 +687,6 @@
 	clean_via_table(&chip->capture, substream, chip->pci);
 	snd_pcm_sgbuf_delete(substream);
 	chip->capture.substream = NULL;
-	/* disable ADC power */
-	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
 	return 0;
 }
 
@@ -764,16 +754,6 @@
  *  Mixer part
  */
 
-static void snd_via686a_codec_init(ac97_t *ac97)
-{
-	// via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
-
-	/* disable DAC & ADC power */
-	snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300);
-	/* disable center DAC/surround DAC/LFE DAC/MIC ADC */
-	snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
-}
-
 static void snd_via686a_mixer_free_ac97(ac97_t *ac97)
 {
 	via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
@@ -788,7 +768,6 @@
 	memset(&ac97, 0, sizeof(ac97));
 	ac97.write = snd_via686a_codec_write;
 	ac97.read = snd_via686a_codec_read;
-	ac97.init = snd_via686a_codec_init;
 	ac97.wait = snd_via686a_codec_wait;
 	ac97.private_data = chip;
 	ac97.private_free = snd_via686a_mixer_free_ac97;
diff -Nru a/sound/pci/via8233.c b/sound/pci/via8233.c
--- a/sound/pci/via8233.c	Tue Oct  1 17:08:04 2002
+++ b/sound/pci/via8233.c	Tue Oct  1 17:08:04 2002
@@ -591,7 +591,7 @@
 	.period_bytes_min =	32,
 	.period_bytes_max =	128 * 1024,
 	.periods_min =		2,
-	.periods_max =		1024,
+	.periods_max =		128,
 	.fifo_size =		0,
 };
 
@@ -610,7 +610,7 @@
 	.period_bytes_min =	32,
 	.period_bytes_max =	128 * 1024,
 	.periods_min =		2,
-	.periods_max =		1024,
+	.periods_max =		128,
 	.fifo_size =		0,
 };
 
@@ -634,17 +634,15 @@
 
 	chip->playback.substream = substream;
 	runtime->hw = snd_via8233_playback;
-	runtime->hw.rates = chip->ac97->rates_front_dac;
+	runtime->hw.rates = chip->ac97->rates[AC97_RATES_FRONT_DAC];
 	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
 		runtime->hw.rate_min = 48000;
 	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
 		return err;
 	if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
 		return err;
-#if 0
 	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
 		return err;
-#endif
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels);
 	return 0;
 }
@@ -657,17 +655,15 @@
 
 	chip->capture.substream = substream;
 	runtime->hw = snd_via8233_capture;
-	runtime->hw.rates = chip->ac97->rates_adc;
+	runtime->hw.rates = chip->ac97->rates[AC97_RATES_ADC];
 	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
 		runtime->hw.rate_min = 48000;
 	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
 		return err;
 	if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
 		return err;
-#if 0
 	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
 		return err;
-#endif
 	return 0;
 }
 
@@ -679,8 +675,6 @@
 	clean_via_table(&chip->playback, substream, chip->pci);
 	snd_pcm_sgbuf_delete(substream);
 	chip->playback.substream = NULL;
-	/* disable DAC power */
-	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
 	return 0;
 }
 
@@ -692,8 +686,6 @@
 	clean_via_table(&chip->capture, substream, chip->pci);
 	snd_pcm_sgbuf_delete(substream);
 	chip->capture.substream = NULL;
-	/* disable ADC power */
-	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
 	return 0;
 }
 
@@ -760,16 +752,6 @@
  *  Mixer part
  */
 
-static void snd_via8233_codec_init(ac97_t *ac97)
-{
-	// via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return);
-
-	/* disable DAC & ADC power */
-	snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300);
-	/* disable center DAC/surround DAC/LFE DAC/MIC ADC */
-	snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
-}
-
 static void snd_via8233_mixer_free_ac97(ac97_t *ac97)
 {
 	via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return);
@@ -784,7 +766,6 @@
 	memset(&ac97, 0, sizeof(ac97));
 	ac97.write = snd_via8233_codec_write;
 	ac97.read = snd_via8233_codec_read;
-	ac97.init = snd_via8233_codec_init;
 	ac97.private_data = chip;
 	ac97.private_free = snd_via8233_mixer_free_ac97;
 	ac97.clock = chip->ac97_clock;
diff -Nru a/sound/usb/Config.help b/sound/usb/Config.help
--- a/sound/usb/Config.help	Tue Oct  1 17:08:04 2002
+++ b/sound/usb/Config.help	Tue Oct  1 17:08:04 2002
@@ -1,5 +1,4 @@
 CONFIG_SND_USB_AUDIO
   Say 'Y' or 'M' to include support for USB audio devices.
-
-CONFIG_SND_USB_MIDI
-  Say 'Y' or 'M' to include support for MIDI devices connected via USB.
+  To support USB MIDI devices, you need to enable ALSA sequencer support
+  (CONFIG_SND_SEQUENCER).
diff -Nru a/sound/usb/Config.in b/sound/usb/Config.in
--- a/sound/usb/Config.in	Tue Oct  1 17:08:04 2002
+++ b/sound/usb/Config.in	Tue Oct  1 17:08:04 2002
@@ -3,7 +3,6 @@
 mainmenu_option next_comment
 comment 'ALSA USB devices'
 
-dep_tristate 'USB Audio driver' CONFIG_SND_USB_AUDIO $CONFIG_SND
-dep_tristate 'USB MIDI driver' CONFIG_SND_USB_MIDI $CONFIG_SND $CONFIG_SND_SEQUENCER
+dep_tristate 'USB Audio/MIDI driver' CONFIG_SND_USB_AUDIO $CONFIG_SND
 
 endmenu
diff -Nru a/sound/usb/Makefile b/sound/usb/Makefile
--- a/sound/usb/Makefile	Tue Oct  1 17:08:04 2002
+++ b/sound/usb/Makefile	Tue Oct  1 17:08:04 2002
@@ -3,10 +3,14 @@
 #
 
 snd-usb-audio-objs := usbaudio.o usbmixer.o
+ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
 snd-usb-midi-objs := usbmidi.o
+endif
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o
-obj-$(CONFIG_SND_USB_MIDI) += snd-usb-midi.o
+ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
+obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-midi.o
+endif
 
 include $(TOPDIR)/Rules.make
diff -Nru a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
--- a/sound/usb/usbaudio.c	Tue Oct  1 17:08:04 2002
+++ b/sound/usb/usbaudio.c	Tue Oct  1 17:08:04 2002
@@ -36,6 +36,7 @@
 #include <sound/core.h>
 #include <sound/info.h>
 #include <sound/pcm.h>
+#include <sound/seq_device.h>
 #define SNDRV_GET_ID
 #include <sound/initval.h>
 
@@ -1234,6 +1235,7 @@
 static void usb_audio_disconnect(struct usb_device *dev, void *ptr);
 
 static struct usb_device_id usb_audio_ids [] = {
+#include "usbquirks.h"
     { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
       .bInterfaceClass = USB_CLASS_AUDIO,
       .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL },
@@ -1716,11 +1718,14 @@
 
 
 /*
- * parse audio control descriptor and create pcm streams
+ * parse audio control descriptor and create pcm/midi streams
  */
 
-static int snd_usb_create_pcm(snd_usb_audio_t *chip, int ctrlif,
-			      unsigned char *buffer, int buflen)
+static int snd_usb_create_midi_interface(snd_usb_audio_t *chip, int ifnum,
+					 const snd_usb_audio_quirk_t *quirk);
+
+static int snd_usb_create_streams(snd_usb_audio_t *chip, int ctrlif,
+				  unsigned char *buffer, int buflen)
 {
 	struct usb_device *dev = chip->dev;
 	struct usb_config_descriptor *config;
@@ -1752,6 +1757,15 @@
 			continue;
 		}
 		iface = &config->interface[j];
+		if (iface->altsetting[0].bInterfaceClass == USB_CLASS_AUDIO &&
+		    iface->altsetting[0].bInterfaceSubClass == USB_SUBCLASS_MIDI_STREAMING) {
+			if (snd_usb_create_midi_interface(chip, j, NULL) < 0) {
+				snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n", dev->devnum, ctrlif, j);
+				continue;
+			}
+			usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1);
+			continue;
+		}
 		if (iface->altsetting[0].bInterfaceClass != USB_CLASS_AUDIO ||
 		    iface->altsetting[0].bInterfaceSubClass != USB_SUBCLASS_AUDIO_STREAMING) {
 			snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n", dev->devnum, ctrlif, j, iface->altsetting[0].bInterfaceClass);
@@ -1808,6 +1822,37 @@
 	return 0;
 }
 
+static int snd_usb_create_midi_interface(snd_usb_audio_t *chip, int ifnum,
+					 const snd_usb_audio_quirk_t *quirk)
+{
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	snd_seq_device_t *seq_device;
+	snd_usb_midi_t *umidi;
+	int err;
+
+	err = snd_seq_device_new(chip->card, chip->next_seq_device,
+				 SNDRV_SEQ_DEV_ID_USBMIDI,
+				 sizeof(snd_usb_midi_t), &seq_device);
+	if (err < 0)
+		return err;
+	chip->next_seq_device++;
+	strcpy(seq_device->name, chip->card->shortname);
+	umidi = (snd_usb_midi_t *)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
+	umidi->chip = chip;
+	umidi->ifnum = ifnum;
+	umidi->quirk = quirk;
+	umidi->seq_client = -1;
+#endif
+	return 0;
+}
+
+static inline int snd_usb_create_quirk(snd_usb_audio_t *chip, int ifnum,
+	       			       const snd_usb_audio_quirk_t *quirk)
+{
+	/* in the future, there may be quirks for PCM devices */
+	return snd_usb_create_midi_interface(chip, ifnum, quirk);
+}
+
 
 /*
  * free the chip instance
@@ -1835,7 +1880,9 @@
 /*
  * create a chip instance and set its names.
  */
-static int snd_usb_audio_create(snd_card_t *card, struct usb_device *dev, snd_usb_audio_t **rchip)
+static int snd_usb_audio_create(snd_card_t *card, struct usb_device *dev,
+				const snd_usb_audio_quirk_t *quirk,
+				snd_usb_audio_t **rchip)
 {
 	snd_usb_audio_t *chip;
 	int err, len;
@@ -1858,18 +1905,50 @@
 	}
 
 	strcpy(card->driver, "USB-Audio");
-	strcpy(card->shortname, "USB Audio Driver");
+
+	/* retrieve the device string as shortname */
+	if (dev->descriptor.iProduct)
+		len = usb_string(dev, dev->descriptor.iProduct,
+				 card->shortname, sizeof(card->shortname));
+	else
+		len = 0;
+	if (len <= 0) {
+ 		if (quirk && quirk->product_name) {
+			strncpy(card->shortname, quirk->product_name, sizeof(card->shortname) - 1);
+			card->shortname[sizeof(card->shortname) - 1] = '\0';
+		} else {
+			sprintf(card->shortname, "USB Device %#04x:%#04x",
+				dev->descriptor.idVendor, dev->descriptor.idProduct);
+		}
+	}
 
 	/* retrieve the vendor and device strings as longname */
-	len = usb_string(dev, 1, card->longname, sizeof(card->longname) - 1);
-	if (len <= 0)
+	if (dev->descriptor.iManufacturer)
+		len = usb_string(dev, dev->descriptor.iManufacturer,
+				 card->longname, sizeof(card->longname) - 1);
+	else
 		len = 0;
-	else {
+	if (len <= 0) {
+		if (quirk && quirk->vendor_name) {
+			strncpy(card->longname, quirk->vendor_name, sizeof(card->longname) - 2);
+			card->longname[sizeof(card->longname) - 2] = '\0';
+			len = strlen(card->longname);
+		} else {
+			len = 0;
+		}
+	}
+	if (len > 0) {
 		card->longname[len] = ' ';
 		len++;
 	}
-	card->longname[len] = 0;
-	usb_string(dev, 2, card->longname + len, sizeof(card->longname) - len);
+	card->longname[len] = '\0';
+	if ((!dev->descriptor.iProduct
+	     || usb_string(dev, dev->descriptor.iProduct,
+			   card->longname + len, sizeof(card->longname) - len) <= 0)
+	    && quirk && quirk->product_name) {
+		strncpy(card->longname + len, quirk->product_name, sizeof(card->longname) - len - 1);
+		card->longname[sizeof(card->longname) - 1] = '\0';
+	}
 
 	*rchip = chip;
 	return 0;
@@ -1926,12 +2005,16 @@
 			     const struct usb_device_id *id)
 {
 	struct usb_config_descriptor *config = dev->actconfig;	
+	const snd_usb_audio_quirk_t *quirk = (const snd_usb_audio_quirk_t *)id->driver_info;
 	unsigned char *buffer;
 	unsigned int index;
 	int i, buflen;
 	snd_card_t *card;
 	snd_usb_audio_t *chip;
 
+	if (quirk && ifnum != quirk->ifnum)
+		return NULL;
+
 	if (usb_set_configuration(dev, config->bConfigurationValue) < 0) {
 		snd_printk(KERN_ERR "cannot set configuration (value 0x%x)\n", config->bConfigurationValue);
 		return NULL;
@@ -1966,7 +2049,7 @@
 					snd_printk(KERN_ERR "cannot create a card instance %d\n", i);
 					goto __error;
 				}
-				if (snd_usb_audio_create(card, dev, &chip) < 0) {
+				if (snd_usb_audio_create(card, dev, quirk, &chip) < 0) {
 					snd_card_free(card);
 					goto __error;
 				}
@@ -1980,10 +2063,15 @@
 		}
 	}
 
-	if (snd_usb_create_pcm(chip, ifnum, buffer, buflen) < 0)
-		goto __error;
-	if (snd_usb_create_mixer(chip, ifnum, buffer, buflen) < 0)
-		goto __error;
+	if (!quirk) {
+		if (snd_usb_create_streams(chip, ifnum, buffer, buflen) < 0)
+			goto __error;
+		if (snd_usb_create_mixer(chip, ifnum, buffer, buflen) < 0)
+			goto __error;
+	} else {
+		if (snd_usb_create_quirk(chip, ifnum, quirk) < 0)
+			goto __error;
+	}
 
 	/* we are allowed to call snd_card_register() many times */
 	if (snd_card_register(chip->card) < 0) {
diff -Nru a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
--- a/sound/usb/usbaudio.h	Tue Oct  1 17:08:04 2002
+++ b/sound/usb/usbaudio.h	Tue Oct  1 17:08:04 2002
@@ -27,6 +27,7 @@
 
 #define USB_SUBCLASS_AUDIO_CONTROL	0x01
 #define USB_SUBCLASS_AUDIO_STREAMING	0x02
+#define USB_SUBCLASS_MIDI_STREAMING	0x03
 
 #define USB_DT_CS_DEVICE                0x21
 #define USB_DT_CS_CONFIG                0x22
@@ -56,6 +57,8 @@
 
 #define EP_GENERAL			0x01
 
+#define MS_GENERAL			0x01
+
 /* endpoint attributes */
 #define EP_ATTR_MASK			0x0c
 #define EP_ATTR_ASYNC			0x04
@@ -115,6 +118,11 @@
 #define USB_AUDIO_FORMAT_IEC1937_MPEG2_LAYER23_LS	0x2006
 
 
+/* maximum number of endpoints per interface */
+#define MIDI_MAX_ENDPOINTS 2
+
+#define SNDRV_SEQ_DEV_ID_USBMIDI "usb-midi"
+
 /*
  */
 
@@ -130,7 +138,52 @@
 	struct list_head pcm_list;	/* list of pcm streams */
 	int pcm_devs;
 
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	int next_seq_device;
+#endif
 };  
+
+/*
+ * Information about devices with broken descriptors
+ */
+typedef struct snd_usb_audio_quirk snd_usb_audio_quirk_t;
+typedef struct snd_usb_midi_endpoint_info snd_usb_midi_endpoint_info_t;
+
+struct snd_usb_audio_quirk {
+	const char *vendor_name;
+	const char *product_name;
+	int ifnum;
+
+	/* MIDI specific */
+	struct snd_usb_midi_endpoint_info {
+		int16_t epnum;		/* ep number, -1 autodetect */
+		uint16_t out_cables;	/* bitmask */
+		uint16_t in_cables;	/* bitmask */
+	} endpoints[MIDI_MAX_ENDPOINTS];
+};
+
+/*
+ * USB MIDI sequencer device data
+ */
+typedef struct snd_usb_midi snd_usb_midi_t;
+typedef struct snd_usb_midi_endpoint snd_usb_midi_endpoint_t;
+typedef struct snd_usb_midi_out_endpoint snd_usb_midi_out_endpoint_t;
+typedef struct snd_usb_midi_in_endpoint snd_usb_midi_in_endpoint_t;
+
+struct snd_usb_midi {
+	/* filled by usbaudio.c */
+	snd_usb_audio_t *chip;
+	int ifnum;
+	const snd_usb_audio_quirk_t *quirk;
+
+	/* used internally in usbmidi.c */
+	int seq_client;
+	struct snd_usb_midi_endpoint {
+		snd_usb_midi_out_endpoint_t *out;
+		snd_usb_midi_in_endpoint_t *in;
+		snd_rawmidi_t *rmidi[0x10];
+	} endpoints[MIDI_MAX_ENDPOINTS];
+};
 
 
 /*
diff -Nru a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
--- a/sound/usb/usbmidi.c	Tue Oct  1 17:08:04 2002
+++ b/sound/usb/usbmidi.c	Tue Oct  1 17:08:04 2002
@@ -40,7 +40,6 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/usb.h>
-#include <asm/semaphore.h>
 #include <sound/core.h>
 #include <sound/minors.h>
 #include <sound/asequencer.h>
@@ -48,59 +47,13 @@
 #include <sound/seq_kernel.h>
 #include <sound/seq_virmidi.h>
 #include <sound/seq_midi_event.h>
-#define SNDRV_GET_ID
 #include <sound/initval.h>
+#include "usbaudio.h"
 
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_DESCRIPTION("USB MIDI");
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_CLASSES("{sound}");
-MODULE_DEVICES("{{Generic,USB MIDI},"
-		"{Roland/EDIROL,PC-300},"
-		"{Roland/EDIROL,SC-8820},"
-		"{Roland/EDIROL,SC-8850},"
-		"{Roland/EDIROL,SC-D70},"
-		"{Roland/EDIROL,SD-20},"
-		"{Roland/EDIROL,SD-80},"
-		"{Roland/EDIROL,SD-90},"
-		"{Roland/EDIROL,SK-500},"
-		"{Roland/EDIROL,U-8},"
-		"{Roland/EDIROL,UA-100(G)},"
-		"{Roland/EDIROL,UA-700},"
-		"{Roland/EDIROL,UM-1(S)},"
-		"{Roland/EDIROL,UM-2(E)},"
-		"{Roland/EDIROL,UM-4},"
-		"{Roland/EDIROL,UM-550},"
-		"{Roland/EDIROL,UM-880},"
-		"{Roland/EDIROL,XV-5050},"
-		"{Yamaha,MU1000},"
-		"{Yamaha,UX256}}");
-
-static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
-static char* snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
-static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int snd_vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Vendor id of this card */
-static int snd_pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Product id of this card */
-static int snd_int_transfer[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* Use interrupt transfers for this card */
-
-MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_index, "Index value for USB MIDI.");
-MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
-MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
-MODULE_PARM_DESC(snd_id, "ID string for USB MIDI.");
-MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
-MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_enable, "Enable USB MIDI.");
-MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
-MODULE_PARM(snd_vid, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_vid, "USB Vendor ID for USB MIDI.");
-MODULE_PARM_SYNTAX(snd_vid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16");
-MODULE_PARM(snd_pid, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_pid, "USB Product ID for USB MIDI.");
-MODULE_PARM_SYNTAX(snd_pid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16");
-MODULE_PARM(snd_int_transfer, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_int_transfer, "Use interrupt transfers for USB MIDI input.");
-MODULE_PARM_SYNTAX(snd_int_transfer, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC ",skill:advanced");
 
 /* size of the per-endpoint output buffer, must be a multiple of 4 */
 #define OUTPUT_BUFFER_SIZE 0x400
@@ -108,26 +61,6 @@
 /* max. size of incoming sysex messages */
 #define INPUT_BUFFER_SIZE 0x200
 
-#define MAX_ENDPOINTS 2
-
-#define SNDRV_SEQ_DEV_ID_USBMIDI "usb-midi"
-
-#ifndef USB_SUBCLASS_MIDISTREAMING
-#define USB_SUBCLASS_MIDISTREAMING 3
-#endif
-
-#ifndef USB_DT_CS_INTERFACE
-#define USB_DT_CS_INTERFACE (USB_TYPE_CLASS | USB_DT_INTERFACE)
-#define USB_DT_CS_ENDPOINT (USB_TYPE_CLASS | USB_DT_ENDPOINT)
-#endif
-
-#ifndef USB_DST_MS_HEADER
-#define USB_DST_MS_HEADER 0x01
-#define USB_DST_MS_GENERAL 0x01
-#define USB_DST_MS_HEADER_SIZE 7
-#define USB_DST_MS_GENERAL_SIZE 4
-#endif
-
 typedef struct usb_driver usb_driver_t;
 typedef struct usb_device usb_device_t;
 typedef struct usb_device_id usb_device_id_t;
@@ -153,47 +86,11 @@
 	__u8  baAssocJackID[0];
 } __attribute__ ((packed));
 
-typedef struct usbmidi usbmidi_t;
-typedef struct usbmidi_device_info usbmidi_device_info_t;
-typedef struct usbmidi_endpoint_info usbmidi_endpoint_info_t;
-typedef struct usbmidi_endpoint usbmidi_endpoint_t;
-typedef struct usbmidi_out_endpoint usbmidi_out_endpoint_t;
 typedef struct usbmidi_out_port usbmidi_out_port_t;
-typedef struct usbmidi_in_endpoint usbmidi_in_endpoint_t;
 typedef struct usbmidi_in_port usbmidi_in_port_t;
 
-/*
- * Describes the capabilities of a USB MIDI device.
- * This structure is filled after parsing the USB descriptors,
- * or is supplied explicitly for broken devices.
- */
-struct usbmidi_device_info {
-	char vendor[32];		/* vendor name */
-	char product[32];		/* device name */
-	int16_t ifnum;			/* interface number */
-	struct usbmidi_endpoint_info {
-		int16_t epnum;		/* endpoint number,
-					   -1: autodetect (first ep only) */
-		uint16_t out_cables;	/* bitmask */
-		uint16_t in_cables;	/* bitmask */
-	} endpoints[MAX_ENDPOINTS];
-};
-
-struct usbmidi {
-	snd_card_t* card;
-	usb_device_t* usb_device;
-	int dev;
-	int seq_client;
-	usbmidi_device_info_t device_info;
-	struct usbmidi_endpoint {
-		usbmidi_out_endpoint_t* out;
-		usbmidi_in_endpoint_t* in;
-		snd_rawmidi_t* rmidi[0x10];
-	} endpoints[MAX_ENDPOINTS];
-};
-
-struct usbmidi_out_endpoint {
-	usbmidi_t* umidi;
+struct snd_usb_midi_out_endpoint {
+	snd_usb_midi_t* umidi;
 	struct urb* urb;
 	int max_transfer;		/* size of urb buffer */
 	struct tasklet_struct tasklet;
@@ -204,16 +101,16 @@
 	spinlock_t buffer_lock;
 
 	struct usbmidi_out_port {
-		usbmidi_out_endpoint_t* ep;
+		snd_usb_midi_out_endpoint_t* ep;
 		uint8_t cable;		/* cable number << 4 */
 		uint8_t sysex_len;
 		uint8_t sysex[2];
 	} ports[0x10];
 };
 
-struct usbmidi_in_endpoint {
-	usbmidi_t* umidi;
-	usbmidi_endpoint_t* ep;
+struct snd_usb_midi_in_endpoint {
+	snd_usb_midi_t* umidi;
+	snd_usb_midi_endpoint_t* ep;
 	struct urb* urb;
 	struct usbmidi_in_port {
 		int seq_port;
@@ -221,10 +118,7 @@
 	} ports[0x10];
 };
 
-static int snd_usbmidi_card_used[SNDRV_CARDS];
-static DECLARE_MUTEX(snd_usbmidi_open_mutex);
-
-static void snd_usbmidi_do_output(usbmidi_out_endpoint_t* ep);
+static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep);
 
 /*
  * Submits the URB, with error handling.
@@ -255,7 +149,7 @@
 /*
  * Converts a USB MIDI packet into an ALSA sequencer event.
  */
-static void snd_usbmidi_input_packet(usbmidi_in_endpoint_t* ep,
+static void snd_usbmidi_input_packet(snd_usb_midi_in_endpoint_t* ep,
 				     uint8_t packet[4])
 {
 	static const uint8_t cin_length[] = {
@@ -285,7 +179,7 @@
  */
 static void snd_usbmidi_in_urb_complete(struct urb* urb)
 {
-	usbmidi_in_endpoint_t* ep = snd_magic_cast(usbmidi_in_endpoint_t, urb->context, return);
+	snd_usb_midi_in_endpoint_t* ep = snd_magic_cast(snd_usb_midi_in_endpoint_t, urb->context, return);
 
 	if (urb->status == 0) {
 		uint8_t* buffer = (uint8_t*)ep->urb->transfer_buffer;
@@ -300,14 +194,14 @@
 	}
 
 	if (!usb_pipeint(urb->pipe)) {
-		urb->dev = ep->umidi->usb_device;
+		urb->dev = ep->umidi->chip->dev;
 		snd_usbmidi_submit_urb(urb, GFP_ATOMIC);
 	}
 }
 
 static void snd_usbmidi_out_urb_complete(struct urb* urb)
 {
-	usbmidi_out_endpoint_t* ep = snd_magic_cast(usbmidi_out_endpoint_t, urb->context, return);
+	snd_usb_midi_out_endpoint_t* ep = snd_magic_cast(snd_usb_midi_out_endpoint_t, urb->context, return);
 	unsigned long flags;
 
 	if (urb->status < 0) {
@@ -324,7 +218,7 @@
  * (after the reception of one or more sequencer events, or after completion
  * of the previous transfer). ep->buffer_lock must be held.
  */
-static void snd_usbmidi_do_output(usbmidi_out_endpoint_t* ep)
+static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep)
 {
 	int len;
 	uint8_t* buffer;
@@ -361,14 +255,14 @@
 	}
 
 	if (len > 0) {
-		ep->urb->dev = ep->umidi->usb_device;
+		ep->urb->dev = ep->umidi->chip->dev;
 		snd_usbmidi_submit_urb(ep->urb, GFP_ATOMIC);
 	}
 }
 
 static void snd_usbmidi_out_tasklet(unsigned long data)
 {
-	usbmidi_out_endpoint_t* ep = snd_magic_cast(usbmidi_out_endpoint_t, (void*)data, return);
+	snd_usb_midi_out_endpoint_t* ep = snd_magic_cast(snd_usb_midi_out_endpoint_t, (void*)data, return);
 	unsigned long flags;
 	
 	spin_lock_irqsave(&ep->buffer_lock, flags);
@@ -382,7 +276,7 @@
 static void output_packet(usbmidi_out_port_t* port,
 			  uint8_t p0, uint8_t p1, uint8_t p2, uint8_t p3)
 {
-	usbmidi_out_endpoint_t* ep = port->ep;
+	snd_usb_midi_out_endpoint_t* ep = port->ep;
 	unsigned long flags;
 
 	spin_lock_irqsave(&ep->buffer_lock, flags);
@@ -568,7 +462,7 @@
  * Frees an input endpoint.
  * May be called when ep hasn't been initialized completely.
  */
-static void snd_usbmidi_in_endpoint_delete(usbmidi_in_endpoint_t* ep)
+static void snd_usbmidi_in_endpoint_delete(snd_usb_midi_in_endpoint_t* ep)
 {
 	int i;
 
@@ -586,53 +480,54 @@
 }
 
 /*
- * Searches for an alternate setting in which the endpoint uses interrupt
+ * For Roland devices, use the alternate setting which uses interrupt
  * transfers for input.
  */
-static int snd_usbmidi_get_int_ep(usbmidi_t* umidi, uint8_t epnum,
-				  usb_endpoint_descriptor_t** descriptor)
+static usb_endpoint_descriptor_t* snd_usbmidi_get_int_epd(snd_usb_midi_t* umidi,
+							  uint8_t epnum)
 {
 	usb_interface_t* intf;
-	int i, j;
+	usb_interface_descriptor_t* intfd;
 
-	*descriptor = NULL;
-	intf = usb_ifnum_to_if(umidi->usb_device, umidi->device_info.ifnum);
-	if (!intf)
-		return -ENXIO;
-	for (i = 0; i < intf->num_altsetting; ++i) {
-		usb_interface_descriptor_t* intfd = &intf->altsetting[i];
-		for (j = 0; j < intfd->bNumEndpoints; ++j) {
-			usb_endpoint_descriptor_t* epd = &intfd->endpoint[j];
-			if ((epd->bEndpointAddress & (USB_ENDPOINT_NUMBER_MASK | USB_ENDPOINT_DIR_MASK)) == (epnum | USB_DIR_IN) &&
-			    (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
-				usb_set_interface(umidi->usb_device,
-						  intfd->bInterfaceNumber,
-						  intfd->bAlternateSetting);
-				*descriptor = &intfd->endpoint[j];
-				return 0;
-			}
-		}
-	}
-	return -ENXIO;
+	if (umidi->chip->dev->descriptor.idVendor != 0x0582)
+		return NULL;
+	intf = usb_ifnum_to_if(umidi->chip->dev, umidi->ifnum);
+	if (!intf || intf->num_altsetting != 2)
+		return NULL;
+
+	intfd = &intf->altsetting[0];
+	if (intfd->bNumEndpoints != 2 ||
+	    (intfd->endpoint[0].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK ||
+	    (intfd->endpoint[1].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK)
+		return NULL;
+
+	intfd = &intf->altsetting[1];
+	if (intfd->bNumEndpoints != 2 ||
+	    (intfd->endpoint[0].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK ||
+	    (intfd->endpoint[1].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
+		return NULL;
+
+	usb_set_interface(umidi->chip->dev, intfd->bInterfaceNumber,
+			  intfd->bAlternateSetting);
+	return &intfd->endpoint[1];
 }
 
 /*
  * Creates an input endpoint, and initalizes input ports.
  * ALSA ports are created later.
  */
-static int snd_usbmidi_in_endpoint_create(usbmidi_t* umidi,
-					  usbmidi_endpoint_info_t* ep_info,
-					  usbmidi_endpoint_t* rep)
+static int snd_usbmidi_in_endpoint_create(snd_usb_midi_t* umidi,
+					  snd_usb_midi_endpoint_info_t* ep_info,
+					  snd_usb_midi_endpoint_t* rep)
 {
-	usbmidi_in_endpoint_t* ep;
-	int do_int_transfer;
-	usb_endpoint_descriptor_t* epd;
+	snd_usb_midi_in_endpoint_t* ep;
+	usb_endpoint_descriptor_t* int_epd;
 	void* buffer;
 	unsigned int pipe;
 	int length, i, err;
 
 	rep->in = NULL;
-	ep = snd_magic_kcalloc(usbmidi_in_endpoint_t, 0, GFP_KERNEL);
+	ep = snd_magic_kcalloc(snd_usb_midi_in_endpoint_t, 0, GFP_KERNEL);
 	if (!ep)
 		return -ENOMEM;
 	ep->umidi = umidi;
@@ -640,34 +535,28 @@
 	for (i = 0; i < 0x10; ++i)
 		ep->ports[i].seq_port = -1;
 
-	do_int_transfer = snd_int_transfer[umidi->dev];
-	if (do_int_transfer) {
-		if (snd_usbmidi_get_int_ep(umidi, ep_info->epnum, &epd) < 0) {
-			printk(KERN_WARNING "snd-usb-midi: interrupt endpoint not found\n");
-			do_int_transfer = 0;
-		}
-	}
+	int_epd = snd_usbmidi_get_int_epd(umidi, ep_info->epnum);
 
 	ep->urb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!ep->urb) {
 		snd_usbmidi_in_endpoint_delete(ep);
 		return -ENOMEM;
 	}
-	if (do_int_transfer)
-		pipe = usb_rcvintpipe(umidi->usb_device, ep_info->epnum);
+	if (int_epd)
+		pipe = usb_rcvintpipe(umidi->chip->dev, ep_info->epnum);
 	else
-		pipe = usb_rcvbulkpipe(umidi->usb_device, ep_info->epnum);
-	length = usb_maxpacket(umidi->usb_device, pipe, 0);
+		pipe = usb_rcvbulkpipe(umidi->chip->dev, ep_info->epnum);
+	length = usb_maxpacket(umidi->chip->dev, pipe, 0);
 	buffer = kmalloc(length, GFP_KERNEL);
 	if (!buffer) {
 		snd_usbmidi_in_endpoint_delete(ep);
 		return -ENOMEM;
 	}
-	if (do_int_transfer)
-		FILL_INT_URB(ep->urb, umidi->usb_device, pipe, buffer, length,
-			     snd_usbmidi_in_urb_complete, ep, epd->bInterval);
+	if (int_epd)
+		FILL_INT_URB(ep->urb, umidi->chip->dev, pipe, buffer, length,
+			     snd_usbmidi_in_urb_complete, ep, int_epd->bInterval);
 	else
-		FILL_BULK_URB(ep->urb, umidi->usb_device, pipe, buffer, length,
+		FILL_BULK_URB(ep->urb, umidi->chip->dev, pipe, buffer, length,
 			      snd_usbmidi_in_urb_complete, ep);
 
 	for (i = 0; i < 0x10; ++i)
@@ -697,7 +586,7 @@
  * Frees an output endpoint.
  * May be called when ep hasn't been initialized completely.
  */
-static void snd_usbmidi_out_endpoint_delete(usbmidi_out_endpoint_t* ep)
+static void snd_usbmidi_out_endpoint_delete(snd_usb_midi_out_endpoint_t* ep)
 {
 	if (ep->tasklet.func)
 		tasklet_kill(&ep->tasklet);
@@ -715,17 +604,17 @@
  * Creates an output endpoint, and initializes output ports.
  * ALSA ports are created later.
  */
-static int snd_usbmidi_out_endpoint_create(usbmidi_t* umidi,
-					   usbmidi_endpoint_info_t* ep_info,
-			 		   usbmidi_endpoint_t* rep)
+static int snd_usbmidi_out_endpoint_create(snd_usb_midi_t* umidi,
+					   snd_usb_midi_endpoint_info_t* ep_info,
+			 		   snd_usb_midi_endpoint_t* rep)
 {
-	usbmidi_out_endpoint_t* ep;
+	snd_usb_midi_out_endpoint_t* ep;
 	int i;
 	unsigned int pipe;
 	void* buffer;
 
 	rep->out = NULL;
-	ep = snd_magic_kcalloc(usbmidi_out_endpoint_t, 0, GFP_KERNEL);
+	ep = snd_magic_kcalloc(snd_usb_midi_out_endpoint_t, 0, GFP_KERNEL);
 	if (!ep)
 		return -ENOMEM;
 	ep->umidi = umidi;
@@ -735,14 +624,14 @@
 		snd_usbmidi_out_endpoint_delete(ep);
 		return -ENOMEM;
 	}
-	pipe = usb_sndbulkpipe(umidi->usb_device, ep_info->epnum);
-	ep->max_transfer = usb_maxpacket(umidi->usb_device, pipe, 1) & ~3;
+	pipe = usb_sndbulkpipe(umidi->chip->dev, ep_info->epnum);
+	ep->max_transfer = usb_maxpacket(umidi->chip->dev, pipe, 1) & ~3;
 	buffer = kmalloc(ep->max_transfer, GFP_KERNEL);
 	if (!buffer) {
 		snd_usbmidi_out_endpoint_delete(ep);
 		return -ENOMEM;
 	}
-	FILL_BULK_URB(ep->urb, umidi->usb_device, pipe, buffer,
+	FILL_BULK_URB(ep->urb, umidi->chip->dev, pipe, buffer,
 		      ep->max_transfer, snd_usbmidi_out_urb_complete, ep);
 
 	spin_lock_init(&ep->buffer_lock);
@@ -763,17 +652,17 @@
  */
 static int snd_usbmidi_seq_device_delete(snd_seq_device_t* seq_device)
 {
-	usbmidi_t* umidi;
+	snd_usb_midi_t* umidi;
 	int i, j;
 
-	umidi = (usbmidi_t*)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
+	umidi = (snd_usb_midi_t*)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
 
 	if (umidi->seq_client >= 0) {
 		snd_seq_delete_kernel_client(umidi->seq_client);
 		umidi->seq_client = -1;
 	}
-	for (i = 0; i < MAX_ENDPOINTS; ++i) {
-		usbmidi_endpoint_t* ep = &umidi->endpoints[i];
+	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
+		snd_usb_midi_endpoint_t* ep = &umidi->endpoints[i];
 		if (ep->out) {
 			snd_usbmidi_out_endpoint_delete(ep->out);
 			ep->out = NULL;
@@ -784,7 +673,7 @@
 		}
 		for (j = 0; j < 0x10; ++j)
 			if (ep->rmidi[j]) {
-				snd_device_free(umidi->card, ep->rmidi[j]);
+				snd_device_free(umidi->chip->card, ep->rmidi[j]);
 				ep->rmidi[j] = NULL;
 			}
 	}
@@ -796,10 +685,9 @@
  * the ALSA port for each input/output port pair in the endpoint.
  * *port_idx is the port number, which must be unique over all endpoints.
  */
-static int snd_usbmidi_create_endpoint_ports(usbmidi_t* umidi, int ep,
-					     int* port_idx)
+static int snd_usbmidi_create_endpoint_ports(snd_usb_midi_t* umidi, int ep, int* port_idx,
+					     snd_usb_midi_endpoint_info_t* ep_info)
 {
-	usbmidi_endpoint_info_t* ep_info = &umidi->device_info.endpoints[ep];
 	int c, err;
 	int cap, type, port;
 	int out, in;
@@ -830,8 +718,8 @@
 		/* TODO: read type bits from element descriptor */
 		type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
 		/* TODO: read port name from jack descriptor */
-		sprintf(port_name, "%s Port %d",
-			umidi->device_info.product, *port_idx);
+		snprintf(port_name, sizeof(port_name), "%s Port %d",
+			 umidi->chip->card->shortname, *port_idx);
 		port = snd_seq_event_port_attach(umidi->seq_client,
 						 &port_callback,
 						 cap, type, port_name);
@@ -845,7 +733,7 @@
 		if (*port_idx < SNDRV_MINOR_RAWMIDIS) {
 			snd_rawmidi_t *rmidi;
 			snd_virmidi_dev_t *rdev;
-			err = snd_virmidi_new(umidi->card, *port_idx, &rmidi);
+			err = snd_virmidi_new(umidi->chip->card, *port_idx, &rmidi);
 			if (err < 0)
 				return err;
 			rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO);
@@ -853,9 +741,9 @@
 			rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH;
 			rdev->client = umidi->seq_client;
 			rdev->port = port;
-			err = snd_device_register(umidi->card, rmidi);
+			err = snd_device_register(umidi->chip->card, rmidi);
 			if (err < 0) {
-				snd_device_free(umidi->card, rmidi);
+				snd_device_free(umidi->chip->card, rmidi);
 				return err;
 			}
 			umidi->endpoints[ep].rmidi[c] = rmidi;
@@ -866,219 +754,45 @@
 }
 
 /*
- * Create the endpoints and their ports.
+ * Creates the endpoints and their ports.
  */
-static int snd_usbmidi_create_endpoints(usbmidi_t* umidi)
+static int snd_usbmidi_create_endpoints(snd_usb_midi_t* umidi,
+					snd_usb_midi_endpoint_info_t* endpoints)
 {
 	int i, err, port_idx = 0;
 
-	for (i = 0; i < MAX_ENDPOINTS; ++i) {
-		usbmidi_endpoint_info_t* ep_info = &umidi->device_info.endpoints[i];
-
-		if (!ep_info->epnum)
+	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
+		if (!endpoints[i].epnum)
 			continue;
-		if (ep_info->out_cables) {
-			err = snd_usbmidi_out_endpoint_create(umidi, ep_info,
+		if (endpoints[i].out_cables) {
+			err = snd_usbmidi_out_endpoint_create(umidi, &endpoints[i],
 							      &umidi->endpoints[i]);
 			if (err < 0)
 				return err;
 		}
-		if (ep_info->in_cables) {
-			err = snd_usbmidi_in_endpoint_create(umidi, ep_info,
+		if (endpoints[i].in_cables) {
+			err = snd_usbmidi_in_endpoint_create(umidi, &endpoints[i],
 							     &umidi->endpoints[i]);
 			if (err < 0)
 				return err;
 		}
-		err = snd_usbmidi_create_endpoint_ports(umidi, i, &port_idx);
+		err = snd_usbmidi_create_endpoint_ports(umidi, i, &port_idx,
+							&endpoints[i]);
 		if (err < 0)
 			return err;
 		printk(KERN_INFO "snd-usb-midi: endpoint %d: created %d output and %d input ports\n",
-		       ep_info->epnum,
-		       snd_usbmidi_count_bits(ep_info->out_cables),
-		       snd_usbmidi_count_bits(ep_info->in_cables));
+		       endpoints[i].epnum,
+		       snd_usbmidi_count_bits(endpoints[i].out_cables),
+		       snd_usbmidi_count_bits(endpoints[i].in_cables));
 	}
 	return 0;
 }
 
 /*
- * Initialize the sequencer device.
- */
-static int snd_usbmidi_seq_device_new(snd_seq_device_t* seq_device)
-{
-	usbmidi_t* umidi;
-	snd_seq_client_callback_t client_callback;
-	snd_seq_client_info_t client_info;
-	int i, err;
-
-	umidi = (usbmidi_t*)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
-
-	memset(&client_callback, 0, sizeof(client_callback));
-	client_callback.allow_output = 1;
-	client_callback.allow_input = 1;
-	umidi->seq_client = snd_seq_create_kernel_client(umidi->card, 0,
-							 &client_callback);
-	if (umidi->seq_client < 0)
-		return umidi->seq_client;
-
-	memset(&client_info, 0, sizeof(client_info));
-	client_info.client = umidi->seq_client;
-	client_info.type = KERNEL_CLIENT;
-	sprintf(client_info.name, "%s %s",
-		umidi->device_info.vendor, umidi->device_info.product);
-	snd_seq_kernel_client_ctl(umidi->seq_client,
-				  SNDRV_SEQ_IOCTL_SET_CLIENT_INFO,
-				  &client_info);
-
-	err = snd_usbmidi_create_endpoints(umidi);
-	if (err < 0) {
-		snd_usbmidi_seq_device_delete(seq_device);
-		return err;
-	}
-
-	for (i = 0; i < MAX_ENDPOINTS; ++i)
-		if (umidi->endpoints[i].in)
-			snd_usbmidi_submit_urb(umidi->endpoints[i].in->urb,
-					       GFP_KERNEL);
-	return 0;
-}
-
-static int snd_usbmidi_card_create(usb_device_t* usb_device,
-				   usbmidi_device_info_t* device_info,
-				   snd_card_t** rcard)
-{
-	snd_card_t* card;
-	snd_seq_device_t* seq_device;
-	usbmidi_t* umidi;
-	int dev, err;
-
-	if (rcard)
-		*rcard = NULL;
-
-	down(&snd_usbmidi_open_mutex);
-
-	for (dev = 0; dev < SNDRV_CARDS; ++dev) {
-		if (snd_enable[dev] && !snd_usbmidi_card_used[dev] &&
-		    (snd_vid[dev] == -1 || 
-		     snd_vid[dev] == usb_device->descriptor.idVendor) &&
-		    (snd_pid[dev] == -1 ||
-		     snd_pid[dev] == usb_device->descriptor.idProduct))
-			break;
-	}
-	if (dev >= SNDRV_CARDS) {
-		up(&snd_usbmidi_open_mutex);
-		return -ENOENT;
-	}
-
-	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
-	if (!card) {
-		up(&snd_usbmidi_open_mutex);
-		return -ENOMEM;
-	}
-	strcpy(card->driver, "USB MIDI");
-	snprintf(card->shortname, sizeof(card->shortname), "%s %s",
-		 device_info->vendor, device_info->product);
-	snprintf(card->longname, sizeof(card->longname), "%s %s at %03d/%03d if %d",
-		 device_info->vendor, device_info->product,
-		 usb_device->bus->busnum, usb_device->devnum,
-		 device_info->ifnum);
-	card->private_data = (void*)dev;
-
-	err = snd_seq_device_new(card, 0, SNDRV_SEQ_DEV_ID_USBMIDI,
-				 sizeof(usbmidi_t), &seq_device);
-	if (err < 0) {
-		snd_card_free(card);
-		up(&snd_usbmidi_open_mutex);
-		return err;
-	}
-	strcpy(seq_device->name, card->shortname);
-	umidi = (usbmidi_t*)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
-	umidi->card = card;
-	umidi->usb_device = usb_device;
-	umidi->dev = dev;
-	umidi->seq_client = -1;
-	umidi->device_info = *device_info;
-
-	err = snd_card_register(card);
-	if (err < 0) {
-		snd_card_free(card);
-		up(&snd_usbmidi_open_mutex);
-		return err;
-	}
-	snd_usbmidi_card_used[dev] = 1;
-	up(&snd_usbmidi_open_mutex);
-	if (rcard)
-		*rcard = card;
-	return 0;
-}
-
-/*
- * If the first endpoint isn't specified, use the first endpoint in the
- * first alternate setting of the interface.
- */
-static int snd_usbmidi_detect_endpoint(usb_device_t* usb_device, 
-			       	       usbmidi_device_info_t* device_info)
-{
-	usb_interface_t* intf;
-	usb_interface_descriptor_t* intfd;
-	usb_endpoint_descriptor_t* epd;
-
-	if (device_info->endpoints[0].epnum == -1) {
-		intf = usb_ifnum_to_if(usb_device, device_info->ifnum);
-		if (!intf || intf->num_altsetting < 1)
-			return -ENOENT;
-		intfd = intf->altsetting;
-		if (intfd->bNumEndpoints < 1)
-			return -ENOENT;
-		epd = intfd->endpoint;
-		device_info->endpoints[0].epnum = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
-	}
-	return 0;
-}
-
-/*
- * Searches for the alternate setting with the greatest number of bulk transfer
- * endpoints.
- */
-static usb_interface_descriptor_t* snd_usbmidi_get_altsetting(usb_device_t* usb_device,
-							      usb_interface_t* intf)
-{
-	int i, best = -1;
-	int best_out = 0, best_in = 0;
-	usb_interface_descriptor_t* intfd;
-
-	if (intf->num_altsetting == 1)
-		return &intf->altsetting[0];
-	for (i = 0; i < intf->num_altsetting; ++i) {
-		int out = 0, in = 0, j;
-		for (j = 0; j < intf->altsetting[i].bNumEndpoints; ++j) {
-			usb_endpoint_descriptor_t* ep = &intf->altsetting[i].endpoint[j];
-			if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
-				if (ep->bEndpointAddress & USB_DIR_IN)
-					++in;
-				else
-					++out;
-			}
-		}
-		if ((out >= best_out && in >= best_in) &&
-		    (out > best_out || in > best_in)) {
-			best_out = out;
-			best_in = in;
-			best = i;
-		}
-	}
-	if (best < 0)
-		return NULL;
-	intfd = &intf->altsetting[best];
-	usb_set_interface(usb_device, intfd->bInterfaceNumber, intfd->bAlternateSetting);
-	return intfd;
-}
-
-/*
- * Returns MIDIStreaming device capabilities in device_info.
+ * Returns MIDIStreaming device capabilities.
  */
-static int snd_usbmidi_get_ms_info(usb_device_t* usb_device,
-				   unsigned int ifnum,
-				   usbmidi_device_info_t* device_info)
+static int snd_usbmidi_get_ms_info(snd_usb_midi_t* umidi,
+			   	   snd_usb_midi_endpoint_info_t* endpoints)
 {
 	usb_interface_t* intf;
 	usb_interface_descriptor_t* intfd;
@@ -1087,34 +801,17 @@
 	usb_ms_endpoint_descriptor_t* ms_ep;
 	int i, epidx;
 
-	memset(device_info, 0, sizeof(*device_info));
+	memset(endpoints, 0, sizeof(*endpoints) * MIDI_MAX_ENDPOINTS);
 
-	if (usb_device->descriptor.iManufacturer == 0 ||
-	    usb_string(usb_device, usb_device->descriptor.iManufacturer,
-		       device_info->vendor, sizeof(device_info->vendor)) < 0)
-		sprintf(device_info->vendor, "Unknown Vendor %x", usb_device->descriptor.idVendor);
-	if (usb_device->descriptor.iProduct == 0 ||
-	    usb_string(usb_device, usb_device->descriptor.iProduct,
-		       device_info->product, sizeof(device_info->product)) < 0)
-		sprintf(device_info->product, "Unknown Device %x", usb_device->descriptor.idProduct);
-
-	intf = usb_ifnum_to_if(usb_device, ifnum);
+	intf = usb_ifnum_to_if(umidi->chip->dev, umidi->ifnum);
 	if (!intf)
 		return -ENXIO;
-	device_info->ifnum = ifnum;
-	printk(KERN_INFO "snd-usb-midi: using interface %d\n",
-	       intf->altsetting[0].bInterfaceNumber);
-
-	intfd = snd_usbmidi_get_altsetting(usb_device, intf);
-	if (!intfd) {
-		printk(KERN_ERR "snd-usb-midi: could not determine altsetting\n");
-		return -ENXIO;
-	}
+	intfd = &intf->altsetting[0];
 	ms_header = (usb_ms_header_descriptor_t*)intfd->extra;
-	if (intfd->extralen >= USB_DST_MS_HEADER_SIZE &&
-	    ms_header->bLength >= USB_DST_MS_HEADER_SIZE &&
+	if (intfd->extralen >= 7 &&
+	    ms_header->bLength >= 7 &&
 	    ms_header->bDescriptorType == USB_DT_CS_INTERFACE &&
-	    ms_header->bDescriptorSubtype == USB_DST_MS_HEADER)
+	    ms_header->bDescriptorSubtype == HEADER)
 		printk(KERN_INFO "snd-usb-midi: MIDIStreaming version %02x.%02x\n",
 		       ms_header->bcdMSC[1], ms_header->bcdMSC[0]);
 	else
@@ -1126,242 +823,124 @@
 		if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK)
 			continue;
 		ms_ep = (usb_ms_endpoint_descriptor_t*)ep->extra;
-		if (ep->extralen < USB_DST_MS_GENERAL_SIZE ||
-		    ms_ep->bLength < USB_DST_MS_GENERAL_SIZE ||
+		if (ep->extralen < 4 ||
+		    ms_ep->bLength < 4 ||
 		    ms_ep->bDescriptorType != USB_DT_CS_ENDPOINT ||
-		    ms_ep->bDescriptorSubtype != USB_DST_MS_GENERAL)
+		    ms_ep->bDescriptorSubtype != MS_GENERAL)
 			continue;
-		if (device_info->endpoints[epidx].epnum != 0 &&
-		    device_info->endpoints[epidx].epnum != (ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)) {
+		if (endpoints[epidx].epnum != 0 &&
+		    endpoints[epidx].epnum != (ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)) {
 			++epidx;
-			if (epidx >= MAX_ENDPOINTS) {
+			if (epidx >= MIDI_MAX_ENDPOINTS) {
 				printk(KERN_WARNING "snd-usb-midi: too many endpoints\n");
 				break;
 			}
 		}
-		device_info->endpoints[epidx].epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+		endpoints[epidx].epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
 		if (ep->bEndpointAddress & USB_DIR_IN) {
-			device_info->endpoints[epidx].in_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1;
+			endpoints[epidx].in_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1;
 		} else {
-			device_info->endpoints[epidx].out_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1;
+			endpoints[epidx].out_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1;
 		}
 		printk(KERN_INFO "snd-usb-midi: detected %d %s jack(s) on endpoint %d\n",
 		       ms_ep->bNumEmbMIDIJack,
 		       ep->bEndpointAddress & USB_DIR_IN ? "input" : "output",
-		       device_info->endpoints[epidx].epnum);
-	}
-	return 0;
-}
-
-/*
- * Returns device capabilities, either explicitly supplied or from the
- * class-specific descriptors.
- */
-static int snd_usbmidi_get_device_info(usb_device_t* usb_device,
-				       unsigned int ifnum,
-				       const usb_device_id_t* usb_device_id,
-				       usbmidi_device_info_t* device_info)
-{
-	if (usb_device_id->driver_info) {
-		usbmidi_device_info_t* id_info = (usbmidi_device_info_t*)usb_device_id->driver_info;
-		if (ifnum != id_info->ifnum)
-			return -ENXIO;
-		*device_info = *id_info;
-		if (snd_usbmidi_detect_endpoint(usb_device, device_info) < 0)
-			return -ENXIO;
-	} else {
-		if (snd_usbmidi_get_ms_info(usb_device, ifnum, device_info) < 0)
-			return -ENXIO;
+		       endpoints[epidx].epnum);
 	}
 	return 0;
 }
 
 /*
- * Probes for a supported device.
+ * If the first endpoint isn't specified, use the first endpoint in the
+ * first alternate setting of the interface.
  */
-static void* snd_usbmidi_usb_probe(usb_device_t* device,
-				   unsigned int ifnum,
-				   const usb_device_id_t* device_id)
+static int snd_usbmidi_detect_endpoint(snd_usb_midi_t* umidi, 
+			       	       snd_usb_midi_endpoint_info_t* endpoint)
 {
-	usbmidi_device_info_t device_info;
-	snd_card_t* card = NULL;
-	int err;
+	usb_interface_t* intf;
+	usb_interface_descriptor_t* intfd;
+	usb_endpoint_descriptor_t* epd;
 
-	if (snd_usbmidi_get_device_info(device, ifnum, device_id,
-					&device_info) == 0) {
-		printk(KERN_INFO "snd-usb-midi: detected %s %s\n",
-		       device_info.vendor, device_info.product);
-		err = snd_usbmidi_card_create(device, &device_info, &card);
-		if (err < 0)
-			snd_printk(KERN_ERR "cannot create card (error code %d)\n", err);
+	if (endpoint->epnum == -1) {
+		intf = usb_ifnum_to_if(umidi->chip->dev, umidi->ifnum);
+		if (!intf || intf->num_altsetting < 1)
+			return -ENOENT;
+		intfd = intf->altsetting;
+		if (intfd->bNumEndpoints < 1)
+			return -ENOENT;
+		epd = intfd->endpoint;
+		endpoint->epnum = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
 	}
-	return card;
+	return 0;
 }
 
 /*
- * Frees the device.
+ * Initialize the sequencer device.
  */
-static void snd_usbmidi_usb_disconnect(usb_device_t* usb_device, void* ptr)
+static int snd_usbmidi_seq_device_new(snd_seq_device_t* seq_device)
 {
-	snd_card_t* card = (snd_card_t*)ptr;
-	int dev = (int)card->private_data;
+	snd_usb_midi_t* umidi;
+	usb_device_t* dev;
+	snd_seq_client_callback_t client_callback;
+	snd_seq_client_info_t client_info;
+	snd_usb_midi_endpoint_info_t endpoints[MIDI_MAX_ENDPOINTS];
+	int i, err;
 
-	snd_card_free(card);
-	down(&snd_usbmidi_open_mutex);
-	snd_usbmidi_card_used[dev] = 0;
-	up(&snd_usbmidi_open_mutex);
-}
+	umidi = (snd_usb_midi_t*)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
 
-/*
- * Information about devices with broken descriptors.
- */
-
-static usbmidi_device_info_t snd_usbmidi_yamaha_ux256_info = {
-	/* from NetBSD's umidi driver */
-	.vendor = "Yamaha", .product = "UX256",
-	.ifnum = 0,
-	.endpoints = {{ -1, 0xffff, 0x00ff }}
-};
-static usbmidi_device_info_t snd_usbmidi_yamaha_mu1000_info = {
-	/* from Nagano Daisuke's usb-midi driver */
-	.vendor = "Yamaha", .product = "MU1000",
-	.ifnum = 0,
-	.endpoints = {{ 1, 0x000f, 0x0001 }}
-};
-/*
- * There ain't no such thing as a standard-compliant Roland device.
- * Apparently, Roland decided not to risk to have wrong entries in the USB
- * descriptors. The consequence is that class-specific descriptors are
- * conspicuous by their absence.
- *
- * And now you may guess which company was responsible for writing the
- * USB Device Class Definition for MIDI Devices.
- */
-static usbmidi_device_info_t snd_usbmidi_roland_ua100_info = {
-	.vendor = "Roland", .product = "UA-100",
-	.ifnum = 2,
-	.endpoints = {{ -1, 0x0007, 0x0007 }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_um4_info = {
-	.vendor = "EDIROL", .product = "UM-4",
-	.ifnum = 2,
-	.endpoints = {{ -1, 0x000f, 0x000f }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_sc8850_info = {
-	.vendor = "Roland", .product = "SC-8850",
-	.ifnum = 2,
-	.endpoints = {{ -1, 0x003f, 0x003f }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_u8_info = {
-	.vendor = "Roland", .product = "U-8",
-	.ifnum = 2,
-	.endpoints = {{ -1, 0x0003, 0x0003 }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_um2_info = {
-	.vendor = "EDIROL", .product = "UM-2",
-	.ifnum = 2,
-	.endpoints = {{ -1, 0x0003, 0x0003 }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_sc8820_info = {
-	.vendor = "Roland", .product = "SC-8820",
-	.ifnum = 2,
-	.endpoints = {{ -1, 0x0013, 0x0013 }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_pc300_info = {
-	.vendor = "Roland", .product = "PC-300",
-	.ifnum = 2,
-	.endpoints = {{ -1, 0x0001, 0x0001 }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_um1_info = {
-	.vendor = "EDIROL", .product = "UM-1",
-	.ifnum = 2,
-	.endpoints = {{ -1, 0x0001, 0x0001 }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_sk500_info = {
-	.vendor = "Roland", .product = "SK-500",
-	.ifnum = 2,
-	.endpoints = {{ -1, 0x0013, 0x0013 }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_scd70_info = {
-	.vendor = "Roland", .product = "SC-D70",
-	.ifnum = 2,
-	.endpoints = {{ -1, 0x0007, 0x0007 }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_xv5050_info = {
-	.vendor = "Roland", .product = "XV-5050",
-	.ifnum = 0,
-	.endpoints = {{ -1, 0x0001, 0x0001 }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_um880_info = {
-	.vendor = "EDIROL", .product = "UM-880",
-	.ifnum = 0,
-	.endpoints = {{ -1, 0x01ff, 0x01ff }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_sd90_info = {
-	.vendor = "EDIROL", .product = "SD-90",
-	.ifnum = 2,
-	.endpoints = {{ -1, 0x000f, 0x000f }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_um550_info = {
-	.vendor = "EDIROL", .product = "UM-550",
-	.ifnum = 0,
-	.endpoints = {{ -1, 0x003f, 0x003f }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_sd20_info = {
-	.vendor = "EDIROL", .product = "SD-20",
-	.ifnum = 0,
-	.endpoints = {{ -1, 0x0003, 0x0007 }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_sd80_info = {
-	.vendor = "EDIROL", .product = "SD-80",
-	.ifnum = 0,
-	.endpoints = {{ -1, 0x000f, 0x000f }}
-};
-static usbmidi_device_info_t snd_usbmidi_roland_ua700_info = {
-	.vendor = "EDIROL", .product = "UA-700",
-	.ifnum = 3,
-	.endpoints = {{ -1, 0x0003, 0x0003 }}
-};
+	memset(&client_callback, 0, sizeof(client_callback));
+	client_callback.allow_output = 1;
+	client_callback.allow_input = 1;
+	umidi->seq_client = snd_seq_create_kernel_client(umidi->chip->card, 0,
+							 &client_callback);
+	if (umidi->seq_client < 0)
+		return umidi->seq_client;
 
-#define USBMIDI_NONCOMPLIANT_DEVICE(vid, pid, name) \
-		USB_DEVICE(vid, pid), \
-		driver_info: (unsigned long)&snd_usbmidi_##name##_info
-static usb_device_id_t snd_usbmidi_usb_id_table[] = {
-	{ match_flags: USB_DEVICE_ID_MATCH_INT_CLASS |
-		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
-	  bInterfaceClass: USB_CLASS_AUDIO,
-	  bInterfaceSubClass: USB_SUBCLASS_MIDISTREAMING },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0499, 0x1000, yamaha_ux256) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0499, 0x1001, yamaha_mu1000) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0000, roland_ua100) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0002, roland_um4) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0003, roland_sc8850) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0004, roland_u8) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0005, roland_um2) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0007, roland_sc8820) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0008, roland_pc300) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0009, roland_um1) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x000b, roland_sk500) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x000c, roland_scd70) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0012, roland_xv5050) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0014, roland_um880) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0016, roland_sd90) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0023, roland_um550) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0027, roland_sd20) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0029, roland_sd80) },
-	{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x002b, roland_ua700) },
-	{ /* terminator */ }
-};
+	memset(&client_info, 0, sizeof(client_info));
+	client_info.client = umidi->seq_client;
+	client_info.type = KERNEL_CLIENT;
+	dev = umidi->chip->dev;
+	if (dev->descriptor.iProduct)
+		err = usb_string(dev, dev->descriptor.iProduct,
+				 client_info.name, sizeof(client_info.name));
+	else
+		err = 0;
+	if (err <= 0) {
+		if (umidi->quirk && umidi->quirk->product_name) {
+			strncpy(client_info.name, umidi->quirk->product_name,
+				sizeof(client_info.name) - 1);
+			client_info.name[sizeof(client_info.name) - 1] = '\0';
+		} else {
+			sprintf(client_info.name, "USB Device %#04x:%#04x",
+				dev->descriptor.idVendor, dev->descriptor.idProduct);
+		}
+	}
+	snd_seq_kernel_client_ctl(umidi->seq_client,
+				  SNDRV_SEQ_IOCTL_SET_CLIENT_INFO,
+				  &client_info);
 
-MODULE_DEVICE_TABLE(usb, snd_usbmidi_usb_id_table);
+	if (umidi->quirk) {
+		memcpy(endpoints, umidi->quirk->endpoints, sizeof(endpoints));
+		err = snd_usbmidi_detect_endpoint(umidi, &endpoints[0]);
+	} else {
+		err = snd_usbmidi_get_ms_info(umidi, endpoints);
+	}
+	if (err < 0) {
+		snd_usbmidi_seq_device_delete(seq_device);
+		return err;
+	}
+	err = snd_usbmidi_create_endpoints(umidi, endpoints);
+	if (err < 0) {
+		snd_usbmidi_seq_device_delete(seq_device);
+		return err;
+	}
 
-static usb_driver_t snd_usbmidi_usb_driver = {
-	.name = "snd-usb-midi",
-	.probe = snd_usbmidi_usb_probe,
-	.disconnect = snd_usbmidi_usb_disconnect,
-	.id_table = snd_usbmidi_usb_id_table,
-	.driver_list = LIST_HEAD_INIT(snd_usbmidi_usb_driver.driver_list)
-};
+	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
+		if (umidi->endpoints[i].in)
+			snd_usbmidi_submit_urb(umidi->endpoints[i].in->urb,
+					       GFP_KERNEL);
+	return 0;
+}
 
 static int __init snd_usbmidi_module_init(void)
 {
@@ -1369,51 +948,15 @@
 		snd_usbmidi_seq_device_new,
 		snd_usbmidi_seq_device_delete
 	};
-	int err;
 
-	err = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_USBMIDI, &ops,
-					     sizeof(usbmidi_t));
-	if (err < 0)
-		return err;
-	err = usb_register(&snd_usbmidi_usb_driver);
-	if (err < 0) {
-		snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_USBMIDI);
-		return err;
-	}
-	return 0;
+	return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_USBMIDI, &ops,
+	 				      sizeof(snd_usb_midi_t));
 }
 
 static void __exit snd_usbmidi_module_exit(void)
 {
-	usb_deregister(&snd_usbmidi_usb_driver);
 	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_USBMIDI);
 }
 
 module_init(snd_usbmidi_module_init)
 module_exit(snd_usbmidi_module_exit)
-
-#ifndef MODULE
-
-/*
- * format is snd-usb-midi=snd_enable,snd_index,snd_id,
- *                        snd_vid,snd_pid,snd_int_transfer
- */
-static int __init snd_usbmidi_module_setup(char* str)
-{
-	static unsigned __initdata nr_dev = 0;
-
-	if (nr_dev >= SNDRV_CARDS)
-		return 0;
-	(void)(get_option(&str, &snd_enable[nr_dev]) == 2 &&
-	       get_option(&str, &snd_index[nr_dev]) == 2 &&
-	       get_id(&str, &snd_id[nr_dev]) == 2 &&
-	       get_option(&str, &snd_vid[nr_dev]) == 2 &&
-	       get_option(&str, &snd_pid[nr_dev]) == 2 &&
-	       get_option(&str, &snd_int_transfer[nr_dev]) == 2);
-	++nr_dev;
-	return 1;
-}
-
-__setup("snd-usb-midi=", snd_usbmidi_module_setup);
-
-#endif /* !MODULE */
diff -Nru a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/usb/usbquirks.h	Tue Oct  1 17:08:04 2002
@@ -0,0 +1,337 @@
+/*
+ * ALSA USB Audio Driver
+ *
+ * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>,
+ *                       Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+/*
+ * The contents of this file are part of the driver's id_table.
+ *
+ * In a perfect world, this file would be empty.
+ */
+
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+
+{
+	/* from NetBSD's umidi driver */
+	USB_DEVICE(0x0499, 0x1000), /* Yamaha UX256 */
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.ifnum = 0,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0xffff,
+				.in_cables  = 0x00ff
+			}
+		}
+	}
+},
+{
+	/* from Nagano Daisuke's usb-midi driver */
+	USB_DEVICE(0x0499, 0x1001), /* Yamaha MU1000 */
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.ifnum = 0,
+		.endpoints = {
+			{
+				.epnum = 1,
+				.out_cables = 0x000f,
+				.in_cables  = 0x0001
+			}
+		}
+	}
+},
+/*
+ * I don't know whether the following Yamaha devices need entries or not:
+ * 0x1002 MU2000   0x1008 UX96
+ * 0x1003 MU500    0x1009 UX16
+ * 0x1004 UW500    0x100e S08
+ * 0x1005 MOTIF6   0x100f CLP-150
+ * 0x1006 MOTIF7   0x1010 CLP-170
+ * 0x1007 MOTIF8
+ */
+
+/*
+ * Once upon a time people thought, "Wouldn't it be nice if there was a
+ * standard for USB MIDI devices, so that device drivers would not be forced
+ * to know about the quirks of specific devices?"  So Roland went ahead and
+ * wrote the USB Device Class Definition for MIDI Devices, and the USB-IF
+ * endorsed it, and now everybody designing USB MIDI devices does so in
+ * agreement with this standard (or at least tries to).
+ *
+ * And if you prefer a happy end, you can imagine that Roland devices set a
+ * good example. Instead of being completely fucked up due to the lack of
+ * class-specific descriptors.
+ */
+{
+	USB_DEVICE(0x0582, 0x0000),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "UA-100",
+		.ifnum = 2,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x0007,
+				.in_cables  = 0x0007
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x0002),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "UM-4",
+		.ifnum = 2,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x000f,
+				.in_cables  = 0x000f
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x0003),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "SC-8850",
+		.ifnum = 2,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x003f,
+				.in_cables  = 0x003f
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x0004),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "U-8",
+		.ifnum = 2,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x0003,
+				.in_cables  = 0x0003
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x0005),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "UM-2",
+		.ifnum = 2,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x0003,
+				.in_cables  = 0x0003
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x0007),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "SC-8820",
+		.ifnum = 2,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x0013,
+				.in_cables  = 0x0013
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x0008),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "PC-300",
+		.ifnum = 2,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x0001,
+				.in_cables  = 0x0001
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x0009),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "UM-1",
+		.ifnum = 2,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x0001,
+				.in_cables  = 0x0001
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x000b),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "SK-500",
+		.ifnum = 2,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x0013,
+				.in_cables  = 0x0013
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x000c),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "SC-D70",
+		.ifnum = 2,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x0007,
+				.in_cables  = 0x0007
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x0012),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "XV-5050",
+		.ifnum = 0,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x0001,
+				.in_cables  = 0x0001
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x0014),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "UM-880",
+		.ifnum = 0,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x01ff,
+				.in_cables  = 0x01ff
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x0016),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "SD-90",
+		.ifnum = 2,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x000f,
+				.in_cables  = 0x000f
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x0023),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "UM-550",
+		.ifnum = 0,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x003f,
+				.in_cables  = 0x003f
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x0027),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "SD-20",
+		.ifnum = 0,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x0003,
+				.in_cables  = 0x0007
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x0029),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "SD-80",
+		.ifnum = 0,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x000f,
+				.in_cables  = 0x000f
+			}
+		}
+	}
+},
+{
+	USB_DEVICE(0x0582, 0x002b),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "UA-700",
+		.ifnum = 3,
+		.endpoints = {
+			{
+				.epnum = -1,
+				.out_cables = 0x0003,
+				.in_cables  = 0x0003
+			}
+		}
+	}
+},
+
+#endif /* CONFIG_SND_SEQUENCER(_MODULE) */

===================================================================


This BitKeeper patch contains the following changesets:
1.650
## Wrapped with gzip_uu ##


begin 664 bkpatch23005
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
`
end
