merge 2.6.13-rc4 with ACPI's to-linus tree
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index 34c1740..2e4a5e0 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -20,5 +20,17 @@
 	select SND_PCM
 	select SND_AC97_CODEC
 
-endmenu
+config SND_PXA2XX_PCM
+	tristate
+	select SND_PCM
 
+config SND_PXA2XX_AC97
+	tristate "AC97 driver for the Intel PXA2xx chip"
+	depends on ARCH_PXA && SND
+	select SND_PXA2XX_PCM
+	select SND_AC97_CODEC
+	help
+	  Say Y or M if you want to support any AC97 codec attached to
+	  the PXA2xx AC97 interface.
+
+endmenu
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index f74ec28..103f136 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -3,9 +3,11 @@
 #
 
 snd-sa11xx-uda1341-objs := sa11xx-uda1341.o
-
-# Toplevel Module Dependency
-obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-sa11xx-uda1341.o 
-
-obj-$(CONFIG_SND_ARMAACI)	+= snd-aaci.o
 snd-aaci-objs			:= aaci.o devdma.o
+snd-pxa2xx-pcm-objs := pxa2xx-pcm.o
+snd-pxa2xx-ac97-objs := pxa2xx-ac97.o
+
+obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-sa11xx-uda1341.o 
+obj-$(CONFIG_SND_ARMAACI)	+= snd-aaci.o
+obj-$(CONFIG_SND_PXA2XX_PCM) += snd-pxa2xx-pcm.o
+obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
new file mode 100644
index 0000000..46052304
--- /dev/null
+++ b/sound/arm/pxa2xx-ac97.c
@@ -0,0 +1,410 @@
+/*
+ * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip.
+ *
+ * Author:	Nicolas Pitre
+ * Created:	Dec 02, 2004
+ * Copyright:	MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+
+#include <asm/irq.h>
+#include <asm/semaphore.h>
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/audio.h>
+
+#include "pxa2xx-pcm.h"
+
+
+static DECLARE_MUTEX(car_mutex);
+static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
+static volatile long gsr_bits;
+
+static unsigned short pxa2xx_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+	unsigned short val = -1;
+	volatile u32 *reg_addr;
+
+	down(&car_mutex);
+	if (CAR & CAR_CAIP) {
+		printk(KERN_CRIT"%s: CAR_CAIP already set\n", __FUNCTION__);
+		goto out;
+	}
+
+	/* set up primary or secondary codec space */
+	reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
+	reg_addr += (reg >> 1);
+
+	/* start read access across the ac97 link */
+	gsr_bits = 0;
+	val = *reg_addr;
+	if (reg == AC97_GPIO_STATUS)
+		goto out;
+	wait_event_timeout(gsr_wq, gsr_bits & GSR_SDONE, 1);
+	if (!gsr_bits & GSR_SDONE) {
+		printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
+				__FUNCTION__, reg, gsr_bits);
+		val = -1;
+		goto out;
+	}
+
+	/* valid data now */
+	gsr_bits = 0;
+	val = *reg_addr;			
+	/* but we've just started another cycle... */
+	wait_event_timeout(gsr_wq, gsr_bits & GSR_SDONE, 1);
+
+out:	up(&car_mutex);
+	return val;
+}
+
+static void pxa2xx_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+	volatile u32 *reg_addr;
+
+	down(&car_mutex);
+
+	if (CAR & CAR_CAIP) {
+		printk(KERN_CRIT "%s: CAR_CAIP already set\n", __FUNCTION__);
+		goto out;
+	}
+
+	/* set up primary or secondary codec space */
+	reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
+	reg_addr += (reg >> 1);
+	gsr_bits = 0;
+	*reg_addr = val;
+	wait_event_timeout(gsr_wq, gsr_bits & GSR_CDONE, 1);
+	if (!gsr_bits & GSR_SDONE)
+		printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
+				__FUNCTION__, reg, gsr_bits);
+
+out:	up(&car_mutex);
+}
+
+static void pxa2xx_ac97_reset(ac97_t *ac97)
+{
+	/* First, try cold reset */
+	GCR &=  GCR_COLD_RST;  /* clear everything but nCRST */
+	GCR &= ~GCR_COLD_RST;  /* then assert nCRST */
+
+	gsr_bits = 0;
+#ifdef CONFIG_PXA27x
+	/* PXA27x Developers Manual section 13.5.2.2.1 */
+	pxa_set_cken(1 << 31, 1);
+	udelay(5);
+	pxa_set_cken(1 << 31, 0);
+	GCR = GCR_COLD_RST;
+	udelay(50);
+#else
+	GCR = GCR_COLD_RST;
+	GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
+	wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
+#endif
+
+	if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) {
+		printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
+				 __FUNCTION__, gsr_bits);
+
+		/* let's try warm reset */
+		gsr_bits = 0;
+#ifdef CONFIG_PXA27x
+		/* warm reset broken on Bulverde,
+		   so manually keep AC97 reset high */
+		pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH); 
+		udelay(10);
+		GCR |= GCR_WARM_RST;
+		pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+		udelay(50);
+#else
+		GCR |= GCR_WARM_RST|GCR_PRIRDY_IEN|GCR_SECRDY_IEN;;
+		wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
+#endif			
+
+		if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
+			printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
+					 __FUNCTION__, gsr_bits);
+	}
+
+	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+	GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
+}
+
+static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	long status;
+
+	status = GSR;
+	if (status) {
+		GSR = status;
+		gsr_bits |= status;
+		wake_up(&gsr_wq);
+
+#ifdef CONFIG_PXA27x
+		/* Although we don't use those we still need to clear them
+		   since they tend to spuriously trigger when MMC is used
+		   (hardware bug? go figure)... */
+		MISR = MISR_EOC;
+		PISR = PISR_EOC;
+		MCSR = MCSR_EOC;
+#endif
+
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static ac97_bus_ops_t pxa2xx_ac97_ops = {
+	.read	= pxa2xx_ac97_read,
+	.write	= pxa2xx_ac97_write,
+	.reset	= pxa2xx_ac97_reset,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ac97_pcm_out = {
+	.name			= "AC97 PCM out",
+	.dev_addr		= __PREG(PCDR),
+	.drcmr			= &DRCMRTXPCDR,
+	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
+				  DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ac97_pcm_in = {
+	.name			= "AC97 PCM in",
+	.dev_addr		= __PREG(PCDR),
+	.drcmr			= &DRCMRRXPCDR,
+	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
+				  DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static snd_pcm_t *pxa2xx_ac97_pcm;
+static ac97_t *pxa2xx_ac97_ac97;
+
+static int pxa2xx_ac97_pcm_startup(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	pxa2xx_audio_ops_t *platform_ops;
+	int r;
+
+	runtime->hw.channels_min = 2;
+	runtime->hw.channels_max = 2;
+
+	r = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+	    AC97_RATES_FRONT_DAC : AC97_RATES_ADC;
+	runtime->hw.rates = pxa2xx_ac97_ac97->rates[r];
+	snd_pcm_limit_hw_rates(runtime);
+
+       	platform_ops = substream->pcm->card->dev->platform_data;
+	if (platform_ops && platform_ops->startup)
+		return platform_ops->startup(substream, platform_ops->priv);
+	else
+		return 0;
+}
+
+static void pxa2xx_ac97_pcm_shutdown(snd_pcm_substream_t *substream)
+{
+	pxa2xx_audio_ops_t *platform_ops;
+
+       	platform_ops = substream->pcm->card->dev->platform_data;
+	if (platform_ops && platform_ops->shutdown)
+		platform_ops->shutdown(substream, platform_ops->priv);
+}
+
+static int pxa2xx_ac97_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		  AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
+	return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate);
+}
+
+static pxa2xx_pcm_client_t pxa2xx_ac97_pcm_client = {
+	.playback_params	= &pxa2xx_ac97_pcm_out,
+	.capture_params		= &pxa2xx_ac97_pcm_in,
+	.startup		= pxa2xx_ac97_pcm_startup,
+	.shutdown		= pxa2xx_ac97_pcm_shutdown,
+	.prepare		= pxa2xx_ac97_pcm_prepare,
+};
+
+#ifdef CONFIG_PM
+
+static int pxa2xx_ac97_do_suspend(snd_card_t *card, unsigned int state)
+{
+	if (card->power_state != SNDRV_CTL_POWER_D3cold) {
+		pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
+		snd_pcm_suspend_all(pxa2xx_ac97_pcm);
+		snd_ac97_suspend(pxa2xx_ac97_ac97);
+		snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
+		if (platform_ops && platform_ops->suspend)
+			platform_ops->suspend(platform_ops->priv);
+		GCR |= GCR_ACLINK_OFF;
+		pxa_set_cken(CKEN2_AC97, 0);
+	}
+
+	return 0;
+}
+
+static int pxa2xx_ac97_do_resume(snd_card_t *card, unsigned int state)
+{
+	if (card->power_state != SNDRV_CTL_POWER_D0) {
+		pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
+		pxa_set_cken(CKEN2_AC97, 1);
+		if (platform_ops && platform_ops->resume)
+			platform_ops->resume(platform_ops->priv);
+		snd_ac97_resume(pxa2xx_ac97_ac97);
+		snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	}
+
+	return 0;
+}
+
+static int pxa2xx_ac97_suspend(struct device *_dev, u32 state, u32 level)
+{
+	snd_card_t *card = dev_get_drvdata(_dev);
+	int ret = 0;
+
+	if (card && level == SUSPEND_DISABLE)
+		ret = pxa2xx_ac97_do_suspend(card, SNDRV_CTL_POWER_D3cold);
+
+	return ret;
+}
+
+static int pxa2xx_ac97_resume(struct device *_dev, u32 level)
+{
+	snd_card_t *card = dev_get_drvdata(_dev);
+	int ret = 0;
+
+	if (card && level == RESUME_ENABLE)
+		ret = pxa2xx_ac97_do_resume(card, SNDRV_CTL_POWER_D0);
+
+	return ret;
+}
+
+#else
+#define pxa2xx_ac97_suspend	NULL
+#define pxa2xx_ac97_resume	NULL
+#endif
+
+static int pxa2xx_ac97_probe(struct device *dev)
+{
+	snd_card_t *card;
+	ac97_bus_t *ac97_bus;
+	ac97_template_t ac97_template;
+	int ret;
+
+	ret = -ENOMEM;
+	card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			    THIS_MODULE, 0);
+	if (!card)
+		goto err;
+
+	card->dev = dev;
+	strncpy(card->driver, dev->driver->name, sizeof(card->driver));
+
+	ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm);
+	if (ret)
+		goto err;
+
+	ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL);
+	if (ret < 0)
+		goto err;
+
+	pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
+	pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
+	pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
+	pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
+#ifdef CONFIG_PXA27x
+	/* Use GPIO 113 as AC97 Reset on Bulverde */
+	pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+#endif
+	pxa_set_cken(CKEN2_AC97, 1);
+
+	ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus);
+	if (ret)
+		goto err;
+	memset(&ac97_template, 0, sizeof(ac97_template));
+	ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97);
+	if (ret)
+		goto err;
+
+	snprintf(card->shortname, sizeof(card->shortname),
+		 "%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s (%s)", dev->driver->name, card->mixername);
+
+	snd_card_set_pm_callback(card, pxa2xx_ac97_do_suspend,
+				 pxa2xx_ac97_do_resume, NULL);
+	ret = snd_card_register(card);
+	if (ret == 0) {
+		dev_set_drvdata(dev, card);
+		return 0;
+	}
+
+ err:
+	if (card)
+		snd_card_free(card);
+	if (CKEN & CKEN2_AC97) {
+		GCR |= GCR_ACLINK_OFF;
+		free_irq(IRQ_AC97, NULL);
+		pxa_set_cken(CKEN2_AC97, 0);
+	}
+	return ret;
+}
+
+static int pxa2xx_ac97_remove(struct device *dev)
+{
+	snd_card_t *card = dev_get_drvdata(dev);
+
+	if (card) {
+		snd_card_free(card);
+		dev_set_drvdata(dev, NULL);
+		GCR |= GCR_ACLINK_OFF;
+		free_irq(IRQ_AC97, NULL);
+		pxa_set_cken(CKEN2_AC97, 0);
+	}
+
+	return 0;
+}
+
+static struct device_driver pxa2xx_ac97_driver = {
+	.name		= "pxa2xx-ac97",
+	.bus		= &platform_bus_type,
+	.probe		= pxa2xx_ac97_probe,
+	.remove		= pxa2xx_ac97_remove,
+	.suspend	= pxa2xx_ac97_suspend,
+	.resume		= pxa2xx_ac97_resume,
+};
+
+static int __init pxa2xx_ac97_init(void)
+{
+	return driver_register(&pxa2xx_ac97_driver);
+}
+
+static void __exit pxa2xx_ac97_exit(void)
+{
+	driver_unregister(&pxa2xx_ac97_driver);
+}
+
+module_init(pxa2xx_ac97_init);
+module_exit(pxa2xx_ac97_exit);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
+MODULE_LICENSE("GPL");
diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c
new file mode 100644
index 0000000..b1eb53b
--- /dev/null
+++ b/sound/arm/pxa2xx-pcm.c
@@ -0,0 +1,367 @@
+/*
+ * linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip
+ *
+ * Author:	Nicolas Pitre
+ * Created:	Nov 30, 2004
+ * Copyright:	(C) 2004 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include <asm/dma.h>
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+#include "pxa2xx-pcm.h"
+
+
+static const snd_pcm_hardware_t pxa2xx_pcm_hardware = {
+	.info			= SNDRV_PCM_INFO_MMAP |
+				  SNDRV_PCM_INFO_MMAP_VALID |
+				  SNDRV_PCM_INFO_INTERLEAVED |
+				  SNDRV_PCM_INFO_PAUSE,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+	.period_bytes_min	= 32,
+	.period_bytes_max	= 8192 - 32,
+	.periods_min		= 1,
+	.periods_max		= PAGE_SIZE/sizeof(pxa_dma_desc),
+	.buffer_bytes_max	= 128 * 1024,
+	.fifo_size		= 32,
+};
+
+struct pxa2xx_runtime_data {
+	int dma_ch;
+	pxa2xx_pcm_dma_params_t *params;
+	pxa_dma_desc *dma_desc_array;
+	dma_addr_t dma_desc_array_phys;
+};
+
+static int pxa2xx_pcm_hw_params(snd_pcm_substream_t *substream,
+				snd_pcm_hw_params_t *params)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	struct pxa2xx_runtime_data *rtd = runtime->private_data;
+	size_t totsize = params_buffer_bytes(params);
+	size_t period = params_period_bytes(params);
+	pxa_dma_desc *dma_desc;
+	dma_addr_t dma_buff_phys, next_desc_phys;
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = totsize;
+
+	dma_desc = rtd->dma_desc_array;
+	next_desc_phys = rtd->dma_desc_array_phys;
+	dma_buff_phys = runtime->dma_addr;
+	do {
+		next_desc_phys += sizeof(pxa_dma_desc);
+		dma_desc->ddadr = next_desc_phys;
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			dma_desc->dsadr = dma_buff_phys;
+			dma_desc->dtadr = rtd->params->dev_addr;
+		} else {
+			dma_desc->dsadr = rtd->params->dev_addr;
+			dma_desc->dtadr = dma_buff_phys;
+		}
+		if (period > totsize)
+			period = totsize;
+		dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN;
+		dma_desc++;
+		dma_buff_phys += period;
+	} while (totsize -= period);
+	dma_desc[-1].ddadr = rtd->dma_desc_array_phys;
+
+	return 0;
+}
+
+static int pxa2xx_pcm_hw_free(snd_pcm_substream_t *substream)
+{
+	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
+
+	*rtd->params->drcmr = 0;
+	snd_pcm_set_runtime_buffer(substream, NULL);
+	return 0;
+}
+
+static int pxa2xx_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	pxa2xx_pcm_client_t *client = substream->private_data;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	struct pxa2xx_runtime_data *rtd = runtime->private_data;
+
+	DCSR(rtd->dma_ch) &= ~DCSR_RUN;
+	DCSR(rtd->dma_ch) = 0;
+	DCMD(rtd->dma_ch) = 0;
+	*rtd->params->drcmr = rtd->dma_ch | DRCMR_MAPVLD;
+
+	return client->prepare(substream);
+}
+
+static int pxa2xx_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		DDADR(rtd->dma_ch) = rtd->dma_desc_array_phys;
+		DCSR(rtd->dma_ch) = DCSR_RUN;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		DCSR(rtd->dma_ch) &= ~DCSR_RUN;
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		DCSR(rtd->dma_ch) |= DCSR_RUN;
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id, struct pt_regs *regs)
+{
+	snd_pcm_substream_t *substream = dev_id;
+	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
+	int dcsr;
+
+	dcsr = DCSR(dma_ch);
+	DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
+
+	if (dcsr & DCSR_ENDINTR) {
+		snd_pcm_period_elapsed(substream);
+	} else {
+		printk( KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
+			rtd->params->name, dma_ch, dcsr );
+		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+	}
+}
+
+static snd_pcm_uframes_t pxa2xx_pcm_pointer(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	struct pxa2xx_runtime_data *rtd = runtime->private_data;
+	dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+			 DSADR(rtd->dma_ch) : DTADR(rtd->dma_ch);
+	snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
+	if (x == runtime->buffer_size)
+		x = 0;
+	return x;
+}
+
+static int
+pxa2xx_pcm_hw_rule_mult32(snd_pcm_hw_params_t *params, snd_pcm_hw_rule_t *rule)
+{
+	snd_interval_t *i = hw_param_interval(params, rule->var);
+	int changed = 0;
+
+	if (i->min & 31) {
+		i->min = (i->min & ~31) + 32;
+		i->openmin = 0;
+		changed = 1;
+	}
+
+	if (i->max & 31) {
+		i->max &= ~31;
+		i->openmax = 0;
+		changed = 1;
+	}
+
+	return changed;
+}
+
+static int pxa2xx_pcm_open(snd_pcm_substream_t *substream)
+{
+	pxa2xx_pcm_client_t *client = substream->private_data;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	struct pxa2xx_runtime_data *rtd;
+	int ret;
+
+	runtime->hw = pxa2xx_pcm_hardware;
+
+	/*
+	 * For mysterious reasons (and despite what the manual says)
+	 * playback samples are lost if the DMA count is not a multiple
+	 * of the DMA burst size.  Let's add a rule to enforce that.
+	 */
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+				  pxa2xx_pcm_hw_rule_mult32, NULL,
+				  SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1);
+	if (ret)
+		goto out;
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+				  pxa2xx_pcm_hw_rule_mult32, NULL,
+				  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1);
+	if (ret)
+		goto out;
+
+	ret = -ENOMEM;
+	rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
+	if (!rtd)
+		goto out;
+	rtd->dma_desc_array =
+		dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
+				       &rtd->dma_desc_array_phys, GFP_KERNEL);
+	if (!rtd->dma_desc_array)
+		goto err1;
+
+	rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		      client->playback_params : client->capture_params;
+	ret = pxa_request_dma(rtd->params->name, DMA_PRIO_LOW,
+			      pxa2xx_pcm_dma_irq, substream);
+	if (ret < 0)
+		goto err2;
+	rtd->dma_ch = ret;
+
+	runtime->private_data = rtd;
+	ret = client->startup(substream);
+	if (!ret)
+		goto out;
+
+	pxa_free_dma(rtd->dma_ch);
+ err2:
+	dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
+			      rtd->dma_desc_array, rtd->dma_desc_array_phys);
+ err1:
+	kfree(rtd);
+ out:
+	return ret;
+}
+
+static int pxa2xx_pcm_close(snd_pcm_substream_t *substream)
+{
+	pxa2xx_pcm_client_t *client = substream->private_data;
+	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
+
+	pxa_free_dma(rtd->dma_ch);
+	client->shutdown(substream);
+	dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
+			      rtd->dma_desc_array, rtd->dma_desc_array_phys);
+	kfree(rtd);
+	return 0;
+}
+
+static int
+pxa2xx_pcm_mmap(snd_pcm_substream_t *substream, struct vm_area_struct *vma)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+				     runtime->dma_area,
+				     runtime->dma_addr,
+				     runtime->dma_bytes);
+}
+
+static snd_pcm_ops_t pxa2xx_pcm_ops = {
+	.open		= pxa2xx_pcm_open,
+	.close		= pxa2xx_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= pxa2xx_pcm_hw_params,
+	.hw_free	= pxa2xx_pcm_hw_free,
+	.prepare	= pxa2xx_pcm_prepare,
+	.trigger	= pxa2xx_pcm_trigger,
+	.pointer	= pxa2xx_pcm_pointer,
+	.mmap		= pxa2xx_pcm_mmap,
+};
+
+static int pxa2xx_pcm_preallocate_dma_buffer(snd_pcm_t *pcm, int stream)
+{
+	snd_pcm_substream_t *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+					   &buf->addr, GFP_KERNEL);
+	if (!buf->area)
+		return -ENOMEM;
+	buf->bytes = size;
+	return 0;
+}
+
+static void pxa2xx_pcm_free_dma_buffers(snd_pcm_t *pcm)
+{
+	snd_pcm_substream_t *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+		dma_free_writecombine(pcm->card->dev, buf->bytes,
+				      buf->area, buf->addr);
+		buf->area = NULL;
+	}
+}
+
+static u64 pxa2xx_pcm_dmamask = 0xffffffff;
+
+int pxa2xx_pcm_new(snd_card_t *card, pxa2xx_pcm_client_t *client, snd_pcm_t **rpcm)
+{
+	snd_pcm_t *pcm;
+	int play = client->playback_params ? 1 : 0;
+	int capt = client->capture_params ? 1 : 0;
+	int ret;
+
+	ret = snd_pcm_new(card, "PXA2xx-PCM", 0, play, capt, &pcm);
+	if (ret)
+		goto out;
+
+	pcm->private_data = client;
+	pcm->private_free = pxa2xx_pcm_free_dma_buffers;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &pxa2xx_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = 0xffffffff;
+
+	if (play) {
+		int stream = SNDRV_PCM_STREAM_PLAYBACK;
+		snd_pcm_set_ops(pcm, stream, &pxa2xx_pcm_ops);
+		ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, stream);
+		if (ret)
+			goto out;
+	}
+	if (capt) {
+		int stream = SNDRV_PCM_STREAM_CAPTURE;
+		snd_pcm_set_ops(pcm, stream, &pxa2xx_pcm_ops);
+		ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, stream);
+		if (ret)
+			goto out;
+	}
+
+	if (rpcm)
+		*rpcm = pcm;
+	ret = 0;
+
+ out:
+	return ret;
+}
+
+EXPORT_SYMBOL(pxa2xx_pcm_new);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h
new file mode 100644
index 0000000..4351759
--- /dev/null
+++ b/sound/arm/pxa2xx-pcm.h
@@ -0,0 +1,29 @@
+/*
+ * linux/sound/arm/pxa2xx-pcm.h -- ALSA PCM interface for the Intel PXA2xx chip
+ *
+ * Author:	Nicolas Pitre
+ * Created:	Nov 30, 2004
+ * Copyright:	MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+typedef struct {
+	char *name;			/* stream identifier */
+	u32 dcmd;			/* DMA descriptor dcmd field */
+	volatile u32 *drcmr;		/* the DMA request channel to use */
+	u32 dev_addr;			/* device physical address for DMA */
+} pxa2xx_pcm_dma_params_t;
+	
+typedef struct {
+	pxa2xx_pcm_dma_params_t *playback_params;
+	pxa2xx_pcm_dma_params_t *capture_params;
+	int (*startup)(snd_pcm_substream_t *);
+	void (*shutdown)(snd_pcm_substream_t *);
+	int (*prepare)(snd_pcm_substream_t *);
+} pxa2xx_pcm_client_t;
+
+extern int pxa2xx_pcm_new(snd_card_t *, pxa2xx_pcm_client_t *, snd_pcm_t **);
+
diff --git a/sound/core/device.c b/sound/core/device.c
index 18c71f9..ca00ad7 100644
--- a/sound/core/device.c
+++ b/sound/core/device.c
@@ -28,7 +28,7 @@
 /**
  * snd_device_new - create an ALSA device component
  * @card: the card instance
- * @type: the device type, SNDRV_DEV_TYPE_XXX
+ * @type: the device type, SNDRV_DEV_XXX
  * @device_data: the data pointer of this device
  * @ops: the operator table
  *
@@ -46,7 +46,9 @@
 {
 	snd_device_t *dev;
 
-	snd_assert(card != NULL && device_data != NULL && ops != NULL, return -ENXIO);
+	snd_assert(card != NULL, return -ENXIO);
+	snd_assert(device_data != NULL, return -ENXIO);
+	snd_assert(ops != NULL, return -ENXIO);
 	dev = kcalloc(1, sizeof(*dev), GFP_KERNEL);
 	if (dev == NULL)
 		return -ENOMEM;
@@ -102,7 +104,7 @@
 }
 
 /**
- * snd_device_free - disconnect the device
+ * snd_device_disconnect - disconnect the device
  * @card: the card instance
  * @device_data: the data pointer to disconnect
  *
@@ -118,7 +120,7 @@
 {
 	struct list_head *list;
 	snd_device_t *dev;
-	
+
 	snd_assert(card != NULL, return -ENXIO);
 	snd_assert(device_data != NULL, return -ENXIO);
 	list_for_each(list, &card->devices) {
@@ -154,8 +156,9 @@
 	struct list_head *list;
 	snd_device_t *dev;
 	int err;
-	
-	snd_assert(card != NULL && device_data != NULL, return -ENXIO);
+
+	snd_assert(card != NULL, return -ENXIO);
+	snd_assert(device_data != NULL, return -ENXIO);
 	list_for_each(list, &card->devices) {
 		dev = snd_device(list);
 		if (dev->device_data != device_data)
diff --git a/sound/core/info.c b/sound/core/info.c
index 5e122bb..7f8bdf7 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -702,7 +702,7 @@
 }
 
 /**
- * snd_info_get_line - parse a string token
+ * snd_info_get_str - parse a string token
  * @dest: the buffer to store the string token
  * @src: the original string
  * @len: the max. length of token - 1
@@ -939,7 +939,8 @@
 {
 	struct proc_dir_entry *root;
 
-	snd_assert(entry != NULL && entry->p != NULL, return -ENXIO);
+	snd_assert(entry != NULL, return -ENXIO);
+	snd_assert(entry->p != NULL, return -ENXIO);
 	root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
 	snd_assert(root, return -ENXIO);
 	down(&info_mutex);
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index dbc23e3..0213256 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -105,7 +105,8 @@
  */
 
 static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size,
-					 dma_addr_t *dma_handle, int flags)
+					 dma_addr_t *dma_handle,
+					 unsigned int __nocast flags)
 {
 	void *ret;
 	u64 dma_mask, coherent_dma_mask;
diff --git a/sound/core/memory.c b/sound/core/memory.c
index c1fb28e..f689557 100644
--- a/sound/core/memory.c
+++ b/sound/core/memory.c
@@ -89,7 +89,7 @@
 	}
 }
 
-static void *__snd_kmalloc(size_t size, int flags, void *caller)
+static void *__snd_kmalloc(size_t size, unsigned int __nocast flags, void *caller)
 {
 	unsigned long cpu_flags;
 	struct snd_alloc_track *t;
@@ -111,12 +111,12 @@
 }
 
 #define _snd_kmalloc(size, flags) __snd_kmalloc((size), (flags), __builtin_return_address(0));
-void *snd_hidden_kmalloc(size_t size, int flags)
+void *snd_hidden_kmalloc(size_t size, unsigned int __nocast flags)
 {
 	return _snd_kmalloc(size, flags);
 }
 
-void *snd_hidden_kcalloc(size_t n, size_t size, int flags)
+void *snd_hidden_kcalloc(size_t n, size_t size, unsigned int __nocast flags)
 {
 	void *ret = NULL;
 	if (n != 0 && size > INT_MAX / n)
@@ -184,7 +184,7 @@
 	snd_wrapper_vfree(obj);
 }
 
-char *snd_hidden_kstrdup(const char *s, int flags)
+char *snd_hidden_kstrdup(const char *s, unsigned int __nocast flags)
 {
 	int len;
 	char *buf;
diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile
index 64cb50d..402e2b4 100644
--- a/sound/core/seq/Makefile
+++ b/sound/core/seq/Makefile
@@ -38,7 +38,7 @@
 obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o
 obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
 obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
-obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-seq-instr.o
+obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-seq-midi-emul.o snd-seq-instr.o
 obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o
 obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o
 obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-seq-midi-emul.o snd-seq-instr.o
diff --git a/sound/core/seq/instr/ainstr_gf1.c b/sound/core/seq/instr/ainstr_gf1.c
index 0779c41..32e91c6 100644
--- a/sound/core/seq/instr/ainstr_gf1.c
+++ b/sound/core/seq/instr/ainstr_gf1.c
@@ -50,7 +50,8 @@
 {
 	gf1_wave_t *wp, *prev;
 	gf1_xwave_t xp;
-	int err, gfp_mask;
+	int err;
+	unsigned int gfp_mask;
 	unsigned int real_size;
 	
 	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
diff --git a/sound/core/seq/instr/ainstr_iw.c b/sound/core/seq/instr/ainstr_iw.c
index 39ff72b..2622b86 100644
--- a/sound/core/seq/instr/ainstr_iw.c
+++ b/sound/core/seq/instr/ainstr_iw.c
@@ -58,7 +58,7 @@
 					       iwffff_xenv_t *ex,
 					       char __user **data,
 					       long *len,
-					       int gfp_mask)
+					       unsigned int __nocast gfp_mask)
 {
 	__u32 stype;
 	iwffff_env_record_t *rp, *rp_last;
@@ -128,7 +128,8 @@
 {
 	iwffff_wave_t *wp, *prev;
 	iwffff_xwave_t xp;
-	int err, gfp_mask;
+	int err;
+	unsigned int gfp_mask;
 	unsigned int real_size;
 	
 	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
@@ -234,7 +235,8 @@
 	iwffff_xinstrument_t ix;
 	iwffff_layer_t *lp, *prev_lp;
 	iwffff_xlayer_t lx;
-	int err, gfp_mask;
+	int err;
+	unsigned int gfp_mask;
 
 	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
 		return -EINVAL;
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index 57be915..4374829 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -134,7 +134,7 @@
 	seq_midisynth_t *msynth = (seq_midisynth_t *) private_data;
 	unsigned char msg[10];	/* buffer for constructing midi messages */
 	snd_rawmidi_substream_t *substream;
-	int res;
+	int len;
 
 	snd_assert(msynth != NULL, return -EINVAL);
 	substream = msynth->output_rfile.output;
@@ -146,20 +146,16 @@
 			snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
 			return 0;
 		}
-		res = snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
+		snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
 		snd_midi_event_reset_decode(msynth->parser);
-		if (res < 0)
-			return res;
 	} else {
 		if (msynth->parser == NULL)
 			return -EIO;
-		res = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev);
-		if (res < 0)
-			return res;
-		if ((res = dump_midi(substream, msg, res)) < 0) {
+		len = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev);
+		if (len < 0)
+			return 0;
+		if (dump_midi(substream, msg, len) < 0)
 			snd_midi_event_reset_decode(msynth->parser);
-			return res;
-		}
 	}
 	return 0;
 }
diff --git a/sound/core/wrappers.c b/sound/core/wrappers.c
index 9f39302..508e6d6 100644
--- a/sound/core/wrappers.c
+++ b/sound/core/wrappers.c
@@ -27,7 +27,7 @@
 #include <linux/fs.h>
 
 #ifdef CONFIG_SND_DEBUG_MEMORY
-void *snd_wrapper_kmalloc(size_t size, int flags)
+void *snd_wrapper_kmalloc(size_t size, unsigned int __nocast flags)
 {
 	return kmalloc(size, flags);
 }
diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c
index 1811471..4fc38bd 100644
--- a/sound/drivers/vx/vx_uer.c
+++ b/sound/drivers/vx/vx_uer.c
@@ -162,34 +162,24 @@
 
 static int vx_calc_clock_from_freq(vx_core_t *chip, int freq)
 {
-#define XX_FECH48000                    0x0000004B
-#define XX_FECH32000                    0x00000171
-#define XX_FECH24000                    0x0000024B
-#define XX_FECH16000                    0x00000371
-#define XX_FECH12000                    0x0000044B
-#define XX_FECH8000                     0x00000571
-#define XX_FECH44100                    0x0000007F
-#define XX_FECH29400                    0x0000016F
-#define XX_FECH22050                    0x0000027F
-#define XX_FECH14000                    0x000003EF
-#define XX_FECH11025                    0x0000047F
-#define XX_FECH7350                     0x000005BF
+	int hexfreq;
 
-	switch (freq) {
-	case 48000:     return XX_FECH48000;
-	case 44100:     return XX_FECH44100;
-	case 32000:     return XX_FECH32000;
-	case 29400:     return XX_FECH29400;
-	case 24000:     return XX_FECH24000;
-	case 22050:     return XX_FECH22050;
-	case 16000:     return XX_FECH16000;
-	case 14000:     return XX_FECH14000;
-	case 12000:     return XX_FECH12000;
-	case 11025:     return XX_FECH11025;
-	case 8000:      return XX_FECH8000;
-	case 7350:      return XX_FECH7350;
-	default:        return freq;   /* The value is already correct */
-	}
+	snd_assert(freq > 0, return 0);
+
+	hexfreq = (28224000 * 10) / freq;
+	hexfreq = (hexfreq + 5) / 10;
+
+	/* max freq = 55125 Hz */
+	snd_assert(hexfreq > 0x00000200, return 0);
+
+	if (hexfreq <= 0x03ff)
+		return hexfreq - 0x00000201;
+	if (hexfreq <= 0x07ff) 
+		return (hexfreq / 2) - 1;
+	if (hexfreq <= 0x0fff)
+		return (hexfreq / 4) + 0x000001ff;
+
+	return 0x5fe; 	/* min freq = 6893 Hz */
 }
 
 
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
index f5e6018..5adde30 100644
--- a/sound/i2c/other/ak4114.c
+++ b/sound/i2c/other/ak4114.c
@@ -554,7 +554,6 @@
 		if (snd_pcm_running(ak4114->capture_substream)) {
 			// printk("rate changed (%i <- %i)\n", runtime->rate, res);
 			snd_pcm_stop(ak4114->capture_substream, SNDRV_PCM_STATE_DRAINING);
-			wake_up(&runtime->sleep);
 			res = 1;
 		}
 		snd_pcm_stream_unlock_irqrestore(ak4114->capture_substream, _flags);
diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c
index 94bbd34..a636d9c 100644
--- a/sound/isa/gus/gus_main.c
+++ b/sound/isa/gus/gus_main.c
@@ -417,11 +417,13 @@
 	return 0;
 }
 
+#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
 static void snd_gus_seq_dev_free(snd_seq_device_t *seq_dev)
 {
 	snd_gus_card_t *gus = seq_dev->private_data;
 	gus->seq_dev = NULL;
 }
+#endif
 
 int snd_gus_initialize(snd_gus_card_t *gus)
 {
diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c
index 0e13623..3237968 100644
--- a/sound/isa/wavefront/wavefront_fx.c
+++ b/sound/isa/wavefront/wavefront_fx.c
@@ -34,7 +34,7 @@
 
 /* weird stuff, derived from port I/O tracing with dosemu */
 
-unsigned char page_zero[] __initdata = {
+static unsigned char page_zero[] __initdata = {
 0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00,
 0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00,
 0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00,
@@ -61,7 +61,7 @@
 0x1d, 0x02, 0xdf
 };
 
-unsigned char page_one[] __initdata = {
+static unsigned char page_one[] __initdata = {
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00,
 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00,
 0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01,
@@ -88,7 +88,7 @@
 0x60, 0x00, 0x1b
 };
 
-unsigned char page_two[] __initdata = {
+static unsigned char page_two[] __initdata = {
 0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4,
 0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -103,7 +103,7 @@
 0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44
 };
 
-unsigned char page_three[] __initdata = {
+static unsigned char page_three[] __initdata = {
 0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06,
 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -118,7 +118,7 @@
 0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40
 };
 
-unsigned char page_four[] __initdata = {
+static unsigned char page_four[] __initdata = {
 0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02,
 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -133,7 +133,7 @@
 0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01
 };
 
-unsigned char page_six[] __initdata = {
+static unsigned char page_six[] __initdata = {
 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00,
 0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e,
 0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00,
@@ -154,7 +154,7 @@
 0x80, 0x00, 0x7e, 0x80, 0x80
 };
 
-unsigned char page_seven[] __initdata = {
+static unsigned char page_seven[] __initdata = {
 0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
 0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f,
@@ -181,7 +181,7 @@
 0x00, 0x02, 0x00
 };
 
-unsigned char page_zero_v2[] __initdata = {
+static unsigned char page_zero_v2[] __initdata = {
 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -193,7 +193,7 @@
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
 
-unsigned char page_one_v2[] __initdata = {
+static unsigned char page_one_v2[] __initdata = {
 0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -205,21 +205,21 @@
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
 
-unsigned char page_two_v2[] __initdata = {
+static unsigned char page_two_v2[] __initdata = {
 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00
 };
-unsigned char page_three_v2[] __initdata = {
+static unsigned char page_three_v2[] __initdata = {
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00
 };
-unsigned char page_four_v2[] __initdata = {
+static unsigned char page_four_v2[] __initdata = {
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -227,7 +227,7 @@
 0x00, 0x00, 0x00, 0x00
 };
 
-unsigned char page_seven_v2[] __initdata = {
+static unsigned char page_seven_v2[] __initdata = {
 0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -239,7 +239,7 @@
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
 
-unsigned char mod_v2[] __initdata = {
+static unsigned char mod_v2[] __initdata = {
 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02,
 0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05,
 0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0,
@@ -269,7 +269,7 @@
 0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01,
 0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01
 };
-unsigned char coefficients[] __initdata = {
+static unsigned char coefficients[] __initdata = {
 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03,
 0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49,
 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01,
@@ -305,14 +305,14 @@
 0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02,
 0xba
 };
-unsigned char coefficients2[] __initdata = {
+static unsigned char coefficients2[] __initdata = {
 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f,
 0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d,
 0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07,
 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00,
 0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00
 };
-unsigned char coefficients3[] __initdata = {
+static unsigned char coefficients3[] __initdata = {
 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00,
 0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc,
 0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01,
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index a4b72cd..6983eea 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -367,6 +367,7 @@
 		ac97->regs[reg] = value;
 		ac97->bus->ops->write(ac97, reg, value);
 	}
+	set_bit(reg, ac97->reg_accessed);
 	up(&ac97->reg_mutex);
 	return change;
 }
@@ -410,6 +411,7 @@
 		ac97->regs[reg] = new;
 		ac97->bus->ops->write(ac97, reg, new);
 	}
+	set_bit(reg, ac97->reg_accessed);
 	return change;
 }
 
@@ -1076,6 +1078,11 @@
 	for (i = 0 ; i < ARRAY_SIZE(cbit); i++) {
 		unsigned short val;
 		snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8));
+		/* Do the read twice due to buffers on some ac97 codecs.
+		 * e.g. The STAC9704 returns exactly what you wrote the the register
+		 * if you read it immediately. This causes the detect routine to fail.
+		 */
+		val = snd_ac97_read(ac97, reg);
 		val = snd_ac97_read(ac97, reg);
 		if (! *lo_max && (val & 0x7f) == cbit[i])
 			*lo_max = max[i];
@@ -2224,7 +2231,7 @@
  */
 void snd_ac97_resume(ac97_t *ac97)
 {
-	int i;
+	unsigned long end_time;
 
 	if (ac97->bus->ops->reset) {
 		ac97->bus->ops->reset(ac97);
@@ -2242,26 +2249,26 @@
 	snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]);
 	if (ac97_is_audio(ac97)) {
 		ac97->bus->ops->write(ac97, AC97_MASTER, 0x8101);
-		for (i = HZ/10; i >= 0; i--) {
+		end_time = jiffies + msecs_to_jiffies(100);
+		do {
 			if (snd_ac97_read(ac97, AC97_MASTER) == 0x8101)
 				break;
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout(1);
-		}
+		} while (time_after_eq(end_time, jiffies));
 		/* FIXME: extra delay */
 		ac97->bus->ops->write(ac97, AC97_MASTER, 0x8000);
-		if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000) {
-			set_current_state(TASK_UNINTERRUPTIBLE);
-			schedule_timeout(HZ/4);
-		}
+		if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000)
+			msleep(250);
 	} else {
-		for (i = HZ/10; i >= 0; i--) {
+		end_time = jiffies + msecs_to_jiffies(100);
+		do {
 			unsigned short val = snd_ac97_read(ac97, AC97_EXTENDED_MID);
 			if (val != 0xffff && (val & 1) != 0)
 				break;
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout(1);
-		}
+		} while (time_after_eq(end_time, jiffies));
 	}
 __reset_ready:
 
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index a15eb85..66edc85 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -1528,6 +1528,9 @@
 	},
 	AC97_SURROUND_JACK_MODE_CTL,
 	AC97_CHANNEL_MODE_CTL,
+
+	AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 10, 1, 0),
+	AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0),
 };
 
 static int patch_ad1888_specific(ac97_t *ac97)
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index eb5c36d..f08ae71f 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -399,7 +399,7 @@
 	unsigned long end_time;
 	unsigned int res;
 	
-	end_time = jiffies + 10 * (HZ >> 2);
+	end_time = jiffies + 10 * msecs_to_jiffies(250);
 	do {
 		res = snd_ali_5451_peek(codec,port);
 		if (! (res & 0x8000))
@@ -422,7 +422,7 @@
 	dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER);
 	dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER);
 
-	end_time = jiffies + 10 * (HZ >> 2);
+	end_time = jiffies + 10 * msecs_to_jiffies(250);
 	do {
 		dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER);
 		if (dwChk2 != dwChk1)
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index a6b4b8d..8d20029 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -265,6 +265,7 @@
  */
 static struct pci_device_id snd_atiixp_ids[] = {
 	{ 0x1002, 0x434d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */
+	{ 0x1002, 0x4378, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB400 */
 	{ 0, }
 };
 
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 4725b4a..f5a4ac1 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -306,7 +306,7 @@
 #define CM_REG_FM_PCI		0x50
 
 /*
- * for CMI-8338 .. this is not valid for CMI-8738.
+ * access from SB-mixer port
  */
 #define CM_REG_EXTENT_IND	0xf0
 #define CM_VPHONE_MASK		0xe0	/* Phone volume control (0-3) << 5 */
@@ -315,6 +315,7 @@
 #define CM_VSPKM		0x08	/* Speaker mute control, default high */
 #define CM_RLOOPREN		0x04    /* Rec. R-channel enable */
 #define CM_RLOOPLEN		0x02	/* Rec. L-channel enable */
+#define CM_VADMIC3		0x01	/* Mic record boost */
 
 /*
  * CMI-8338 spec ver 0.5 (this is not valid for CMI-8738):
@@ -2135,8 +2136,12 @@
 	CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15),
 	CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0),
 	CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0),
-	CMIPCI_MIXER_SW_MONO("Mic Boost", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1),
+	CMIPCI_MIXER_SW_MONO("Mic Boost Playback Switch", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1),
 	CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7),
+	CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7),
+	CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0),
+	CMIPCI_DOUBLE("PC Speaker Playnack Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0),
+	CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0),
 };
 
 /*
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index fd4c50c..ff28af1 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -2400,8 +2400,7 @@
 		if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05)
 			return;
 
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(HZ/100);
+		msleep(10);
 	} while (time_after_eq(end_time, jiffies));
 
 	snd_printk("CS46xx secondary codec dont respond!\n");  
@@ -2435,8 +2434,7 @@
 			err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97[codec]);
 			return err;
 		}
-		set_current_state(TASK_INTERRUPTIBLE);
-		schedule_timeout(HZ/100);
+		msleep(10);
 	}
 	snd_printdd("snd_cs46xx: codec %d detection timeout\n", codec);
 	return -ENXIO;
@@ -3018,8 +3016,7 @@
 	/*
          *  Wait until the PLL has stabilized.
 	 */
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout(HZ/10); /* 100ms */
+	msleep(100);
 
 	/*
 	 *  Turn on clocking of the core so that we can setup the serial ports.
@@ -3072,8 +3069,7 @@
 		 */
 		if (snd_cs46xx_peekBA0(chip, BA0_ACSTS) & ACSTS_CRDY)
 			goto ok1;
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout((HZ+99)/100);
+		msleep(10);
 	}
 
 
@@ -3122,8 +3118,7 @@
 		 */
 		if ((snd_cs46xx_peekBA0(chip, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4))
 			goto ok2;
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout((HZ+99)/100);
+		msleep(10);
 	}
 
 #ifndef CONFIG_SND_CS46XX_NEW_DSP
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index 2085a99..b17142c 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -52,6 +52,7 @@
 static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64};
 static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128};
 static int enable_ir[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static uint subsystem[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* Force card subsystem model */
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard.");
@@ -71,7 +72,8 @@
 MODULE_PARM_DESC(max_buffer_size, "Maximum sample buffer size in MB.");
 module_param_array(enable_ir, bool, NULL, 0444);
 MODULE_PARM_DESC(enable_ir, "Enable IR.");
-
+module_param_array(subsystem, uint, NULL, 0444);
+MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
 /*
  * Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value  Model:SB0400
  */
@@ -122,7 +124,7 @@
 		max_buffer_size[dev] = 1024;
 	if ((err = snd_emu10k1_create(card, pci, extin[dev], extout[dev],
 				      (long)max_buffer_size[dev] * 1024 * 1024,
-				      enable_ir[dev],
+				      enable_ir[dev], subsystem[dev],
 				      &emu)) < 0) {
 		snd_card_free(card);
 		return err;
@@ -140,7 +142,7 @@
 		return err;
 	}
 	/* This stores the periods table. */
-	if (emu->audigy && emu->revision == 4) { /* P16V */	
+	if (emu->card_capabilities->ca0151_chip) { /* P16V */	
 		if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), 1024, &emu->p16v_buffer) < 0) {
 			snd_p16v_free(emu);
 			return -ENOMEM;
@@ -161,7 +163,7 @@
 		snd_card_free(card);
 		return err;
 	}
-	if (emu->audigy && emu->revision == 4) { /* P16V */	
+	if (emu->card_capabilities->ca0151_chip) { /* P16V */	
 		if ((err = snd_p16v_pcm(emu, 4, NULL)) < 0) {
 			snd_card_free(card);
 			return err;
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index a341e75..746b51e 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -191,7 +191,7 @@
 		/* Set playback routing. */
 		snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, 0x78e4);
 	}
-	if (emu->audigy && (emu->serial == 0x10011102) ) { /* audigy2 Value */
+	if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */
 		/* Hacks for Alice3 to work independent of haP16V driver */
 		u32 tmp;
 
@@ -253,6 +253,8 @@
 			     HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
 		else
 			outl(HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+	/* FIXME: Remove all these emu->model and replace it with a card recognition parameter,
+	 * e.g. card_capabilities->joystick */
 	} else if (emu->model == 0x20 ||
 	    emu->model == 0xc400 ||
 	    (emu->model == 0x21 && emu->revision < 6))
@@ -299,12 +301,12 @@
 	if (emu->audigy) {
 		outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
  
-		if (emu->revision == 4) { /* audigy2 */
+		if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
 			/* Unmute Analog now.  Set GPO6 to 1 for Apollo.
 			 * This has to be done after init ALice3 I2SOut beyond 48KHz.
 			 * So, sequence is important. */
 			outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG);
-		} else if (emu->serial == 0x10011102) { /* audigy2 value */
+		} else if (emu->card_capabilities->ca0108_chip) { /* audigy2 value */
 			/* Unmute Analog now. */
 			outl(inl(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG);
 		} else {
@@ -614,6 +616,7 @@
 
 static emu_chip_details_t emu_chip_details[] = {
 	/* Audigy 2 Value AC3 out does not work yet. Need to find out how to turn off interpolators.*/
+	/* Tested by James@superbug.co.uk 3rd July 2005 */
 	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102,
 	 .driver = "Audigy2", .name = "Audigy 2 Value [SB0400]", 
 	 .id = "Audigy2",
@@ -627,6 +630,14 @@
 	 .emu10k2_chip = 1,
 	 .ca0108_chip = 1,
 	 .ac97_chip = 1} ,
+	/* Tested by James@superbug.co.uk 8th July 2005. No sound available yet. */
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
+	 .driver = "Audigy2", .name = "E-mu 1212m [4001]", 
+	 .id = "EMU1212m",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ecard = 1} ,
+	/* Tested by James@superbug.co.uk 3rd July 2005 */
 	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102,
 	 .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]", 
 	 .id = "Audigy2",
@@ -687,19 +698,19 @@
 	 .ca0151_chip = 1,
 	 .spdif_bug = 1,
 	 .ac97_chip = 1} ,
-	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10020052,
-	 .driver = "Audigy", .name = "Audigy 1 ES [SB0160]", 
-	 .id = "Audigy",
-	 .emu10k2_chip = 1,
-	 .ca0102_chip = 1,
-	 .spdif_bug = 1,
-	 .ac97_chip = 1} ,
 	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00531102,
 	 .driver = "Audigy", .name = "Audigy 1 [SB0090]", 
 	 .id = "Audigy",
 	 .emu10k2_chip = 1,
 	 .ca0102_chip = 1,
 	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00521102,
+	 .driver = "Audigy", .name = "Audigy 1 ES [SB0160]", 
+	 .id = "Audigy",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .spdif_bug = 1,
+	 .ac97_chip = 1} ,
 	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00511102,
 	 .driver = "Audigy", .name = "Audigy 1 [SB0090]", 
 	 .id = "Audigy",
@@ -712,96 +723,8 @@
 	 .emu10k2_chip = 1,
 	 .ca0102_chip = 1,
 	 .ac97_chip = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102,
-	 .driver = "EMU10K1", .name = "E-mu APS [4001]", 
-	 .id = "APS",
-	 .emu10k1_chip = 1,
-	 .ecard = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102,
-	 .driver = "EMU10K1", .name = "SBLive! Player 5.1 [SB0060]", 
-	 .id = "Live",
-	 .emu10k1_chip = 1,
-	 .ac97_chip = 1,
-	 .sblive51 = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80641102,
-	 .driver = "EMU10K1", .name = "SB Live 5.1", 
-	 .id = "Live",
-	 .emu10k1_chip = 1,
-	 .ac97_chip = 1,
-	 .sblive51 = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102,
-	 .driver = "EMU10K1", .name = "SBLive! Platinum [CT4760P]", 
-	 .id = "Live",
-	 .emu10k1_chip = 1,
-	 .ac97_chip = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00211102,
-	 .driver = "EMU10K1", .name = "SBLive! [CT4620]", 
-	 .id = "Live",
-	 .emu10k1_chip = 1,
-	 .ac97_chip = 1,
-	 .sblive51 = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00201102,
-	 .driver = "EMU10K1", .name = "SBLive! Value [CT4670]", 
-	 .id = "Live",
-	 .emu10k1_chip = 1,
-	 .ac97_chip = 1,
-	 .sblive51 = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80221102,
-	 .driver = "EMU10K1", .name = "SBLive! Value [CT4780]", 
-	 .id = "Live",
-	 .emu10k1_chip = 1,
-	 .ac97_chip = 1,
-	 .sblive51 = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80231102,
-	 .driver = "EMU10K1", .name = "SB PCI512 [CT4790]", 
-	 .id = "Live",
-	 .emu10k1_chip = 1,
-	 .ac97_chip = 1,
-	 .sblive51 = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80261102,
-	 .driver = "EMU10K1", .name = "SBLive! Value [CT4830]", 
-	 .id = "Live",
-	 .emu10k1_chip = 1,
-	 .ac97_chip = 1,
-	 .sblive51 = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80311102,
-	 .driver = "EMU10K1", .name = "SBLive! Value [CT4831]", 
-	 .id = "Live",
-	 .emu10k1_chip = 1,
-	 .ac97_chip = 1,
-	 .sblive51 = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80271102,
-	 .driver = "EMU10K1", .name = "SBLive! Value [CT4832]", 
-	 .id = "Live",
-	 .emu10k1_chip = 1,
-	 .ac97_chip = 1,
-	 .sblive51 = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80511102,
-	 .driver = "EMU10K1", .name = "SBLive! Value [CT4850]", 
-	 .id = "Live",
-	 .emu10k1_chip = 1,
-	 .ac97_chip = 1,
-	 .sblive51 = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80281102,
-	 .driver = "EMU10K1", .name = "SBLive! Value [CT4870]", 
-	 .id = "Live",
-	 .emu10k1_chip = 1,
-	 .ac97_chip = 1,
-	 .sblive51 = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80321102,
-	 .driver = "EMU10K1", .name = "SBLive! Value [CT4871]", 
-	 .id = "Live",
-	 .emu10k1_chip = 1,
-	 .ac97_chip = 1,
-	 .sblive51 = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102,
-	 .driver = "EMU10K1", .name = "SBLive! Value [SB0060]", 
-	 .id = "Live",
-	 .emu10k1_chip = 1,
-	 .ac97_chip = 1,
-	 .sblive51 = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80691102,
-	 .driver = "EMU10K1", .name = "SBLive! Value [SB0101]", 
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806B1102,
+	 .driver = "EMU10K1", .name = "SBLive! [SB0105]", 
 	 .id = "Live",
 	 .emu10k1_chip = 1,
 	 .ac97_chip = 1,
@@ -812,8 +735,91 @@
 	 .emu10k1_chip = 1,
 	 .ac97_chip = 1,
 	 .sblive51 = 1} ,
-	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806B1102,
-	 .driver = "EMU10K1", .name = "SBLive! [SB0105]", 
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80691102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [SB0101]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80641102,
+	 .driver = "EMU10K1", .name = "SB Live 5.1", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102,
+	 .driver = "EMU10K1", .name = "SBLive! Player 5.1 [SB0060]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80511102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4850]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102,
+	 .driver = "EMU10K1", .name = "SBLive! Platinum [CT4760P]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80321102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4871]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80311102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4831]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80281102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4870]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	/* Tested by James@superbug.co.uk 3rd July 2005 */
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80271102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4832]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80261102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4830]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80231102,
+	 .driver = "EMU10K1", .name = "SB PCI512 [CT4790]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80221102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4780]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102,
+	 .driver = "EMU10K1", .name = "E-mu APS [4001]", 
+	 .id = "APS",
+	 .emu10k1_chip = 1,
+	 .ecard = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00211102,
+	 .driver = "EMU10K1", .name = "SBLive! [CT4620]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00201102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4670]", 
 	 .id = "Live",
 	 .emu10k1_chip = 1,
 	 .ac97_chip = 1,
@@ -833,6 +839,7 @@
 		       unsigned short extout_mask,
 		       long max_cache_bytes,
 		       int enable_ir,
+		       uint subsystem,
 		       emu10k1_t ** remu)
 {
 	emu10k1_t *emu;
@@ -878,10 +885,16 @@
 
 	for (c = emu_chip_details; c->vendor; c++) {
 		if (c->vendor == pci->vendor && c->device == pci->device) {
-			if (c->subsystem && c->subsystem != emu->serial)
-				continue;
-			if (c->revision && c->revision != emu->revision)
-				continue;
+			if (subsystem) {
+				if (c->subsystem && (c->subsystem == subsystem) ) {
+					break;
+				} else continue;
+			} else {
+				if (c->subsystem && (c->subsystem != emu->serial) )
+					continue;
+				if (c->revision && c->revision != emu->revision)
+					continue;
+			}
 			break;
 		}
 	}
@@ -892,10 +905,14 @@
 		return -ENOENT;
 	}
 	emu->card_capabilities = c;
-	if (c->subsystem != 0)
+	if (c->subsystem && !subsystem)
 		snd_printdd("Sound card name=%s\n", c->name);
-	else
-		snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x\n", c->name, pci->vendor, pci->device, emu->serial);
+	else if (subsystem) 
+		snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x. Forced to subsytem=0x%x\n",
+		       	c->name, pci->vendor, pci->device, emu->serial, c->subsystem);
+	else 
+		snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x.\n",
+		      	c->name, pci->vendor, pci->device, emu->serial);
 	
 	if (!*card->id && c->id) {
 		int i, n = 0;
diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
index 98f9801..a169133 100644
--- a/sound/pci/emu10k1/p16v.c
+++ b/sound/pci/emu10k1/p16v.c
@@ -822,7 +822,7 @@
 static snd_kcontrol_new_t snd_p16v_volume_control_analog_front =
 {
         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
-        .name =         "HD Analog Front Volume",
+        .name =         "HD Analog Front Playback Volume",
         .info =         snd_p16v_volume_info,
         .get =          snd_p16v_volume_get_analog_front,
         .put =          snd_p16v_volume_put_analog_front
@@ -831,7 +831,7 @@
 static snd_kcontrol_new_t snd_p16v_volume_control_analog_center_lfe =
 {
         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
-        .name =         "HD Analog Center/LFE Volume",
+        .name =         "HD Analog Center/LFE Playback Volume",
         .info =         snd_p16v_volume_info,
         .get =          snd_p16v_volume_get_analog_center_lfe,
         .put =          snd_p16v_volume_put_analog_center_lfe
@@ -840,7 +840,7 @@
 static snd_kcontrol_new_t snd_p16v_volume_control_analog_unknown =
 {
         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
-        .name =         "HD Analog Unknown Volume",
+        .name =         "HD Analog Unknown Playback Volume",
         .info =         snd_p16v_volume_info,
         .get =          snd_p16v_volume_get_analog_unknown,
         .put =          snd_p16v_volume_put_analog_unknown
@@ -849,7 +849,7 @@
 static snd_kcontrol_new_t snd_p16v_volume_control_analog_rear =
 {
         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
-        .name =         "HD Analog Rear Volume",
+        .name =         "HD Analog Rear Playback Volume",
         .info =         snd_p16v_volume_info,
         .get =          snd_p16v_volume_get_analog_rear,
         .put =          snd_p16v_volume_put_analog_rear
@@ -858,7 +858,7 @@
 static snd_kcontrol_new_t snd_p16v_volume_control_spdif_front =
 {
         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
-        .name =         "HD SPDIF Front Volume",
+        .name =         "HD SPDIF Front Playback Volume",
         .info =         snd_p16v_volume_info,
         .get =          snd_p16v_volume_get_spdif_front,
         .put =          snd_p16v_volume_put_spdif_front
@@ -867,7 +867,7 @@
 static snd_kcontrol_new_t snd_p16v_volume_control_spdif_center_lfe =
 {
         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
-        .name =         "HD SPDIF Center/LFE Volume",
+        .name =         "HD SPDIF Center/LFE Playback Volume",
         .info =         snd_p16v_volume_info,
         .get =          snd_p16v_volume_get_spdif_center_lfe,
         .put =          snd_p16v_volume_put_spdif_center_lfe
@@ -876,7 +876,7 @@
 static snd_kcontrol_new_t snd_p16v_volume_control_spdif_unknown =
 {
         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
-        .name =         "HD SPDIF Unknown Volume",
+        .name =         "HD SPDIF Unknown Playback Volume",
         .info =         snd_p16v_volume_info,
         .get =          snd_p16v_volume_get_spdif_unknown,
         .put =          snd_p16v_volume_put_spdif_unknown
@@ -885,7 +885,7 @@
 static snd_kcontrol_new_t snd_p16v_volume_control_spdif_rear =
 {
         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
-        .name =         "HD SPDIF Rear Volume",
+        .name =         "HD SPDIF Rear Playback Volume",
         .info =         snd_p16v_volume_info,
         .get =          snd_p16v_volume_get_spdif_rear,
         .put =          snd_p16v_volume_put_spdif_rear
@@ -936,7 +936,7 @@
 static snd_kcontrol_new_t snd_p16v_capture_source __devinitdata =
 {
 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name =		"HD Capture source",
+	.name =		"HD source Capture",
 	.info =		snd_p16v_capture_source_info,
 	.get =		snd_p16v_capture_source_get,
 	.put =		snd_p16v_capture_source_put
@@ -985,7 +985,7 @@
 static snd_kcontrol_new_t snd_p16v_capture_channel __devinitdata =
 {
 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name =		"HD Capture channel",
+	.name =		"HD channel Capture",
 	.info =		snd_p16v_capture_channel_info,
 	.get =		snd_p16v_capture_channel_get,
 	.put =		snd_p16v_capture_channel_put
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index 4e63498..78a81f3 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -685,6 +685,15 @@
 	return 0;
 }
 
+static void snd_es1371_codec_wait(ac97_t *ac97)
+{
+	msleep(750);
+	snd_es1371_codec_read(ac97, AC97_RESET);
+	snd_es1371_codec_read(ac97, AC97_VENDOR_ID1);
+	snd_es1371_codec_read(ac97, AC97_VENDOR_ID2);
+	msleep(50);
+}
+
 static void snd_es1371_adc_rate(ensoniq_t * ensoniq, unsigned int rate)
 {
 	unsigned int n, truncm, freq, result;
@@ -1585,6 +1594,7 @@
 	static ac97_bus_ops_t ops = {
 		.write = snd_es1371_codec_write,
 		.read = snd_es1371_codec_read,
+		.wait = snd_es1371_codec_wait,
 	};
 
 	if ((err = snd_ac97_bus(card, 0, &ops, NULL, &pbus)) < 0)
@@ -2008,21 +2018,11 @@
 		if (pci->vendor == es1371_ac97_reset_hack[idx].vid &&
 		    pci->device == es1371_ac97_reset_hack[idx].did &&
 		    ensoniq->rev == es1371_ac97_reset_hack[idx].rev) {
-		        unsigned long tmo;
-			signed long tmo2;
-
 			ensoniq->cssr |= ES_1371_ST_AC97_RST;
 			outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
 			/* need to delay around 20ms(bleech) to give
 			some CODECs enough time to wakeup */
-			tmo = jiffies + (HZ / 50) + 1;
-			while (1) {
-				tmo2 = tmo - jiffies;
-				if (tmo2 <= 0)
-					break;
-				set_current_state(TASK_UNINTERRUPTIBLE);
-				schedule_timeout(tmo2);
-			}
+			msleep(20);
 			break;
 		}
 	/* AC'97 warm reset to start the bitclk */
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 327a341..9d7a287 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -664,11 +664,6 @@
 	return result;
 }
 
-#define big_mdelay(msec) do {\
-	set_current_state(TASK_UNINTERRUPTIBLE);\
-	schedule_timeout(((msec) * HZ + 999) / 1000);\
-} while (0)
-	
 /* Wait for the codec bus to be free */
 static int snd_es1968_ac97_wait(es1968_t *chip)
 {
@@ -1809,8 +1804,7 @@
 	snd_es1968_trigger_apu(chip, apu, ESM_APU_16BITLINEAR);
 	do_gettimeofday(&start_time);
 	spin_unlock_irq(&chip->reg_lock);
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout(HZ / 20); /* 50 msec */
+	msleep(50);
 	spin_lock_irq(&chip->reg_lock);
 	offset = __apu_get_register(chip, apu, 5);
 	do_gettimeofday(&stop_time);
@@ -2093,7 +2087,7 @@
 	outw(0x0000, ioaddr + 0x60);	/* write 0 to gpio 0 */
 	udelay(20);
 	outw(0x0001, ioaddr + 0x60);	/* write 1 to gpio 1 */
-	big_mdelay(20);
+	msleep(20);
 
 	outw(save_68 | 0x1, ioaddr + 0x68);	/* now restore .. */
 	outw((inw(ioaddr + 0x38) & 0xfffc) | 0x1, ioaddr + 0x38);
@@ -2109,7 +2103,7 @@
 	outw(0x0001, ioaddr + 0x60);	/* write 1 to gpio */
 	udelay(20);
 	outw(0x0009, ioaddr + 0x60);	/* write 9 to gpio */
-	big_mdelay(500);
+	msleep(500);
 	//outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38);
 	outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a);
 	outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);
@@ -2135,7 +2129,7 @@
 
 		if (w > 10000) {
 			outb(inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37);	/* do a software reset */
-			big_mdelay(500);	/* oh my.. */
+			msleep(500);	/* oh my.. */
 			outb(inb(ioaddr + 0x37) & ~0x08,
 				ioaddr + 0x37);
 			udelay(1);
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 5999156..dd0d99d 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -262,6 +262,9 @@
 #define AC_PINCTL_OUT_EN		(1<<6)
 #define AC_PINCTL_HP_EN			(1<<7)
 
+/* Unsolicited response - 8bit */
+#define AC_USRSP_EN			(1<<7)
+
 /* configuration default - 32bit */
 #define AC_DEFCFG_SEQUENCE		(0xf<<0)
 #define AC_DEFCFG_DEF_ASSOC		(0xf<<4)
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 5e0cca3..288ab07 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -178,6 +178,9 @@
 #define ICH6_INT_CTRL_EN	0x40000000	/* controller interrupt enable bit */
 #define ICH6_INT_GLOBAL_EN	0x80000000	/* global interrupt enable bit */
 
+/* GCTL unsolicited response enable bit */
+#define ICH6_GCTL_UREN		(1<<8)
+
 /* GCTL reset bit */
 #define ICH6_GCTL_RESET		(1<<0)
 
@@ -562,6 +565,9 @@
 		return -EBUSY;
 	}
 
+	/* Accept unsolicited responses */
+	azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UREN);
+
 	/* detect codecs */
 	if (! chip->codec_mask) {
 		chip->codec_mask = azx_readw(chip, STATESTS);
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index 2d6e3e3..86f195f 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -408,7 +408,7 @@
 		/* search for an empty channel */
 		for (j = 0; j < cfg->line_outs; j++) {
 			if (! assigned[j]) {
-				spec->dac_nids[i] = i + 0x03;
+				spec->dac_nids[i] = j + 0x03;
 				assigned[j] = 1;
 				break;
 			}
@@ -444,11 +444,10 @@
 			len = snd_hda_get_connections(codec, nid, conn, 4);
 			for (k = 0; k < len; k++)
 				if (conn[k] == spec->dac_nids[i]) {
-					spec->multi_init[j].param = j;
+					spec->multi_init[j].param = k;
 					break;
 				}
 			j++;
-			break;
 		}
 	}
 	return 0;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index bab8984..9b85699 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -40,6 +40,7 @@
 	ALC880_W810,
 	ALC880_Z71V,
 	ALC880_AUTO,
+	ALC880_6ST,
 	ALC880_6ST_DIG,
 	ALC880_F1734,
 	ALC880_ASUS,
@@ -119,6 +120,7 @@
 	unsigned int num_kctl_alloc, num_kctl_used;
 	snd_kcontrol_new_t *kctl_alloc;
 	struct hda_input_mux private_imux;
+	hda_nid_t private_dac_nids[4];
 };
 
 
@@ -1547,9 +1549,10 @@
 	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xa100, .config = ALC880_5ST_DIG },
 	{ .pci_subvendor = 0x1565, .pci_subdevice = 0x8202, .config = ALC880_5ST_DIG },
 	{ .pci_subvendor = 0x1019, .pci_subdevice = 0xa880, .config = ALC880_5ST_DIG },
-	{ .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_5ST_DIG },
+	/* { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_5ST_DIG }, */ /* conflict with 6stack */
 	{ .pci_subvendor = 0x1695, .pci_subdevice = 0x400d, .config = ALC880_5ST_DIG },
-	{ .pci_subvendor = 0x0000, .pci_subdevice = 0x8086, .config = ALC880_5ST_DIG },
+	/* note subvendor = 0 below */
+	/* { .pci_subvendor = 0x0000, .pci_subdevice = 0x8086, .config = ALC880_5ST_DIG }, */
 
 	{ .modelname = "w810", .config = ALC880_W810 },
 	{ .pci_subvendor = 0x161f, .pci_subdevice = 0x203d, .config = ALC880_W810 },
@@ -1557,7 +1560,10 @@
 	{ .modelname = "z71v", .config = ALC880_Z71V },
 	{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_Z71V },
 
-	{ .modelname = "6statack-digout", .config = ALC880_6ST_DIG },
+	{ .modelname = "6stack", .config = ALC880_6ST },
+	{ .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_6ST }, /* Acer APFV */
+
+	{ .modelname = "6stack-digout", .config = ALC880_6ST_DIG },
 	{ .pci_subvendor = 0x2668, .pci_subdevice = 0x8086, .config = ALC880_6ST_DIG },
 	{ .pci_subvendor = 0x8086, .pci_subdevice = 0x2668, .config = ALC880_6ST_DIG },
 	{ .pci_subvendor = 0x1462, .pci_subdevice = 0x1150, .config = ALC880_6ST_DIG },
@@ -1644,6 +1650,15 @@
 		.channel_mode = alc880_fivestack_modes,
 		.input_mux = &alc880_capture_source,
 	},
+	[ALC880_6ST] = {
+		.mixers = { alc880_six_stack_mixer },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_6stack_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_6st_dac_nids),
+		.dac_nids = alc880_6st_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes),
+		.channel_mode = alc880_sixstack_modes,
+		.input_mux = &alc880_6stack_capture_source,
+	},
 	[ALC880_6ST_DIG] = {
 		.mixers = { alc880_six_stack_mixer },
 		.init_verbs = { alc880_volume_init_verbs, alc880_pin_6stack_init_verbs },
@@ -1656,7 +1671,8 @@
 	},
 	[ALC880_W810] = {
 		.mixers = { alc880_w810_base_mixer },
-		.init_verbs = { alc880_volume_init_verbs, alc880_pin_w810_init_verbs },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_w810_init_verbs,
+				alc880_gpio2_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc880_w810_dac_nids),
 		.dac_nids = alc880_w810_dac_nids,
 		.dig_out_nid = ALC880_DIGOUT_NID,
@@ -1666,8 +1682,7 @@
 	},
 	[ALC880_Z71V] = {
 		.mixers = { alc880_z71v_mixer },
-		.init_verbs = { alc880_volume_init_verbs, alc880_pin_z71v_init_verbs,
-				alc880_gpio2_init_verbs },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_z71v_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids),
 		.dac_nids = alc880_z71v_dac_nids,
 		.dig_out_nid = ALC880_DIGOUT_NID,
@@ -1809,6 +1824,7 @@
 	int i, j;
 
 	memset(assigned, 0, sizeof(assigned));
+	spec->multiout.dac_nids = spec->private_dac_nids;
 
 	/* check the pins hardwired to audio widget */
 	for (i = 0; i < cfg->line_outs; i++) {
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 013be2e..9d503da 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -30,32 +30,37 @@
 #include <linux/slab.h>
 #include <linux/pci.h>
 #include <sound/core.h>
+#include <sound/asoundef.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 
 #undef STAC_TEST
 
+#define NUM_CONTROL_ALLOC	32
+#define STAC_HP_EVENT		0x37
+#define STAC_UNSOL_ENABLE 	(AC_USRSP_EN | STAC_HP_EVENT)
+
 struct sigmatel_spec {
+	snd_kcontrol_new_t *mixers[4];
+	unsigned int num_mixers;
+
+	unsigned int surr_switch: 1;
+
 	/* playback */
 	struct hda_multi_out multiout;
-	hda_nid_t playback_nid;
+	hda_nid_t dac_nids[4];
 
 	/* capture */
 	hda_nid_t *adc_nids;
 	unsigned int num_adcs;
 	hda_nid_t *mux_nids;
 	unsigned int num_muxes;
-	hda_nid_t capture_nid;
 	hda_nid_t dig_in_nid;
 
-	/* power management*/
-	hda_nid_t *pstate_nids;
-	unsigned int num_pstates;
-
+#ifdef STAC_TEST
 	/* pin widgets */
 	hda_nid_t *pin_nids;
 	unsigned int num_pins;
-#ifdef STAC_TEST
 	unsigned int *pin_configs;
 #endif
 
@@ -64,16 +69,20 @@
 	snd_kcontrol_new_t *mixer;
 
 	/* capture source */
-	struct hda_input_mux input_mux;
-	char input_labels[HDA_MAX_NUM_INPUTS][16];
+	struct hda_input_mux *input_mux;
 	unsigned int cur_mux[2];
 
 	/* channel mode */
 	unsigned int num_ch_modes;
 	unsigned int cur_ch_mode;
-	const struct sigmatel_channel_mode *channel_modes;
 
-	struct hda_pcm pcm_rec[1];	/* PCM information */
+	struct hda_pcm pcm_rec[2];	/* PCM information */
+
+	/* dynamic controls and input_mux */
+	struct auto_pin_cfg autocfg;
+	unsigned int num_kctl_alloc, num_kctl_used;
+	snd_kcontrol_new_t *kctl_alloc;
+	struct hda_input_mux private_imux;
 };
 
 static hda_nid_t stac9200_adc_nids[1] = {
@@ -88,14 +97,6 @@
         0x02,
 };
 
-static hda_nid_t stac9200_pstate_nids[3] = {
-	0x01, 0x02, 0x03,
-};
-
-static hda_nid_t stac9200_pin_nids[8] = {
-	0x08, 0x09, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
-};
-
 static hda_nid_t stac922x_adc_nids[2] = {
         0x06, 0x07,
 };
@@ -104,24 +105,22 @@
         0x12, 0x13,
 };
 
-static hda_nid_t stac922x_dac_nids[4] = {
-        0x02, 0x03, 0x04, 0x05,
-};
-
-static hda_nid_t stac922x_pstate_nids[8] = {
-	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x11,
+#ifdef STAC_TEST
+static hda_nid_t stac9200_pin_nids[8] = {
+	0x08, 0x09, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
 };
 
 static hda_nid_t stac922x_pin_nids[10] = {
 	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
 	0x0f, 0x10, 0x11, 0x15, 0x1b,
 };
+#endif
 
 static int stac92xx_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct sigmatel_spec *spec = codec->spec;
-	return snd_hda_input_mux_info(&spec->input_mux, uinfo);
+	return snd_hda_input_mux_info(spec->input_mux, uinfo);
 }
 
 static int stac92xx_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
@@ -140,26 +139,64 @@
 	struct sigmatel_spec *spec = codec->spec;
 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 
-	return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol,
+	return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
 				     spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]);
 }
 
-static struct hda_verb stac9200_ch2_init[] = {
+static struct hda_verb stac9200_core_init[] = {
 	/* set dac0mux for dac converter */
-	{ 0x07, 0x701, 0x00},
+	{ 0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{}
 };
 
-static struct hda_verb stac922x_ch2_init[] = {
+static struct hda_verb stac922x_core_init[] = {
 	/* set master volume and direct control */	
-	{ 0x16, 0x70f, 0xff},
+	{ 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
 	{}
 };
 
-struct sigmatel_channel_mode {
-	unsigned int channels;
-	const struct hda_verb *sequence;
-};
+static int stac922x_channel_modes[3] = {2, 6, 8};
+
+static int stac922x_ch_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = spec->num_ch_modes;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	sprintf(uinfo->value.enumerated.name, "%dch",
+		stac922x_channel_modes[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int stac922x_ch_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->cur_ch_mode;
+	return 0;
+}
+
+static int stac922x_ch_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+
+	if (ucontrol->value.enumerated.item[0] >= spec->num_ch_modes)
+		ucontrol->value.enumerated.item[0] = spec->num_ch_modes;
+	if (ucontrol->value.enumerated.item[0] == spec->cur_ch_mode &&
+	    ! codec->in_resume)
+		return 0;
+
+	spec->cur_ch_mode = ucontrol->value.enumerated.item[0];
+	spec->multiout.max_channels = stac922x_channel_modes[spec->cur_ch_mode];
+
+	return 1;
+}
 
 static snd_kcontrol_new_t stac9200_mixer[] = {
 	HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
@@ -174,13 +211,12 @@
 	},
 	HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Input Mux Volume", 0x0c, 0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Capture Mux Volume", 0x0c, 0, HDA_OUTPUT),
 	{ } /* end */
 };
 
+/* This needs to be generated dynamically based on sequence */
 static snd_kcontrol_new_t stac922x_mixer[] = {
-	HDA_CODEC_VOLUME("PCM Playback Volume", 0x2, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("PCM Playback Switch", 0x2, 0x0, HDA_OUTPUT),
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Input Source",
@@ -195,14 +231,38 @@
 	{ } /* end */
 };
 
+static snd_kcontrol_new_t stac922x_ch_mode_mixer[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = stac922x_ch_mode_info,
+		.get = stac922x_ch_mode_get,
+		.put = stac922x_ch_mode_put,
+	},
+	{ } /* end */
+};
+
 static int stac92xx_build_controls(struct hda_codec *codec)
 {
 	struct sigmatel_spec *spec = codec->spec;
 	int err;
+	int i;
 
 	err = snd_hda_add_new_ctls(codec, spec->mixer);
 	if (err < 0)
 		return err;
+
+	for (i = 0; i < spec->num_mixers; i++) {
+		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+		if (err < 0)
+			return err;
+	}
+
+	if (spec->surr_switch) {
+		err = snd_hda_add_new_ctls(codec, stac922x_ch_mode_mixer);
+		if (err < 0)
+			return err;
+	}
 	if (spec->multiout.dig_out_nid) {
 		err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
 		if (err < 0)
@@ -222,9 +282,9 @@
 	0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
 };
 
-static unsigned int stac922x_pin_configs[14] = {
-	0x40000100, 0x40000100, 0x40000100, 0x01114010,
-	0x01813122, 0x40000100, 0x01447010, 0x01c47010,
+static unsigned int stac922x_pin_configs[10] = {
+	0x01014010, 0x01014011, 0x01014012, 0x0221401f,
+	0x01813122, 0x01014014, 0x01441030, 0x01c41030,
 	0x40000100, 0x40000100,
 };
 
@@ -255,170 +315,6 @@
 }
 #endif
 
-static int stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, unsigned int value)
-{
-	unsigned int pin_ctl;
-
-	pin_ctl = snd_hda_codec_read(codec, nid, 0,
-				     AC_VERB_GET_PIN_WIDGET_CONTROL,
-				     0x00);
-	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-			    pin_ctl | value);
-
-	return 0;
-}
-
-static int stac92xx_set_vref(struct hda_codec *codec, hda_nid_t nid)
-{
-	unsigned int vref_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP) >> AC_PINCAP_VREF_SHIFT;
-	unsigned int vref_ctl = AC_PINCTL_VREF_HIZ;
-
-	if (vref_caps & AC_PINCAP_VREF_100)
-		vref_ctl = AC_PINCTL_VREF_100;
-	else if (vref_caps & AC_PINCAP_VREF_80)
-		vref_ctl = AC_PINCTL_VREF_80;
-	else if (vref_caps & AC_PINCAP_VREF_50)
-		vref_ctl = AC_PINCTL_VREF_50;
-	else if (vref_caps & AC_PINCAP_VREF_GRD)
-		vref_ctl = AC_PINCTL_VREF_GRD;
-
-	stac92xx_set_pinctl(codec, nid, vref_ctl);
-	
-	return 0;
-}
-
-/*
- * retrieve the default device type from the default config value
- */
-#define get_defcfg_type(cfg) ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
-#define get_defcfg_location(cfg) ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT)
-
-static int stac92xx_config_pin(struct hda_codec *codec, hda_nid_t nid, unsigned int pin_cfg)
-{
-	struct sigmatel_spec *spec = codec->spec;
-	u32 location = get_defcfg_location(pin_cfg);
-	char *label;
-	const char *type = NULL;
-	int ainput = 0;
-
-	switch(get_defcfg_type(pin_cfg)) {
-		case AC_JACK_HP_OUT:
-			/* Enable HP amp */
-			stac92xx_set_pinctl(codec, nid, AC_PINCTL_HP_EN);
-			/* Fall through */
-		case AC_JACK_SPDIF_OUT:
-		case AC_JACK_LINE_OUT:
-		case AC_JACK_SPEAKER:
-			/* Enable output */
-			stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
-			break;
-		case AC_JACK_SPDIF_IN:
-			stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
-			break;
-		case AC_JACK_MIC_IN:
-			if ((location & 0x0f) == AC_JACK_LOC_FRONT)
-				type = "Front Mic";
-			else
-				type = "Mic";
-			ainput = 1;
-			/* Set vref */
-			stac92xx_set_vref(codec, nid);
-			stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
-			break;
-		case AC_JACK_CD:
-			type = "CD";
-			ainput = 1;
-			stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
-			break;
-		case AC_JACK_LINE_IN:
-			if ((location & 0x0f) == AC_JACK_LOC_FRONT)
-				type = "Front Line";
-			else
-				type = "Line";
-			ainput = 1;
-			stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
-			break;
-		case AC_JACK_AUX:
-			if ((location & 0x0f) == AC_JACK_LOC_FRONT)
-				type = "Front Aux";
-			else
-				type = "Aux";
-			ainput = 1;
-			stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
-			break;
-	}
-
-	if (ainput) {
-		hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
-		int i, j, num_cons, index = -1;
-		if (!type)
-			type = "Input";
-		label = spec->input_labels[spec->input_mux.num_items];
-		strcpy(label, type);
-		spec->input_mux.items[spec->input_mux.num_items].label = label;
-		for (i=0; i<spec->num_muxes; i++) {
-			num_cons = snd_hda_get_connections(codec, spec->mux_nids[i], con_lst, HDA_MAX_NUM_INPUTS);
-			for (j=0; j<num_cons; j++)
-				if (con_lst[j] == nid) {
-					index = j;
-					break;
-				}
-			if (index >= 0)
-				break;
-		}
-		spec->input_mux.items[spec->input_mux.num_items].index = index;
-		spec->input_mux.num_items++;
-	}
-
-	return 0;
-}
-
-static int stac92xx_config_pins(struct hda_codec *codec)
-{
-	struct sigmatel_spec *spec = codec->spec;
-	int i;
-	unsigned int pin_cfg;
-
-	for (i=0; i < spec->num_pins; i++) {
-		/* Default to disabled */
-		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL,
-				    0x00);
-
-		pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0,
-					     AC_VERB_GET_CONFIG_DEFAULT,
-					     0x00);
-		if (((pin_cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) == AC_JACK_PORT_NONE)
-			continue;	/* Move on */
-
-		stac92xx_config_pin(codec, spec->pin_nids[i], pin_cfg);
-	}
-
-	return 0;
-}
-
-static int stac92xx_init(struct hda_codec *codec)
-{
-	struct sigmatel_spec *spec = codec->spec;
-	int i;
-
-	for (i=0; i < spec->num_pstates; i++)
-		snd_hda_codec_write(codec, spec->pstate_nids[i], 0,
-				    AC_VERB_SET_POWER_STATE, 0x00);
-
-	mdelay(100);
-
-	snd_hda_sequence_write(codec, spec->init);
-
-#ifdef STAC_TEST
-	stac92xx_set_config_regs(codec);
-#endif
-
-	stac92xx_config_pins(codec);
-
-	return 0;
-}
-
 /*
  * Analog playback callbacks
  */
@@ -430,6 +326,56 @@
 	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
 }
 
+/*
+ * set up the i/o for analog out
+ * when the digital out is available, copy the front out to digital out, too.
+ */
+static int stac92xx_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout,
+				     unsigned int stream_tag,
+				     unsigned int format,
+				     snd_pcm_substream_t *substream)
+{
+	hda_nid_t *nids = mout->dac_nids;
+	int chs = substream->runtime->channels;
+	int i;
+
+	down(&codec->spdif_mutex);
+	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
+		if (chs == 2 &&
+		    snd_hda_is_supported_format(codec, mout->dig_out_nid, format) &&
+		    ! (codec->spdif_status & IEC958_AES0_NONAUDIO)) {
+			mout->dig_out_used = HDA_DIG_ANALOG_DUP;
+			/* setup digital receiver */
+			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
+						   stream_tag, 0, format);
+		} else {
+			mout->dig_out_used = 0;
+			snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
+		}
+	}
+	up(&codec->spdif_mutex);
+
+	/* front */
+	snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format);
+	if (mout->hp_nid)
+		/* headphone out will just decode front left/right (stereo) */
+		snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format);
+	/* surrounds */
+	if (mout->max_channels > 2)
+		for (i = 1; i < mout->num_dacs; i++) {
+			if ((mout->max_channels == 6) && (i == 3))
+				break;
+			if (chs >= (i + 1) * 2) /* independent out */
+				snd_hda_codec_setup_stream(codec, nids[i], stream_tag, i * 2,
+						format);
+			else /* copy front */
+				snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0,
+						format);
+		}
+	return 0;
+}
+
+
 static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 					 struct hda_codec *codec,
 					 unsigned int stream_tag,
@@ -437,7 +383,7 @@
 					 snd_pcm_substream_t *substream)
 {
 	struct sigmatel_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+	return stac92xx_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
 						format, substream);
 }
 
@@ -516,7 +462,7 @@
 static struct hda_pcm_stream stac92xx_pcm_analog_playback = {
 	.substreams = 1,
 	.channels_min = 2,
-	.channels_max = 2,
+	.channels_max = 8,
 	.nid = 0x02, /* NID to query formats and rates */
 	.ops = {
 		.open = stac92xx_playback_pcm_open,
@@ -544,11 +490,9 @@
 	codec->num_pcms = 1;
 	codec->pcm_info = info;
 
-	info->name = "STAC92xx";
+	info->name = "STAC92xx Analog";
 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback;
-	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->playback_nid;
 	info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture;
-	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->capture_nid;
 
 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
 		codec->num_pcms++;
@@ -567,21 +511,413 @@
 	return 0;
 }
 
+enum {
+	STAC_CTL_WIDGET_VOL,
+	STAC_CTL_WIDGET_MUTE,
+};
+
+static snd_kcontrol_new_t stac92xx_control_templates[] = {
+	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+	HDA_CODEC_MUTE(NULL, 0, 0, 0),
+};
+
+/* add dynamic controls */
+static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char *name, unsigned long val)
+{
+	snd_kcontrol_new_t *knew;
+
+	if (spec->num_kctl_used >= spec->num_kctl_alloc) {
+		int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
+
+		knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
+		if (! knew)
+			return -ENOMEM;
+		if (spec->kctl_alloc) {
+			memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
+			kfree(spec->kctl_alloc);
+		}
+		spec->kctl_alloc = knew;
+		spec->num_kctl_alloc = num;
+	}
+
+	knew = &spec->kctl_alloc[spec->num_kctl_used];
+	*knew = stac92xx_control_templates[type];
+	knew->name = kstrdup(name, GFP_KERNEL);
+	if (! knew->name)
+		return -ENOMEM;
+	knew->private_value = val;
+	spec->num_kctl_used++;
+	return 0;
+}
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	hda_nid_t nid;
+	int i;
+
+	/* check the pins hardwired to audio widget */
+	for (i = 0; i < cfg->line_outs; i++) {
+		nid = cfg->line_out_pins[i];
+		spec->multiout.dac_nids[i] = snd_hda_codec_read(codec, nid, 0,
+					AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
+	}
+
+	spec->multiout.num_dacs = cfg->line_outs;
+
+	return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec, const struct auto_pin_cfg *cfg)
+{
+	char name[32];
+	static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
+	hda_nid_t nid;
+	int i, err;
+
+	for (i = 0; i < cfg->line_outs; i++) {
+		if (! spec->multiout.dac_nids[i])
+			continue;
+
+		nid = spec->multiout.dac_nids[i];
+
+		if (i == 2) {
+			/* Center/LFE */
+			if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, "Center Playback Volume",
+					       HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0)
+				return err;
+			if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, "LFE Playback Volume",
+					       HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
+				return err;
+			if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, "Center Playback Switch",
+					       HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0)
+				return err;
+			if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, "LFE Playback Switch",
+					       HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
+				return err;
+		} else {
+			sprintf(name, "%s Playback Volume", chname[i]);
+			if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name,
+					       HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+				return err;
+			sprintf(name, "%s Playback Switch", chname[i]);
+			if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name,
+					       HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+				return err;
+		}
+	}
+
+	return 0;
+}
+
+/* add playback controls for HP output */
+static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, struct auto_pin_cfg *cfg)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	hda_nid_t pin = cfg->hp_pin;
+	hda_nid_t nid;
+	int i, err;
+	unsigned int wid_caps;
+
+	if (! pin)
+		return 0;
+
+	wid_caps = snd_hda_param_read(codec, pin, AC_PAR_AUDIO_WIDGET_CAP);
+	if (wid_caps & AC_WCAP_UNSOL_CAP)
+		/* Enable unsolicited responses on the HP widget */
+		snd_hda_codec_write(codec, pin, 0,
+				AC_VERB_SET_UNSOLICITED_ENABLE,
+				STAC_UNSOL_ENABLE);
+
+	nid = snd_hda_codec_read(codec, pin, 0, AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
+	for (i = 0; i < cfg->line_outs; i++) {
+		if (! spec->multiout.dac_nids[i])
+			continue;
+		if (spec->multiout.dac_nids[i] == nid)
+			return 0;
+	}
+
+	spec->multiout.hp_nid = nid;
+
+	/* control HP volume/switch on the output mixer amp */
+	if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, "Headphone Playback Volume",
+					HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+		return err;
+	if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
+					HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+		return err;
+
+	return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	static char *labels[AUTO_PIN_LAST] = {
+		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
+	};
+	struct hda_input_mux *imux = &spec->private_imux;
+	hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
+	int i, j, k;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		int index = -1;
+		if (cfg->input_pins[i]) {
+			imux->items[imux->num_items].label = labels[i];
+
+			for (j=0; j<spec->num_muxes; j++) {
+				int num_cons = snd_hda_get_connections(codec, spec->mux_nids[j], con_lst, HDA_MAX_NUM_INPUTS);
+				for (k=0; k<num_cons; k++)
+					if (con_lst[k] == cfg->input_pins[i]) {
+						index = k;
+					 	break;
+					}
+				if (index >= 0)
+					break;
+			}
+			imux->items[imux->num_items].index = index;
+			imux->num_items++;
+		}
+	}
+
+	return 0;
+}
+
+static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
+
+{
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+}
+
+static void stac92xx_auto_init_multi_out(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->autocfg.line_outs; i++) {
+		hda_nid_t nid = spec->autocfg.line_out_pins[i];
+		stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
+	}
+}
+
+static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	hda_nid_t pin;
+
+	pin = spec->autocfg.hp_pin;
+	if (pin) /* connect to front */
+		stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+}
+
+static int stac922x_parse_auto_config(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int err;
+
+	if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg)) < 0)
+		return err;
+	if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
+		return err;
+	if (! spec->autocfg.line_outs && ! spec->autocfg.hp_pin)
+		return 0; /* can't find valid pin config */
+
+	if ((err = stac92xx_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
+	    (err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg)) < 0 ||
+	    (err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
+		return err;
+
+	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+	if (spec->multiout.max_channels > 2) {
+		spec->surr_switch = 1;
+		spec->cur_ch_mode = 1;
+		spec->num_ch_modes = 2;
+		if (spec->multiout.max_channels == 8) {
+			spec->cur_ch_mode++;
+			spec->num_ch_modes++;
+		}
+	}
+
+	if (spec->autocfg.dig_out_pin) {
+		spec->multiout.dig_out_nid = 0x08;
+		stac92xx_auto_set_pinctl(codec, spec->autocfg.dig_out_pin, AC_PINCTL_OUT_EN);
+	}
+	if (spec->autocfg.dig_in_pin) {
+		spec->dig_in_nid = 0x09;
+		stac92xx_auto_set_pinctl(codec, spec->autocfg.dig_in_pin, AC_PINCTL_IN_EN);
+	}
+
+	if (spec->kctl_alloc)
+		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+	spec->input_mux = &spec->private_imux;
+
+	return 1;
+}
+
+static int stac9200_parse_auto_config(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int err;
+
+	if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg)) < 0)
+		return err;
+
+	if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
+		return err;
+
+	if (spec->autocfg.dig_out_pin) {
+		spec->multiout.dig_out_nid = 0x05;
+		stac92xx_auto_set_pinctl(codec, spec->autocfg.dig_out_pin, AC_PINCTL_OUT_EN);
+	}
+	if (spec->autocfg.dig_in_pin) {
+		spec->dig_in_nid = 0x04;
+		stac92xx_auto_set_pinctl(codec, spec->autocfg.dig_in_pin, AC_PINCTL_IN_EN);
+	}
+
+	if (spec->kctl_alloc)
+		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+	spec->input_mux = &spec->private_imux;
+
+	return 1;
+}
+
+static int stac92xx_init_pstate(struct hda_codec *codec)
+{
+       hda_nid_t nid, nid_start;
+       int nodes;
+
+	snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_POWER_STATE, 0x00);
+
+       nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);
+       for (nid = nid_start; nid < nodes + nid_start; nid++) {
+               unsigned int wid_caps = snd_hda_param_read(codec, nid,
+                                                  AC_PAR_AUDIO_WIDGET_CAP);
+		if (wid_caps & AC_WCAP_POWER)
+			snd_hda_codec_write(codec, nid, 0,
+                                    AC_VERB_SET_POWER_STATE, 0x00);
+	}
+
+	mdelay(100);
+
+	return 0;
+}
+
+static int stac92xx_init(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+
+	stac92xx_init_pstate(codec);
+
+	snd_hda_sequence_write(codec, spec->init);
+
+	stac92xx_auto_init_multi_out(codec);
+	stac92xx_auto_init_hp_out(codec);
+
+	return 0;
+}
+
 static void stac92xx_free(struct hda_codec *codec)
 {
-	kfree(codec->spec);
+	struct sigmatel_spec *spec = codec->spec;
+	int i;
+
+	if (! spec)
+		return;
+
+	if (spec->kctl_alloc) {
+		for (i = 0; i < spec->num_kctl_used; i++)
+			kfree(spec->kctl_alloc[i].name);
+		kfree(spec->kctl_alloc);
+	}
+
+	kfree(spec);
 }
 
+static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
+				unsigned int flag)
+{
+	unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
+			0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
+	snd_hda_codec_write(codec, nid, 0,
+			AC_VERB_SET_PIN_WIDGET_CONTROL,
+			pin_ctl | flag);
+}
+
+static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
+				  unsigned int flag)
+{
+	unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
+			0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
+	snd_hda_codec_write(codec, nid, 0,
+			AC_VERB_SET_PIN_WIDGET_CONTROL,
+			pin_ctl & ~flag);
+}
+
+static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i, presence;
+
+	if ((res >> 26) != STAC_HP_EVENT)
+		return;
+
+	presence = snd_hda_codec_read(codec, cfg->hp_pin, 0,
+			AC_VERB_GET_PIN_SENSE, 0x00) >> 31;
+
+	if (presence) {
+		/* disable lineouts, enable hp */
+		for (i = 0; i < cfg->line_outs; i++)
+			stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
+						AC_PINCTL_OUT_EN);
+		stac92xx_set_pinctl(codec, cfg->hp_pin, AC_PINCTL_OUT_EN);
+	} else {
+		/* enable lineouts, disable hp */
+		for (i = 0; i < cfg->line_outs; i++)
+			stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
+						AC_PINCTL_OUT_EN);
+		stac92xx_reset_pinctl(codec, cfg->hp_pin, AC_PINCTL_OUT_EN);
+	}
+} 
+
+#ifdef CONFIG_PM
+static int stac92xx_resume(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int i;
+
+	stac92xx_init(codec);
+	for (i = 0; i < spec->num_mixers; i++)
+		snd_hda_resume_ctls(codec, spec->mixers[i]);
+	if (spec->multiout.dig_out_nid)
+		snd_hda_resume_spdif_out(codec);
+	if (spec->dig_in_nid)
+		snd_hda_resume_spdif_in(codec);
+
+	return 0;
+}
+#endif
+
 static struct hda_codec_ops stac92xx_patch_ops = {
 	.build_controls = stac92xx_build_controls,
 	.build_pcms = stac92xx_build_pcms,
 	.init = stac92xx_init,
 	.free = stac92xx_free,
+	.unsol_event = stac92xx_unsol_event,
+#ifdef CONFIG_PM
+	.resume = stac92xx_resume,
+#endif
 };
 
 static int patch_stac9200(struct hda_codec *codec)
 {
 	struct sigmatel_spec *spec;
+	int err;
 
 	spec  = kcalloc(1, sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
@@ -589,26 +925,27 @@
 
 	codec->spec = spec;
 
+#ifdef STAC_TEST
+	spec->pin_nids = stac9200_pin_nids;
+	spec->num_pins = 8;
+	spec->pin_configs = stac9200_pin_configs;
+	stac92xx_set_config_regs(codec);
+#endif
 	spec->multiout.max_channels = 2;
 	spec->multiout.num_dacs = 1;
 	spec->multiout.dac_nids = stac9200_dac_nids;
-	spec->multiout.dig_out_nid = 0x05;
-	spec->dig_in_nid = 0x04;
 	spec->adc_nids = stac9200_adc_nids;
 	spec->mux_nids = stac9200_mux_nids;
 	spec->num_muxes = 1;
-	spec->input_mux.num_items = 0;
-	spec->pstate_nids = stac9200_pstate_nids;
-	spec->num_pstates = 3;
-	spec->pin_nids = stac9200_pin_nids;
-#ifdef STAC_TEST
-	spec->pin_configs = stac9200_pin_configs;
-#endif
-	spec->num_pins = 8;
-	spec->init = stac9200_ch2_init;
+
+	spec->init = stac9200_core_init;
 	spec->mixer = stac9200_mixer;
-	spec->playback_nid = 0x02;
-	spec->capture_nid = 0x03;
+
+	err = stac9200_parse_auto_config(codec);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
+	}
 
 	codec->patch_ops = stac92xx_patch_ops;
 
@@ -618,6 +955,7 @@
 static int patch_stac922x(struct hda_codec *codec)
 {
 	struct sigmatel_spec *spec;
+	int err;
 
 	spec  = kcalloc(1, sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
@@ -625,26 +963,26 @@
 
 	codec->spec = spec;
 
-	spec->multiout.max_channels = 2;
-	spec->multiout.num_dacs = 4;
-	spec->multiout.dac_nids = stac922x_dac_nids;
-	spec->multiout.dig_out_nid = 0x08;
-	spec->dig_in_nid = 0x09;
+#ifdef STAC_TEST
+	spec->num_pins = 10;
+	spec->pin_nids = stac922x_pin_nids;
+	spec->pin_configs = stac922x_pin_configs;
+	stac92xx_set_config_regs(codec);
+#endif
 	spec->adc_nids = stac922x_adc_nids;
 	spec->mux_nids = stac922x_mux_nids;
 	spec->num_muxes = 2;
-	spec->input_mux.num_items = 0;
-	spec->pstate_nids = stac922x_pstate_nids;
-	spec->num_pstates = 8;
-	spec->pin_nids = stac922x_pin_nids;
-#ifdef STAC_TEST
-	spec->pin_configs = stac922x_pin_configs;
-#endif
-	spec->num_pins = 10;
-	spec->init = stac922x_ch2_init;
+
+	spec->init = stac922x_core_init;
 	spec->mixer = stac922x_mixer;
-	spec->playback_nid = 0x02;
-	spec->capture_nid = 0x06;
+
+	spec->multiout.dac_nids = spec->dac_nids;
+
+	err = stac922x_parse_auto_config(codec);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
+	}
 
 	codec->patch_ops = stac92xx_patch_ops;
 
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 28ac005..d7af3e4 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -424,6 +424,7 @@
 	unsigned xbox: 1;		/* workaround for Xbox AC'97 detection */
 
 	int spdif_idx;	/* SPDIF BAR index; *_SPBAR or -1 if use PCMOUT */
+	unsigned int sdm_saved;	/* SDM reg value */
 
 	ac97_bus_t *ac97_bus;
 	ac97_t *ac97[3];
@@ -2373,6 +2374,9 @@
 	for (i = 0; i < 3; i++)
 		if (chip->ac97[i])
 			snd_ac97_suspend(chip->ac97[i]);
+	if (chip->device_type == DEVICE_INTEL_ICH4)
+		chip->sdm_saved = igetbyte(chip, ICHREG(SDM));
+
 	if (chip->irq >= 0)
 		free_irq(chip->irq, (void *)chip);
 	pci_disable_device(chip->pci);
@@ -2390,6 +2394,16 @@
 	synchronize_irq(chip->irq);
 	snd_intel8x0_chip_init(chip, 1);
 
+	/* re-initialize mixer stuff */
+	if (chip->device_type == DEVICE_INTEL_ICH4) {
+		/* enable separate SDINs for ICH4 */
+		iputbyte(chip, ICHREG(SDM), chip->sdm_saved);
+		/* use slot 10/11 for SPDIF */
+		iputdword(chip, ICHREG(GLOB_CNT),
+			  (igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_PCM_SPDIF_MASK) |
+			  ICH_PCM_SPDIF_1011);
+	}
+
 	/* refill nocache */
 	if (chip->fix_nocache)
 		fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 1);
@@ -2455,8 +2469,7 @@
 	}
 	do_gettimeofday(&start_time);
 	spin_unlock_irq(&chip->reg_lock);
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout(HZ / 20);
+	msleep(50);
 	spin_lock_irq(&chip->reg_lock);
 	/* check the position */
 	pos = ichdev->fragsize1;
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 52c5859..39b5e7d 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -1050,11 +1050,6 @@
  * lowlevel functions
  */
 
-#define big_mdelay(msec) do {\
-	set_current_state(TASK_UNINTERRUPTIBLE);\
-	schedule_timeout(((msec) * HZ) / 1000);\
-} while (0)
-	
 static inline void snd_m3_outw(m3_t *chip, u16 value, unsigned long reg)
 {
 	outw(value, chip->iobase + reg);
@@ -1096,7 +1091,7 @@
 static void snd_m3_assp_halt(m3_t *chip)
 {
 	chip->reset_state = snd_m3_inb(chip, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK;
-	big_mdelay(10);
+	msleep(10);
 	snd_m3_outb(chip, chip->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B);
 }
 
@@ -2108,9 +2103,9 @@
 	 */
 	tmp = inw(io + RING_BUS_CTRL_A);
 	outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A);
-	big_mdelay(20);
+	msleep(20);
 	outw(tmp, io + RING_BUS_CTRL_A);
-	big_mdelay(50);
+	msleep(50);
 #endif
 }
 
@@ -2525,9 +2520,13 @@
 snd_m3_enable_ints(m3_t *chip)
 {
 	unsigned long io = chip->iobase;
+	unsigned short val;
 
 	/* TODO: MPU401 not supported yet */
-	outw(ASSP_INT_ENABLE | HV_INT_ENABLE /*| MPU401_INT_ENABLE*/, io + HOST_INT_CTRL);
+	val = ASSP_INT_ENABLE /*| MPU401_INT_ENABLE*/;
+	if (chip->hv_quirk && (chip->hv_quirk->config & HV_CTRL_ENABLE))
+		val |= HV_INT_ENABLE;
+	outw(val, io + HOST_INT_CTRL);
 	outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE,
 	     io + ASSP_CONTROL_C);
 }
@@ -2589,7 +2588,7 @@
 	snd_pcm_suspend_all(chip->pcm);
 	snd_ac97_suspend(chip->ac97);
 
-	big_mdelay(10); /* give the assp a chance to idle.. */
+	msleep(10); /* give the assp a chance to idle.. */
 
 	snd_m3_assp_halt(chip);
 
@@ -2697,6 +2696,8 @@
 	}
 
 	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->ac97_lock);
+
 	switch (pci->device) {
 	case PCI_DEVICE_ID_ESS_ALLEGRO:
 	case PCI_DEVICE_ID_ESS_ALLEGRO_1:
@@ -2765,6 +2766,8 @@
 	snd_m3_assp_init(chip);
 	snd_m3_amp_enable(chip, 1);
 
+	tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip);
+
 	if (request_irq(pci->irq, snd_m3_interrupt, SA_INTERRUPT|SA_SHIRQ,
 			card->driver, (void *)chip)) {
 		snd_printk("unable to grab IRQ %d\n", pci->irq);
@@ -2786,9 +2789,6 @@
 		return err;
 	}
 
-	spin_lock_init(&chip->ac97_lock);
-	tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip);
-
 	if ((err = snd_m3_mixer(chip)) < 0)
 		return err;
 
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index 082c0d0..6c868d9 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -445,9 +445,9 @@
 
 static int mixart_sync_nonblock_events(mixart_mgr_t *mgr)
 {
-	int timeout = HZ;
+	unsigned long timeout = jiffies + HZ;
 	while (atomic_read(&mgr->msg_processed) > 0) {
-		if (! timeout--) {
+		if (time_after(jiffies, timeout)) {
 			snd_printk(KERN_ERR "mixart: cannot process nonblock events!\n");
 			return -EBUSY;
 		}
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index a673cc4..796621d 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -445,6 +445,7 @@
 	u32                   control2_register;     /* cached value */
 	u32                   creg_spdif;
 	u32                   creg_spdif_stream;
+	int                   clock_source_locked;
 	char                 *card_name;	     /* digiface/multiface */
 	HDSP_IO_Type          io_type;               /* ditto, but for code use */
         unsigned short        firmware_rev;
@@ -678,8 +679,7 @@
 		}
 
 		if ((1000 / HZ) < 3000) {
-			set_current_state(TASK_UNINTERRUPTIBLE);
-			schedule_timeout((3000 * HZ + 999) / 1000);
+			ssleep(3);
 		} else {
 			mdelay(3000);
 		}
@@ -2095,6 +2095,34 @@
 	return change;
 }
 
+static int snd_hdsp_info_clock_source_lock(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_hdsp_get_clock_source_lock(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] = hdsp->clock_source_locked;
+	return 0;
+}
+
+static int snd_hdsp_put_clock_source_lock(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	hdsp_t *hdsp = snd_kcontrol_chip(kcontrol);
+	int change;
+
+	change = (int)ucontrol->value.integer.value[0] != hdsp->clock_source_locked;
+	if (change)
+		hdsp->clock_source_locked = ucontrol->value.integer.value[0];
+	return change;
+}
+
 #define HDSP_DA_GAIN(xname, xindex) \
 { .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
   .name = xname, \
@@ -3117,6 +3145,15 @@
 HDSP_SPDIF_NON_AUDIO("IEC958 Non-audio Bit", 0),
 /* 'Sample Clock Source' complies with the alsa control naming scheme */ 
 HDSP_CLOCK_SOURCE("Sample Clock Source", 0),
+{
+	/* FIXME: should be PCM or MIXER? */
+	/* .iface = SNDRV_CTL_ELEM_IFACE_PCM, */
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Sample Clock Source Locking",
+	.info = snd_hdsp_info_clock_source_lock,
+	.get = snd_hdsp_get_clock_source_lock,
+	.put = snd_hdsp_put_clock_source_lock,
+},
 HDSP_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
 HDSP_PREF_SYNC_REF("Preferred Sync Reference", 0),
 HDSP_AUTOSYNC_REF("AutoSync Reference", 0),
@@ -3349,6 +3386,7 @@
 	snd_iprintf (buffer, "System Clock Mode: %s\n", system_clock_mode);
 
 	snd_iprintf (buffer, "System Clock Frequency: %d\n", hdsp->system_sample_rate);
+	snd_iprintf (buffer, "System Clock Locked: %s\n", hdsp->clock_source_locked ? "Yes" : "No");
 		
 	snd_iprintf(buffer, "\n");
 
@@ -3853,13 +3891,14 @@
 	 */
 
 	spin_lock_irq(&hdsp->lock);
-	if ((err = hdsp_set_rate(hdsp, params_rate(params), 0)) < 0) {
-		spin_unlock_irq(&hdsp->lock);
-		_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
-		return err;
-	} else {
-		spin_unlock_irq(&hdsp->lock);
+	if (! hdsp->clock_source_locked) {
+		if ((err = hdsp_set_rate(hdsp, params_rate(params), 0)) < 0) {
+			spin_unlock_irq(&hdsp->lock);
+			_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
+			return err;
+		}
 	}
+	spin_unlock_irq(&hdsp->lock);
 
 	if ((err = hdsp_set_interrupt_interval(hdsp, params_period_size(params))) < 0) {
 		_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
@@ -4284,13 +4323,17 @@
 
 	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hdsp_hw_constraints_period_sizes);
-	if (hdsp->io_type == H9632) {
-		runtime->hw.channels_min = hdsp->qs_out_channels;
-		runtime->hw.channels_max = hdsp->ss_out_channels;
+	if (hdsp->clock_source_locked) {
+		runtime->hw.rate_min = runtime->hw.rate_max = hdsp->system_sample_rate;
+	} else if (hdsp->io_type == H9632) {
 		runtime->hw.rate_max = 192000;
 		runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
 		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hdsp_hw_constraints_9632_sample_rates);
 	}
+	if (hdsp->io_type == H9632) {
+		runtime->hw.channels_min = hdsp->qs_out_channels;
+		runtime->hw.channels_max = hdsp->ss_out_channels;
+	}	
 	
 	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
 			     snd_hdsp_hw_rule_out_channels, hdsp,
@@ -5036,8 +5079,7 @@
 	if (!is_9652 && !is_9632) {
 		/* we wait 2 seconds to let freshly inserted cardbus cards do their hardware init */
  		if ((1000 / HZ) < 2000) {
-			set_current_state(TASK_UNINTERRUPTIBLE);
-			schedule_timeout((2000 * HZ + 999) / 1000);
+			ssleep(2);
 		} else {
 			mdelay(2000);
 		}
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index a09b0fb4..29d89bfb 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -472,6 +472,7 @@
 		break;
 	default:
 		snd_BUG();
+		return;
 	}
 
 	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
@@ -3152,8 +3153,7 @@
 	switch (mode) {
 		case GAMEPORT_MODE_COOKED:
 			outb(GAMEPORT_MODE_ADC, TRID_REG(chip, GAMEPORT_GCR));
-			set_current_state(TASK_UNINTERRUPTIBLE);
-			schedule_timeout(1 + 20 * HZ / 1000); /* 20msec */
+			msleep(20);
 			return 0;
 		case GAMEPORT_MODE_RAW:
 			outb(0, TRID_REG(chip, GAMEPORT_GCR));
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 42c48f0..4889600 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -547,8 +547,7 @@
 	int err;
 	err = snd_via82xx_codec_ready(chip, ac97->num);
 	/* here we need to wait fairly for long time.. */
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout(HZ/2);
+	msleep(500);
 }
 
 static void snd_via82xx_codec_write(ac97_t *ac97,
@@ -1847,7 +1846,7 @@
 static int snd_via82xx_chip_init(via82xx_t *chip)
 {
 	unsigned int val;
-	int max_count;
+	unsigned long end_time;
 	unsigned char pval;
 
 #if 0 /* broken on K7M? */
@@ -1889,14 +1888,14 @@
 	}
 
 	/* wait until codec ready */
-	max_count = ((3 * HZ) / 4) + 1;
+	end_time = jiffies + msecs_to_jiffies(750);
 	do {
 		pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
 		if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
 			break;
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout(1);
-	} while (--max_count > 0);
+	} while (time_before(jiffies, end_time));
 
 	if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
 		snd_printk("AC'97 codec is not ready [0x%x]\n", val);
@@ -1905,7 +1904,7 @@
 	snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
 				 VIA_REG_AC97_SECONDARY_VALID |
 				 (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
-	max_count = ((3 * HZ) / 4) + 1;
+	end_time = jiffies + msecs_to_jiffies(750);
 	snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
 				 VIA_REG_AC97_SECONDARY_VALID |
 				 (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
@@ -1916,7 +1915,7 @@
 		}
 		set_current_state(TASK_INTERRUPTIBLE);
 		schedule_timeout(1);
-	} while (--max_count > 0);
+	} while (time_before(jiffies, end_time));
 	/* This is ok, the most of motherboards have only one codec */
 
       __ac97_ok2:
@@ -2178,7 +2177,7 @@
 		{ .subvendor = 0x147b, .subdevice = 0x1413, .action = VIA_DXS_ENABLE }, /* ABIT KV8 Pro */
 		{ .subvendor = 0x147b, .subdevice = 0x1415, .action = VIA_DXS_NO_VRA }, /* Abit AV8 */
 		{ .subvendor = 0x14ff, .subdevice = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */
-		{ .subvendor = 0x14ff, .subdevice = 0x0408, .action = VIA_DXS_NO_VRA }, /* Twinhead mobo */
+		{ .subvendor = 0x14ff, .subdevice = 0x0408, .action = VIA_DXS_SRC }, /* Twinhead laptop */
 		{ .subvendor = 0x1584, .subdevice = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */
 		{ .subvendor = 0x1584, .subdevice = 0x8123, .action = VIA_DXS_NO_VRA }, /* Uniwill (Targa Visionary XP-210) */
 		{ .subvendor = 0x161f, .subdevice = 0x202b, .action = VIA_DXS_NO_VRA }, /* Amira Note book */
@@ -2187,6 +2186,7 @@
 		{ .subvendor = 0x1695, .subdevice = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */
 		{ .subvendor = 0x1849, .subdevice = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */
 		{ .subvendor = 0x1919, .subdevice = 0x200a, .action = VIA_DXS_NO_VRA }, /* Soltek SL-K8Tpro-939 */
+		{ .subvendor = 0x4005, .subdevice = 0x4710, .action = VIA_DXS_SRC },	/* MSI K7T266 Pro2 (MS-6380 V2.0) BIOS 3.7 */
 		{ } /* terminator */
 	};
 	struct dxs_whitelist *w;
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index 5896d28..4a9779c 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -408,8 +408,7 @@
 	int err;
 	err = snd_via82xx_codec_ready(chip, ac97->num);
 	/* here we need to wait fairly for long time.. */
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout(HZ/2);
+	msleep(500);
 }
 
 static void snd_via82xx_codec_write(ac97_t *ac97,
@@ -923,7 +922,7 @@
 static int snd_via82xx_chip_init(via82xx_t *chip)
 {
 	unsigned int val;
-	int max_count;
+	unsigned long end_time;
 	unsigned char pval;
 
 	pci_read_config_byte(chip->pci, VIA_MC97_CTRL, &pval);
@@ -962,14 +961,14 @@
 	}
 
 	/* wait until codec ready */
-	max_count = ((3 * HZ) / 4) + 1;
+	end_time = jiffies + msecs_to_jiffies(750);
 	do {
 		pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
 		if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
 			break;
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout(1);
-	} while (--max_count > 0);
+	} while (time_before(jiffies, end_time));
 
 	if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
 		snd_printk("AC'97 codec is not ready [0x%x]\n", val);
@@ -977,7 +976,7 @@
 	snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
 				 VIA_REG_AC97_SECONDARY_VALID |
 				 (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
-	max_count = ((3 * HZ) / 4) + 1;
+	end_time = jiffies + msecs_to_jiffies(750);
 	snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
 				 VIA_REG_AC97_SECONDARY_VALID |
 				 (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
@@ -988,7 +987,7 @@
 		}
 		set_current_state(TASK_INTERRUPTIBLE);
 		schedule_timeout(1);
-	} while (--max_count > 0);
+	} while (time_before(jiffies, end_time));
 	/* This is ok, the most of motherboards have only one codec */
 
       __ac97_ok2:
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 2ae79610..d54f88a 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -84,16 +84,16 @@
 
 static int snd_ymfpci_codec_ready(ymfpci_t *chip, int secondary)
 {
-	signed long end_time;
+	unsigned long end_time;
 	u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR;
 	
-	end_time = (jiffies + ((3 * HZ) / 4)) + 1;
+	end_time = jiffies + msecs_to_jiffies(750);
 	do {
 		if ((snd_ymfpci_readw(chip, reg) & 0x8000) == 0)
 			return 0;
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout(1);
-	} while (end_time - (signed long)jiffies >= 0);
+	} while (time_before(jiffies, end_time));
 	snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_ymfpci_readw(chip, reg));
 	return -EBUSY;
 }
diff --git a/sound/pcmcia/Kconfig b/sound/pcmcia/Kconfig
index 3611e29..5d1b0b7 100644
--- a/sound/pcmcia/Kconfig
+++ b/sound/pcmcia/Kconfig
@@ -8,23 +8,12 @@
 	depends on SND && PCMCIA && ISA
 	select SND_VX_LIB
 	help
-	  Say Y here to include support for Digigram VXpocket
-	  soundcards.
+	  Say Y here to include support for Digigram VXpocket and
+	  VXpocket 440 soundcards.
 
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-vxpocket.
 
-config SND_VXP440
-	tristate "Digigram VXpocket 440"
-	depends on SND && PCMCIA && ISA
-	select SND_VX_LIB
-	help
-	  Say Y here to include support for Digigram VXpocket 440
-	  soundcards.
-
-	  To compile this driver as a module, choose M here: the module
-	  will be called snd-vxp440.
-
 config SND_PDAUDIOCF
 	tristate "Sound Core PDAudioCF"
 	depends on SND && PCMCIA && ISA
diff --git a/sound/pcmcia/vx/Makefile b/sound/pcmcia/vx/Makefile
index f35dfa1..54971f0 100644
--- a/sound/pcmcia/vx/Makefile
+++ b/sound/pcmcia/vx/Makefile
@@ -3,9 +3,6 @@
 # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
 #
 
-snd-vx-cs-objs := vx_entry.o vxp_ops.o vxp_mixer.o
-snd-vxpocket-objs := vxpocket.o
-snd-vxp440-objs := vxp440.o
+snd-vxpocket-objs := vxpocket.o vxp_ops.o vxp_mixer.o
 
-obj-$(CONFIG_SND_VXPOCKET) += snd-vxpocket.o snd-vx-cs.o
-obj-$(CONFIG_SND_VXP440) += snd-vxp440.o snd-vx-cs.o
+obj-$(CONFIG_SND_VXPOCKET) += snd-vxpocket.o
diff --git a/sound/pcmcia/vx/vx_entry.c b/sound/pcmcia/vx/vx_entry.c
deleted file mode 100644
index df7a39b..0000000
--- a/sound/pcmcia/vx/vx_entry.c
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * Driver for Digigram VXpocket soundcards
- *
- * PCMCIA entry part
- *
- * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- */
-
-#include <sound/driver.h>
-#include <sound/core.h>
-#include "vxpocket.h"
-#include <pcmcia/ciscode.h>
-#include <pcmcia/cisreg.h>
-
-
-MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
-MODULE_DESCRIPTION("Common routines for Digigram PCMCIA VX drivers");
-MODULE_LICENSE("GPL");
-
-/*
- * prototypes
- */
-static void vxpocket_config(dev_link_t *link);
-
-
-static void vxpocket_release(dev_link_t *link)
-{
-	if (link->state & DEV_CONFIG) {
-		/* release cs resources */
-		pcmcia_release_configuration(link->handle);
-		pcmcia_release_io(link->handle, &link->io);
-		pcmcia_release_irq(link->handle, &link->irq);
-		link->state &= ~DEV_CONFIG;
-	}
-}
-
-/*
- * destructor
- */
-static int snd_vxpocket_free(vx_core_t *chip)
-{
-	struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
-	struct snd_vxp_entry *hw;
-	dev_link_t *link = &vxp->link;
-
-	vxpocket_release(link);
-
-	/* Break the link with Card Services */
-	if (link->handle)
-		pcmcia_deregister_client(link->handle);
-
-	hw = vxp->hw_entry;
-	if (hw)
-		hw->card_list[vxp->index] = NULL;
-	chip->card = NULL;
-	kfree(chip->dev);
-
-	snd_vx_free_firmware(chip);
-	kfree(chip);
-	return 0;
-}
-
-static int snd_vxpocket_dev_free(snd_device_t *device)
-{
-	vx_core_t *chip = device->device_data;
-	return snd_vxpocket_free(chip);
-}
-
-/*
- * snd_vxpocket_attach - attach callback for cs
- * @hw: the hardware information
- */
-dev_link_t *snd_vxpocket_attach(struct snd_vxp_entry *hw)
-{
-	client_reg_t client_reg;	/* Register with cardmgr */
-	dev_link_t *link;		/* Info for cardmgr */
-	int i, ret;
-	vx_core_t *chip;
-	struct snd_vxpocket *vxp;
-	snd_card_t *card;
-	static snd_device_ops_t ops = {
-		.dev_free =	snd_vxpocket_dev_free,
-	};
-
-	snd_printdd(KERN_DEBUG "vxpocket_attach called\n");
-	/* find an empty slot from the card list */
-	for (i = 0; i < SNDRV_CARDS; i++) {
-		if (! hw->card_list[i])
-			break;
-	}
-	if (i >= SNDRV_CARDS) {
-		snd_printk(KERN_ERR "vxpocket: too many cards found\n");
-		return NULL;
-	}
-	if (! hw->enable_table[i])
-		return NULL; /* disabled explicitly */
-
-	/* ok, create a card instance */
-	card = snd_card_new(hw->index_table[i], hw->id_table[i], THIS_MODULE, 0);
-	if (card == NULL) {
-		snd_printk(KERN_ERR "vxpocket: cannot create a card instance\n");
-		return NULL;
-	}
-
-	chip = snd_vx_create(card, hw->hardware, hw->ops,
-			     sizeof(struct snd_vxpocket) - sizeof(vx_core_t));
-	if (! chip)
-		return NULL;
-
-#ifdef SND_VX_FW_LOADER
-	/* fake a device here since pcmcia doesn't give a valid device... */
-	chip->dev = kcalloc(1, sizeof(*chip->dev), GFP_KERNEL);
-	if (! chip->dev) {
-		snd_printk(KERN_ERR "vxp: can't malloc chip->dev\n");
-		kfree(chip);
-		snd_card_free(card);
-		return NULL;
-	}
-	device_initialize(chip->dev);
-	sprintf(chip->dev->bus_id, "vxpocket%d", i);
-#endif
-
-	if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops) < 0) {
-		kfree(chip);
-		snd_card_free(card);
-		return NULL;
-	}
-
-	vxp = (struct snd_vxpocket *)chip;
-	vxp->index = i;
-	vxp->hw_entry = hw;
-	chip->ibl.size = hw->ibl[i];
-	hw->card_list[i] = chip;
-
-	link = &vxp->link;
-	link->priv = chip;
-
-	link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
-	link->io.NumPorts1 = 16;
-
-	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
-	// link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
-
-	link->irq.IRQInfo1 = IRQ_LEVEL_ID;
-	link->irq.Handler = &snd_vx_irq_handler;
-	link->irq.Instance = chip;
-
-	link->conf.Attributes = CONF_ENABLE_IRQ;
-	link->conf.Vcc = 50;
-	link->conf.IntType = INT_MEMORY_AND_IO;
-	link->conf.ConfigIndex = 1;
-	link->conf.Present = PRESENT_OPTION;
-
-	/* Register with Card Services */
-	memset(&client_reg, 0, sizeof(client_reg));
-	client_reg.dev_info = hw->dev_info;
-	client_reg.Version = 0x0210;
-	client_reg.event_callback_args.client_data = link;
-
-	ret = pcmcia_register_client(&link->handle, &client_reg);
-	if (ret != CS_SUCCESS) {
-		cs_error(link->handle, RegisterClient, ret);
-		snd_card_free(card);
-		return NULL;
-	}
-
-	/* Chain drivers */
-	link->next = hw->dev_list;
-	hw->dev_list = link;
-
-	/* snd_card_set_pm_callback(card, snd_vxpocket_suspend, snd_vxpocket_resume, chip); */
-
-	return link;
-}
-
-
-/**
- * snd_vxpocket_assign_resources - initialize the hardware and card instance.
- * @port: i/o port for the card
- * @irq: irq number for the card
- *
- * this function assigns the specified port and irq, boot the card,
- * create pcm and control instances, and initialize the rest hardware.
- *
- * returns 0 if successful, or a negative error code.
- */
-static int snd_vxpocket_assign_resources(vx_core_t *chip, int port, int irq)
-{
-	int err;
-	snd_card_t *card = chip->card;
-	struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
-
-	snd_printdd(KERN_DEBUG "vxpocket assign resources: port = 0x%x, irq = %d\n", port, irq);
-	vxp->port = port;
-
-	sprintf(card->shortname, "Digigram %s", card->driver);
-	sprintf(card->longname, "%s at 0x%x, irq %i",
-		card->shortname, port, irq);
-
-	chip->irq = irq;
-
-	if ((err = snd_vx_setup_firmware(chip)) < 0)
-		return err;
-
-	return 0;
-}
-
-
-/*
- * snd_vxpocket_detach - detach callback for cs
- * @hw: the hardware information
- */
-void snd_vxpocket_detach(struct snd_vxp_entry *hw, dev_link_t *link)
-{
-	vx_core_t *chip;
-
-	if (! link)
-		return;
-
-	chip = link->priv;
-
-	snd_printdd(KERN_DEBUG "vxpocket_detach called\n");
-	/* Remove the interface data from the linked list */
-	if (hw) {
-		dev_link_t **linkp;
-		/* Locate device structure */
-		for (linkp = &hw->dev_list; *linkp; linkp = &(*linkp)->next)
-			if (*linkp == link) {
-				*linkp = link->next;
-				break;
-			}
-	}
-	chip->chip_status |= VX_STAT_IS_STALE; /* to be sure */
-	snd_card_disconnect(chip->card);
-	snd_card_free_in_thread(chip->card);
-}
-
-/*
- * configuration callback
- */
-
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
-static void vxpocket_config(dev_link_t *link)
-{
-	client_handle_t handle = link->handle;
-	vx_core_t *chip = link->priv;
-	struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
-	tuple_t tuple;
-	cisparse_t *parse = NULL;
-	u_short buf[32];
-	int last_fn, last_ret;
-
-	snd_printdd(KERN_DEBUG "vxpocket_config called\n");
-	parse = kmalloc(sizeof(*parse), GFP_KERNEL);
-	if (! parse) {
-		snd_printk(KERN_ERR "vx: cannot allocate\n");
-		return;
-	}
-	tuple.Attributes = 0;
-	tuple.TupleData = (cisdata_t *)buf;
-	tuple.TupleDataMax = sizeof(buf);
-	tuple.TupleOffset = 0;
-	tuple.DesiredTuple = CISTPL_CONFIG;
-	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-	CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-	CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, parse));
-	link->conf.ConfigBase = parse->config.base;
-	link->conf.Present = parse->config.rmask[0];
-
-	/* Configure card */
-	link->state |= DEV_CONFIG;
-
-	CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io));
-	CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
-	CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
-
-	if (snd_vxpocket_assign_resources(chip, link->io.BasePort1, link->irq.AssignedIRQ) < 0)
-		goto failed;
-
-	link->dev = &vxp->node;
-	link->state &= ~DEV_CONFIG_PENDING;
-	kfree(parse);
-	return;
-
-cs_failed:
-	cs_error(link->handle, last_fn, last_ret);
-failed:
-	pcmcia_release_configuration(link->handle);
-	pcmcia_release_io(link->handle, &link->io);
-	pcmcia_release_irq(link->handle, &link->irq);
-	link->state &= ~DEV_CONFIG;
-	kfree(parse);
-}
-
-
-/*
- * event callback
- */
-int vxpocket_event(event_t event, int priority, event_callback_args_t *args)
-{
-	dev_link_t *link = args->client_data;
-	vx_core_t *chip = link->priv;
-
-	switch (event) {
-	case CS_EVENT_CARD_REMOVAL:
-		snd_printdd(KERN_DEBUG "CARD_REMOVAL..\n");
-		link->state &= ~DEV_PRESENT;
-		if (link->state & DEV_CONFIG) {
-			chip->chip_status |= VX_STAT_IS_STALE;
-		}
-		break;
-	case CS_EVENT_CARD_INSERTION:
-		snd_printdd(KERN_DEBUG "CARD_INSERTION..\n");
-		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-		vxpocket_config(link);
-		break;
-#ifdef CONFIG_PM
-	case CS_EVENT_PM_SUSPEND:
-		snd_printdd(KERN_DEBUG "SUSPEND\n");
-		link->state |= DEV_SUSPEND;
-		if (chip && chip->card->pm_suspend) {
-			snd_printdd(KERN_DEBUG "snd_vx_suspend calling\n");
-			chip->card->pm_suspend(chip->card, PMSG_SUSPEND);
-		}
-		/* Fall through... */
-	case CS_EVENT_RESET_PHYSICAL:
-		snd_printdd(KERN_DEBUG "RESET_PHYSICAL\n");
-		if (link->state & DEV_CONFIG)
-			pcmcia_release_configuration(link->handle);
-		break;
-	case CS_EVENT_PM_RESUME:
-		snd_printdd(KERN_DEBUG "RESUME\n");
-		link->state &= ~DEV_SUSPEND;
-		/* Fall through... */
-	case CS_EVENT_CARD_RESET:
-		snd_printdd(KERN_DEBUG "CARD_RESET\n");
-		if (DEV_OK(link)) {
-			//struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
-			snd_printdd(KERN_DEBUG "requestconfig...\n");
-			pcmcia_request_configuration(link->handle, &link->conf);
-			if (chip && chip->card->pm_resume) {
-				snd_printdd(KERN_DEBUG "calling snd_vx_resume\n");
-				chip->card->pm_resume(chip->card);
-			}
-		}
-		snd_printdd(KERN_DEBUG "resume done!\n");
-		break;
-#endif
-	}
-	return 0;
-}
-
-/*
- * exported stuffs
- */
-EXPORT_SYMBOL(snd_vxpocket_ops);
-EXPORT_SYMBOL(snd_vxpocket_attach);
-EXPORT_SYMBOL(vxpocket_event);
-EXPORT_SYMBOL(snd_vxpocket_detach);
diff --git a/sound/pcmcia/vx/vxp440.c b/sound/pcmcia/vx/vxp440.c
deleted file mode 100644
index 59190a8..0000000
--- a/sound/pcmcia/vx/vxp440.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#define COMPILE_VXP440
-
-/*
- add the following as /etc/pcmcia/vxp440.conf:
-
-  device "snd-vxp440"
-    class "audio" module "snd-vxp440"
-
-  card "Digigram VX-POCKET440"
-    manfid 0x01f1, 0x0100
-    bind "snd-vxp440"
-*/
-
-#include "vxpocket.c"
diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c
index 62d6fa1..3a82161 100644
--- a/sound/pcmcia/vx/vxpocket.c
+++ b/sound/pcmcia/vx/vxpocket.c
@@ -24,21 +24,17 @@
 #include <linux/moduleparam.h>
 #include <sound/core.h>
 #include "vxpocket.h"
+#include <pcmcia/ciscode.h>
+#include <pcmcia/cisreg.h>
 #include <sound/initval.h>
 
 /*
  */
 
-#ifdef COMPILE_VXP440
-#define CARD_NAME	"VXPocket440"
-#else
-#define CARD_NAME	"VXPocket"
-#endif
-
 MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
-MODULE_DESCRIPTION("Digigram " CARD_NAME);
+MODULE_DESCRIPTION("Digigram VXPocket");
 MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}");
+MODULE_SUPPORTED_DEVICE("{{Digigram,VXPocket},{Digigram,VXPocket440}}");
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
@@ -46,82 +42,405 @@
 static int ibl[SNDRV_CARDS];
 
 module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+MODULE_PARM_DESC(index, "Index value for VXPocket soundcard.");
 module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+MODULE_PARM_DESC(id, "ID string for VXPocket soundcard.");
 module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+MODULE_PARM_DESC(enable, "Enable VXPocket soundcard.");
 module_param_array(ibl, int, NULL, 0444);
-MODULE_PARM_DESC(ibl, "Capture IBL size for " CARD_NAME " soundcard.");
+MODULE_PARM_DESC(ibl, "Capture IBL size for VXPocket soundcard.");
  
 
 /*
  */
 
-#ifdef COMPILE_VXP440
+static unsigned int card_alloc;
+static dev_link_t *dev_list;		/* Linked list of devices */
+static dev_info_t dev_info = "snd-vxpocket";
 
-/* 1 DSP, 1 sync UER, 1 sync World Clock (NIY) */
-/* SMPTE (NIY) */
-/* 2 stereo analog input (line/micro) */
-/* 2 stereo analog output */
-/* Only output levels can be modified */
-/* UER, but only for the first two inputs and outputs. */
 
-#define NUM_CODECS	2
-#define CARD_TYPE	VX_TYPE_VXP440
-#define DEV_INFO	"snd-vxp440"
+static int vxpocket_event(event_t event, int priority, event_callback_args_t *args);
 
-#else
 
-/* 1 DSP, 1 sync UER */
-/* 1 programmable clock (NIY) */
-/* 1 stereo analog input (line/micro) */
-/* 1 stereo analog output */
-/* Only output levels can be modified */
+/*
+ */
+static void vxpocket_release(dev_link_t *link)
+{
+	if (link->state & DEV_CONFIG) {
+		/* release cs resources */
+		pcmcia_release_configuration(link->handle);
+		pcmcia_release_io(link->handle, &link->io);
+		pcmcia_release_irq(link->handle, &link->irq);
+		link->state &= ~DEV_CONFIG;
+	}
+	if (link->handle) {
+		/* Break the link with Card Services */
+		pcmcia_deregister_client(link->handle);
+		link->handle = NULL;
+	}
+}
 
-#define NUM_CODECS	1
-#define CARD_TYPE	VX_TYPE_VXPOCKET
-#define DEV_INFO	"snd-vxpocket"
+/*
+ * destructor, called from snd_card_free_in_thread()
+ */
+static int snd_vxpocket_dev_free(snd_device_t *device)
+{
+	vx_core_t *chip = device->device_data;
 
-#endif
+	snd_vx_free_firmware(chip);
+	kfree(chip);
+	return 0;
+}
 
-static dev_info_t dev_info = DEV_INFO;
 
-static struct snd_vx_hardware vxp_hw = {
-	.name = CARD_NAME,
-	.type = CARD_TYPE,
+/*
+ * Hardware information
+ */
+
+/* VX-pocket V2
+ *
+ * 1 DSP, 1 sync UER
+ * 1 programmable clock (NIY)
+ * 1 stereo analog input (line/micro)
+ * 1 stereo analog output
+ * Only output levels can be modified
+ */
+
+static struct snd_vx_hardware vxpocket_hw = {
+	.name = "VXPocket",
+	.type = VX_TYPE_VXPOCKET,
 
 	/* hardware specs */
-	.num_codecs = NUM_CODECS,
-	.num_ins = NUM_CODECS,
-	.num_outs = NUM_CODECS,
+	.num_codecs = 1,
+	.num_ins = 1,
+	.num_outs = 1,
 	.output_level_max = VX_ANALOG_OUT_LEVEL_MAX,
 };	
 
-static struct snd_vxp_entry hw_entry = {
-	.dev_info = &dev_info,
+/* VX-pocket 440
+ *
+ * 1 DSP, 1 sync UER, 1 sync World Clock (NIY)
+ * SMPTE (NIY)
+ * 2 stereo analog input (line/micro)
+ * 2 stereo analog output
+ * Only output levels can be modified
+ * UER, but only for the first two inputs and outputs.
+ */
 
-	/* module parameters */
-	.index_table = index,
-	.id_table = id,
-	.enable_table = enable,
-	.ibl = ibl,
+static struct snd_vx_hardware vxp440_hw = {
+	.name = "VXPocket440",
+	.type = VX_TYPE_VXP440,
 
-	/* h/w config */
-	.hardware = &vxp_hw,
-	.ops = &snd_vxpocket_ops,
-};
+	/* hardware specs */
+	.num_codecs = 2,
+	.num_ins = 2,
+	.num_outs = 2,
+	.output_level_max = VX_ANALOG_OUT_LEVEL_MAX,
+};	
+
+
+/*
+ * create vxpocket instance
+ */
+static struct snd_vxpocket *snd_vxpocket_new(snd_card_t *card, int ibl)
+{
+	client_reg_t client_reg;	/* Register with cardmgr */
+	dev_link_t *link;		/* Info for cardmgr */
+	vx_core_t *chip;
+	struct snd_vxpocket *vxp;
+	int ret;
+	static snd_device_ops_t ops = {
+		.dev_free =	snd_vxpocket_dev_free,
+	};
+
+	chip = snd_vx_create(card, &vxpocket_hw, &snd_vxpocket_ops,
+			     sizeof(struct snd_vxpocket) - sizeof(vx_core_t));
+	if (! chip)
+		return NULL;
+
+	if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops) < 0) {
+		kfree(chip);
+		return NULL;
+	}
+	chip->ibl.size = ibl;
+
+	vxp = (struct snd_vxpocket *)chip;
+
+	link = &vxp->link;
+	link->priv = chip;
+
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+	link->io.NumPorts1 = 16;
+
+	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+
+	link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+	link->irq.Handler = &snd_vx_irq_handler;
+	link->irq.Instance = chip;
+
+	link->conf.Attributes = CONF_ENABLE_IRQ;
+	link->conf.Vcc = 50;
+	link->conf.IntType = INT_MEMORY_AND_IO;
+	link->conf.ConfigIndex = 1;
+	link->conf.Present = PRESENT_OPTION;
+
+	/* Register with Card Services */
+	memset(&client_reg, 0, sizeof(client_reg));
+	client_reg.dev_info = &dev_info;
+	client_reg.EventMask = 
+		CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL
+#ifdef CONFIG_PM
+		| CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET
+		| CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME
+#endif
+		;
+	client_reg.event_handler = &vxpocket_event;
+	client_reg.Version = 0x0210;
+	client_reg.event_callback_args.client_data = link;
+
+	ret = pcmcia_register_client(&link->handle, &client_reg);
+	if (ret != CS_SUCCESS) {
+		cs_error(link->handle, RegisterClient, ret);
+		return NULL;
+	}
+
+	return vxp;
+}
+
+
+/**
+ * snd_vxpocket_assign_resources - initialize the hardware and card instance.
+ * @port: i/o port for the card
+ * @irq: irq number for the card
+ *
+ * this function assigns the specified port and irq, boot the card,
+ * create pcm and control instances, and initialize the rest hardware.
+ *
+ * returns 0 if successful, or a negative error code.
+ */
+static int snd_vxpocket_assign_resources(vx_core_t *chip, int port, int irq)
+{
+	int err;
+	snd_card_t *card = chip->card;
+	struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
+
+	snd_printdd(KERN_DEBUG "vxpocket assign resources: port = 0x%x, irq = %d\n", port, irq);
+	vxp->port = port;
+
+	sprintf(card->shortname, "Digigram %s", card->driver);
+	sprintf(card->longname, "%s at 0x%x, irq %i",
+		card->shortname, port, irq);
+
+	chip->irq = irq;
+
+	if ((err = snd_vx_setup_firmware(chip)) < 0)
+		return err;
+
+	return 0;
+}
+
+
+/*
+ * configuration callback
+ */
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void vxpocket_config(dev_link_t *link)
+{
+	client_handle_t handle = link->handle;
+	vx_core_t *chip = link->priv;
+	struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
+	tuple_t tuple;
+	cisparse_t *parse;
+	u_short buf[32];
+	int last_fn, last_ret;
+
+	snd_printdd(KERN_DEBUG "vxpocket_config called\n");
+	parse = kmalloc(sizeof(*parse), GFP_KERNEL);
+	if (! parse) {
+		snd_printk(KERN_ERR "vx: cannot allocate\n");
+		return;
+	}
+	tuple.Attributes = 0;
+	tuple.TupleData = (cisdata_t *)buf;
+	tuple.TupleDataMax = sizeof(buf);
+	tuple.TupleOffset = 0;
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+	CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+	CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, parse));
+	link->conf.ConfigBase = parse->config.base;
+	link->conf.Present = parse->config.rmask[0];
+
+	/* redefine hardware record according to the VERSION1 string */
+	tuple.DesiredTuple = CISTPL_VERS_1;
+	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+	CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+	CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, parse));
+	if (! strcmp(parse->version_1.str + parse->version_1.ofs[1], "VX-POCKET")) {
+		snd_printdd("VX-pocket is detected\n");
+	} else {
+		snd_printdd("VX-pocket 440 is detected\n");
+		/* overwrite the hardware information */
+		chip->hw = &vxp440_hw;
+		chip->type = vxp440_hw.type;
+		strcpy(chip->card->driver, vxp440_hw.name);
+	}
+
+	/* Configure card */
+	link->state |= DEV_CONFIG;
+
+	CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io));
+	CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+	CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+
+	chip->dev = &handle_to_dev(link->handle);
+
+	if (snd_vxpocket_assign_resources(chip, link->io.BasePort1, link->irq.AssignedIRQ) < 0)
+		goto failed;
+
+	link->dev = &vxp->node;
+	link->state &= ~DEV_CONFIG_PENDING;
+	kfree(parse);
+	return;
+
+cs_failed:
+	cs_error(link->handle, last_fn, last_ret);
+failed:
+	pcmcia_release_configuration(link->handle);
+	pcmcia_release_io(link->handle, &link->io);
+	pcmcia_release_irq(link->handle, &link->irq);
+	link->state &= ~DEV_CONFIG;
+	kfree(parse);
+}
+
+
+/*
+ * event callback
+ */
+static int vxpocket_event(event_t event, int priority, event_callback_args_t *args)
+{
+	dev_link_t *link = args->client_data;
+	vx_core_t *chip = link->priv;
+
+	switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+		snd_printdd(KERN_DEBUG "CARD_REMOVAL..\n");
+		link->state &= ~DEV_PRESENT;
+		if (link->state & DEV_CONFIG)
+			chip->chip_status |= VX_STAT_IS_STALE;
+		break;
+	case CS_EVENT_CARD_INSERTION:
+		snd_printdd(KERN_DEBUG "CARD_INSERTION..\n");
+		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+		vxpocket_config(link);
+		break;
+#ifdef CONFIG_PM
+	case CS_EVENT_PM_SUSPEND:
+		snd_printdd(KERN_DEBUG "SUSPEND\n");
+		link->state |= DEV_SUSPEND;
+		if (chip && chip->card->pm_suspend) {
+			snd_printdd(KERN_DEBUG "snd_vx_suspend calling\n");
+			chip->card->pm_suspend(chip->card, PMSG_SUSPEND);
+		}
+		/* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+		snd_printdd(KERN_DEBUG "RESET_PHYSICAL\n");
+		if (link->state & DEV_CONFIG)
+			pcmcia_release_configuration(link->handle);
+		break;
+	case CS_EVENT_PM_RESUME:
+		snd_printdd(KERN_DEBUG "RESUME\n");
+		link->state &= ~DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_CARD_RESET:
+		snd_printdd(KERN_DEBUG "CARD_RESET\n");
+		if (DEV_OK(link)) {
+			//struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
+			snd_printdd(KERN_DEBUG "requestconfig...\n");
+			pcmcia_request_configuration(link->handle, &link->conf);
+			if (chip && chip->card->pm_resume) {
+				snd_printdd(KERN_DEBUG "calling snd_vx_resume\n");
+				chip->card->pm_resume(chip->card);
+			}
+		}
+		snd_printdd(KERN_DEBUG "resume done!\n");
+		break;
+#endif
+	}
+	return 0;
+}
+
 
 /*
  */
 static dev_link_t *vxp_attach(void)
 {
-	return snd_vxpocket_attach(&hw_entry);
+	snd_card_t *card;
+	struct snd_vxpocket *vxp;
+	int i;
+
+	/* find an empty slot from the card list */
+	for (i = 0; i < SNDRV_CARDS; i++) {
+		if (! card_alloc & (1 << i))
+			break;
+	}
+	if (i >= SNDRV_CARDS) {
+		snd_printk(KERN_ERR "vxpocket: too many cards found\n");
+		return NULL;
+	}
+	if (! enable[i])
+		return NULL; /* disabled explicitly */
+
+	/* ok, create a card instance */
+	card = snd_card_new(index[i], id[i], THIS_MODULE, 0);
+	if (card == NULL) {
+		snd_printk(KERN_ERR "vxpocket: cannot create a card instance\n");
+		return NULL;
+	}
+
+	vxp = snd_vxpocket_new(card, ibl[i]);
+	if (! vxp) {
+		snd_card_free(card);
+		return NULL;
+	}
+
+	vxp->index = index[i];
+	card_alloc |= 1 << i;
+
+	/* Chain drivers */
+	vxp->link.next = dev_list;
+	dev_list = &vxp->link;
+
+	return &vxp->link;
 }
 
 static void vxp_detach(dev_link_t *link)
 {
-	snd_vxpocket_detach(&hw_entry, link);
+	struct snd_vxpocket *vxp;
+	vx_core_t *chip;
+	dev_link_t **linkp;
+
+	if (! link)
+		return;
+
+	vxp = link->priv;
+	chip = (vx_core_t *)vxp;
+	card_alloc &= ~(1 << vxp->index);
+
+	/* Remove the interface data from the linked list */
+	for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+		if (*linkp == link) {
+			*linkp = link->next;
+			break;
+		}
+
+	chip->chip_status |= VX_STAT_IS_STALE; /* to be sure */
+	snd_card_disconnect(chip->card);
+	vxpocket_release(link);
+	snd_card_free_in_thread(chip->card);
 }
 
 /*
@@ -137,7 +456,7 @@
 static struct pcmcia_driver vxp_cs_driver = {
 	.owner		= THIS_MODULE,
 	.drv		= {
-		.name	= DEV_INFO,
+		.name	= "snd-vxpocket",
 	},
 	.attach		= vxp_attach,
 	.detach		= vxp_detach,
@@ -152,7 +471,7 @@
 static void __exit exit_vxpocket(void)
 {
 	pcmcia_unregister_driver(&vxp_cs_driver);
-	BUG_ON(hw_entry.dev_list != NULL);
+	BUG_ON(dev_list != NULL);
 }
 
 module_init(init_vxpocket);
diff --git a/sound/pcmcia/vx/vxpocket.h b/sound/pcmcia/vx/vxpocket.h
index 4462c04..70754aa 100644
--- a/sound/pcmcia/vx/vxpocket.h
+++ b/sound/pcmcia/vx/vxpocket.h
@@ -28,24 +28,6 @@
 #include <pcmcia/cistpl.h>
 #include <pcmcia/ds.h>
 
-struct snd_vxp_entry {
-	dev_info_t *dev_info;
-
-	/* module parameters */
-	int *index_table;
-	char **id_table;
-	int *enable_table;
-	int *ibl;
-
-	/* h/w config */
-	struct snd_vx_hardware *hardware;
-	struct snd_vx_ops *ops;
-
-	/* slots */
-	vx_core_t *card_list[SNDRV_CARDS];
-	dev_link_t *dev_list;		/* Linked list of devices */
-};
-
 struct snd_vxpocket {
 
 	vx_core_t core;
@@ -57,8 +39,7 @@
 	unsigned int regCDSP;	/* current CDSP register */
 	unsigned int regDIALOG;	/* current DIALOG register */
 
-	int index;
-	struct snd_vxp_entry *hw_entry;
+	int index;	/* card index */
 
 	/* pcmcia stuff */
 	dev_link_t link;
@@ -70,12 +51,6 @@
 void vx_set_mic_boost(vx_core_t *chip, int boost);
 void vx_set_mic_level(vx_core_t *chip, int level);
 
-/*
- * pcmcia stuff
- */
-dev_link_t *snd_vxpocket_attach(struct snd_vxp_entry *hw);
-void snd_vxpocket_detach(struct snd_vxp_entry *hw, dev_link_t *link);
-
 int vxp_add_mic_controls(vx_core_t *chip);
 
 /* Constants used to access the CDSP register (0x08). */
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index 061e52d..758ca1b 100644
--- a/sound/ppc/awacs.c
+++ b/sound/ppc/awacs.c
@@ -103,7 +103,7 @@
 	snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
 	if (chip->manufacturer == 0x1)
 		/* delay for broken crystal part */
-		big_mdelay(750);
+		msleep(750);
 	snd_pmac_awacs_write_noreg(chip, 1,
 				   chip->awacs_reg[1] | MASK_RECALIBRATE | MASK_CMUTE | MASK_AMUTE);
 	snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
@@ -653,10 +653,10 @@
 {
 	if (machine_is_compatible("PowerBook3,1")
 	    || machine_is_compatible("PowerBook3,2")) {
-		big_mdelay(100);
+		msleep(100);
 		snd_pmac_awacs_write_reg(chip, 1,
 			chip->awacs_reg[1] & ~MASK_PAROUT);
-		big_mdelay(300);
+		msleep(300);
 	}
 
 	awacs_restore_all_regs(chip);
diff --git a/sound/ppc/pmac.h b/sound/ppc/pmac.h
index 582db52..ae3bb6c 100644
--- a/sound/ppc/pmac.h
+++ b/sound/ppc/pmac.h
@@ -212,9 +212,4 @@
 
 int snd_pmac_add_automute(pmac_t *chip);
 
-#define big_mdelay(msec) do {\
-	set_current_state(TASK_UNINTERRUPTIBLE);\
-	schedule_timeout(((msec) * HZ + 999) / 1000);\
-} while (0)
-
 #endif /* __PMAC_H */
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 36c5d5d..b94437c 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -945,7 +945,7 @@
 			check_mute(chip, &mix->line_mute, 0, mix->auto_mute_notify,
 				   chip->lineout_sw_ctl);
 		if (mix->anded_reset)
-			big_mdelay(10);
+			msleep(10);
 		check_mute(chip, &mix->amp_mute, 1, mix->auto_mute_notify,
 			   chip->speaker_sw_ctl);
 		mix->drc_enable = 0;
@@ -954,7 +954,7 @@
 		check_mute(chip, &mix->amp_mute, 0, mix->auto_mute_notify,
 			   chip->speaker_sw_ctl);
 		if (mix->anded_reset)
-			big_mdelay(10);
+			msleep(10);
 		check_mute(chip, &mix->hp_mute, 1, mix->auto_mute_notify,
 			   chip->master_sw_ctl);
 		if (mix->line_mute.addr != 0)
@@ -1109,22 +1109,22 @@
 		DBG("(I) codec anded reset !\n");
 		write_audio_gpio(&mix->hp_mute, 0);
 		write_audio_gpio(&mix->amp_mute, 0);
-		big_mdelay(200);
+		msleep(200);
 		write_audio_gpio(&mix->hp_mute, 1);
 		write_audio_gpio(&mix->amp_mute, 1);
-		big_mdelay(100);
+		msleep(100);
 		write_audio_gpio(&mix->hp_mute, 0);
 		write_audio_gpio(&mix->amp_mute, 0);
-		big_mdelay(100);
+		msleep(100);
 	} else {
 		DBG("(I) codec normal reset !\n");
 
 		write_audio_gpio(&mix->audio_reset, 0);
-		big_mdelay(200);
+		msleep(200);
 		write_audio_gpio(&mix->audio_reset, 1);
-		big_mdelay(100);
+		msleep(100);
 		write_audio_gpio(&mix->audio_reset, 0);
-		big_mdelay(100);
+		msleep(100);
 	}
 }
 
diff --git a/sound/sparc/Kconfig b/sound/sparc/Kconfig
index 2358df1..25a8a55 100644
--- a/sound/sparc/Kconfig
+++ b/sound/sparc/Kconfig
@@ -7,12 +7,30 @@
 	tristate "Sun AMD7930"
 	depends on SBUS && SND
 	select SND_PCM
+	help
+	  Say Y here to include support for AMD7930 sound device on Sun.
 
-#  dep_tristate 'Sun DBRI' CONFIG_SND_SUN_DBRI $CONFIG_SND
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-sun-amd7930.
+
 config SND_SUN_CS4231
 	tristate "Sun CS4231"
 	depends on SND
 	select SND_PCM
+	help
+	  Say Y here to include support for CS4231 sound device on Sun.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-sun-cs4231.
+
+config SND_SUN_DBRI
+	tristate "Sun DBRI"
+	depends on SND && SBUS
+	select SND_PCM
+	help
+	  Say Y here to include support for DBRI sound device on Sun.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-sun-dbri.
 
 endmenu
-
diff --git a/sound/sparc/Makefile b/sound/sparc/Makefile
index 6809cc9..3cd89c6 100644
--- a/sound/sparc/Makefile
+++ b/sound/sparc/Makefile
@@ -4,9 +4,9 @@
 #
 
 snd-sun-amd7930-objs := amd7930.o
-#snd-sun-dbri-objs := dbri.o
 snd-sun-cs4231-objs := cs4231.o
+snd-sun-dbri-objs := dbri.o
 
 obj-$(CONFIG_SND_SUN_AMD7930) += snd-sun-amd7930.o
-#obj-$(CONFIG_SND_SUN_DBRI) += snd-sun-dbri.o
 obj-$(CONFIG_SND_SUN_CS4231) += snd-sun-cs4231.o
+obj-$(CONFIG_SND_SUN_DBRI) += snd-sun-dbri.o
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
new file mode 100644
index 0000000..941c7b1
--- /dev/null
+++ b/sound/sparc/dbri.c
@@ -0,0 +1,2729 @@
+/*
+ * Driver for DBRI sound chip found on Sparcs.
+ * Copyright (C) 2004 Martin Habets (mhabets@users.sourceforge.net)
+ *
+ * Based entirely upon drivers/sbus/audio/dbri.c which is:
+ * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
+ * Copyright (C) 1998, 1999 Brent Baccala (baccala@freesoft.org)
+ *
+ * This is the lowlevel driver for the DBRI & MMCODEC duo used for ISDN & AUDIO
+ * on Sun SPARCstation 10, 20, LX and Voyager models.
+ *
+ * - DBRI: AT&T T5900FX Dual Basic Rates ISDN Interface. It is a 32 channel
+ *   data time multiplexer with ISDN support (aka T7259)
+ *   Interfaces: SBus,ISDN NT & TE, CHI, 4 bits parallel.
+ *   CHI: (spelled ki) Concentration Highway Interface (AT&T or Intel bus ?).
+ *   Documentation:
+ *   - "STP 4000SBus Dual Basic Rate ISDN (DBRI) Tranceiver" from
+ *     Sparc Technology Business (courtesy of Sun Support)
+ *   - Data sheet of the T7903, a newer but very similar ISA bus equivalent
+ *     available from the Lucent (formarly AT&T microelectronics) home
+ *     page.
+ *   - http://www.freesoft.org/Linux/DBRI/
+ * - MMCODEC: Crystal Semiconductor CS4215 16 bit Multimedia Audio Codec
+ *   Interfaces: CHI, Audio In & Out, 2 bits parallel
+ *   Documentation: from the Crystal Semiconductor home page.
+ *
+ * The DBRI is a 32 pipe machine, each pipe can transfer some bits between
+ * memory and a serial device (long pipes, nr 0-15) or between two serial
+ * devices (short pipes, nr 16-31), or simply send a fixed data to a serial
+ * device (short pipes).
+ * A timeslot defines the bit-offset and nr of bits read from a serial device.
+ * The timeslots are linked to 6 circular lists, one for each direction for
+ * each serial device (NT,TE,CHI). A timeslot is associated to 1 or 2 pipes
+ * (the second one is a monitor/tee pipe, valid only for serial input).
+ *
+ * The mmcodec is connected via the CHI bus and needs the data & some
+ * parameters (volume, balance, output selection) timemultiplexed in 8 byte
+ * chunks. It also has a control mode, which serves for audio format setting.
+ *
+ * Looking at the CS4215 data sheet it is easy to set up 2 or 4 codecs on
+ * the same CHI bus, so I thought perhaps it is possible to use the onboard
+ * & the speakerbox codec simultanously, giving 2 (not very independent :-)
+ * audio devices. But the SUN HW group decided against it, at least on my
+ * LX the speakerbox connector has at least 1 pin missing and 1 wrongly
+ * connected.
+ */
+
+#include <sound/driver.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/sbus.h>
+#include <asm/atomic.h>
+
+MODULE_AUTHOR("Rudolf Koenig, Brent Baccala and Martin Habets");
+MODULE_DESCRIPTION("Sun DBRI");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Sun,DBRI}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Sun DBRI soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Sun DBRI soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Sun DBRI soundcard.");
+
+#define DBRI_DEBUG
+
+#define D_INT	(1<<0)
+#define D_GEN	(1<<1)
+#define D_CMD	(1<<2)
+#define D_MM	(1<<3)
+#define D_USR	(1<<4)
+#define D_DESC	(1<<5)
+
+static int dbri_debug = 0;
+module_param(dbri_debug, int, 0444);
+MODULE_PARM_DESC(dbri_debug, "Debug value for Sun DBRI soundcard.");
+
+#ifdef DBRI_DEBUG
+static char *cmds[] = {
+	"WAIT", "PAUSE", "JUMP", "IIQ", "REX", "SDP", "CDP", "DTS",
+	"SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV"
+};
+
+#define dprintk(a, x...) if(dbri_debug & a) printk(KERN_DEBUG x)
+
+#define DBRI_CMD(cmd, intr, value) ((cmd << 28) |			\
+				    (1 << 27) | \
+				    value)
+#else
+#define dprintk(a, x...)
+
+#define DBRI_CMD(cmd, intr, value) ((cmd << 28) |			\
+				    (intr << 27) | \
+				    value)
+#endif				/* DBRI_DEBUG */
+
+/***************************************************************************
+	CS4215 specific definitions and structures
+****************************************************************************/
+
+struct cs4215 {
+	__u8 data[4];		/* Data mode: Time slots 5-8 */
+	__u8 ctrl[4];		/* Ctrl mode: Time slots 1-4 */
+	__u8 onboard;
+	__u8 offset;		/* Bit offset from frame sync to time slot 1 */
+	volatile __u32 status;
+	volatile __u32 version;
+	__u8 precision;		/* In bits, either 8 or 16 */
+	__u8 channels;		/* 1 or 2 */
+};
+
+/*
+ * Control mode first 
+ */
+
+/* Time Slot 1, Status register */
+#define CS4215_CLB	(1<<2)	/* Control Latch Bit */
+#define CS4215_OLB	(1<<3)	/* 1: line: 2.0V, speaker 4V */
+				/* 0: line: 2.8V, speaker 8V */
+#define CS4215_MLB	(1<<4)	/* 1: Microphone: 20dB gain disabled */
+#define CS4215_RSRVD_1  (1<<5)
+
+/* Time Slot 2, Data Format Register */
+#define CS4215_DFR_LINEAR16	0
+#define CS4215_DFR_ULAW		1
+#define CS4215_DFR_ALAW		2
+#define CS4215_DFR_LINEAR8	3
+#define CS4215_DFR_STEREO	(1<<2)
+static struct {
+	unsigned short freq;
+	unsigned char xtal;
+	unsigned char csval;
+} CS4215_FREQ[] = {
+	{  8000, (1 << 4), (0 << 3) },
+	{ 16000, (1 << 4), (1 << 3) },
+	{ 27429, (1 << 4), (2 << 3) },	/* Actually 24428.57 */
+	{ 32000, (1 << 4), (3 << 3) },
+     /* {    NA, (1 << 4), (4 << 3) }, */
+     /* {    NA, (1 << 4), (5 << 3) }, */
+	{ 48000, (1 << 4), (6 << 3) },
+	{  9600, (1 << 4), (7 << 3) },
+	{  5513, (2 << 4), (0 << 3) },	/* Actually 5512.5 */
+	{ 11025, (2 << 4), (1 << 3) },
+	{ 18900, (2 << 4), (2 << 3) },
+	{ 22050, (2 << 4), (3 << 3) },
+	{ 37800, (2 << 4), (4 << 3) },
+	{ 44100, (2 << 4), (5 << 3) },
+	{ 33075, (2 << 4), (6 << 3) },
+	{  6615, (2 << 4), (7 << 3) },
+	{ 0, 0, 0}
+};
+
+#define CS4215_HPF	(1<<7)	/* High Pass Filter, 1: Enabled */
+
+#define CS4215_12_MASK	0xfcbf	/* Mask off reserved bits in slot 1 & 2 */
+
+/* Time Slot 3, Serial Port Control register */
+#define CS4215_XEN	(1<<0)	/* 0: Enable serial output */
+#define CS4215_XCLK	(1<<1)	/* 1: Master mode: Generate SCLK */
+#define CS4215_BSEL_64	(0<<2)	/* Bitrate: 64 bits per frame */
+#define CS4215_BSEL_128	(1<<2)
+#define CS4215_BSEL_256	(2<<2)
+#define CS4215_MCK_MAST (0<<4)	/* Master clock */
+#define CS4215_MCK_XTL1 (1<<4)	/* 24.576 MHz clock source */
+#define CS4215_MCK_XTL2 (2<<4)	/* 16.9344 MHz clock source */
+#define CS4215_MCK_CLK1 (3<<4)	/* Clockin, 256 x Fs */
+#define CS4215_MCK_CLK2 (4<<4)	/* Clockin, see DFR */
+
+/* Time Slot 4, Test Register */
+#define CS4215_DAD	(1<<0)	/* 0:Digital-Dig loop, 1:Dig-Analog-Dig loop */
+#define CS4215_ENL	(1<<1)	/* Enable Loopback Testing */
+
+/* Time Slot 5, Parallel Port Register */
+/* Read only here and the same as the in data mode */
+
+/* Time Slot 6, Reserved  */
+
+/* Time Slot 7, Version Register  */
+#define CS4215_VERSION_MASK 0xf	/* Known versions 0/C, 1/D, 2/E */
+
+/* Time Slot 8, Reserved  */
+
+/*
+ * Data mode
+ */
+/* Time Slot 1-2: Left Channel Data, 2-3: Right Channel Data  */
+
+/* Time Slot 5, Output Setting  */
+#define CS4215_LO(v)	v	/* Left Output Attenuation 0x3f: -94.5 dB */
+#define CS4215_LE	(1<<6)	/* Line Out Enable */
+#define CS4215_HE	(1<<7)	/* Headphone Enable */
+
+/* Time Slot 6, Output Setting  */
+#define CS4215_RO(v)	v	/* Right Output Attenuation 0x3f: -94.5 dB */
+#define CS4215_SE	(1<<6)	/* Speaker Enable */
+#define CS4215_ADI	(1<<7)	/* A/D Data Invalid: Busy in calibration */
+
+/* Time Slot 7, Input Setting */
+#define CS4215_LG(v)	v	/* Left Gain Setting 0xf: 22.5 dB */
+#define CS4215_IS	(1<<4)	/* Input Select: 1=Microphone, 0=Line */
+#define CS4215_OVR	(1<<5)	/* 1: Overrange condition occurred */
+#define CS4215_PIO0	(1<<6)	/* Parallel I/O 0 */
+#define CS4215_PIO1	(1<<7)
+
+/* Time Slot 8, Input Setting */
+#define CS4215_RG(v)	v	/* Right Gain Setting 0xf: 22.5 dB */
+#define CS4215_MA(v)	(v<<4)	/* Monitor Path Attenuation 0xf: mute */
+
+/***************************************************************************
+		DBRI specific definitions and structures
+****************************************************************************/
+
+/* DBRI main registers */
+#define REG0	0x00UL		/* Status and Control */
+#define REG1	0x04UL		/* Mode and Interrupt */
+#define REG2	0x08UL		/* Parallel IO */
+#define REG3	0x0cUL		/* Test */
+#define REG8	0x20UL		/* Command Queue Pointer */
+#define REG9	0x24UL		/* Interrupt Queue Pointer */
+
+#define DBRI_NO_CMDS	64
+#define DBRI_NO_INTS	1	/* Note: the value of this define was
+				 * originally 2.  The ringbuffer to store
+				 * interrupts in dma is currently broken.
+				 * This is a temporary fix until the ringbuffer
+				 * is fixed.
+				 */
+#define DBRI_INT_BLK	64
+#define DBRI_NO_DESCS	64
+#define DBRI_NO_PIPES	32
+
+#define DBRI_MM_ONB	1
+#define DBRI_MM_SB	2
+
+#define DBRI_REC	0
+#define DBRI_PLAY	1
+#define DBRI_NO_STREAMS	2
+
+/* One transmit/receive descriptor */
+struct dbri_mem {
+	volatile __u32 word1;
+	volatile __u32 ba;	/* Transmit/Receive Buffer Address */
+	volatile __u32 nda;	/* Next Descriptor Address */
+	volatile __u32 word4;
+};
+
+/* This structure is in a DMA region where it can accessed by both
+ * the CPU and the DBRI
+ */
+struct dbri_dma {
+	volatile s32 cmd[DBRI_NO_CMDS];	/* Place for commands       */
+	volatile s32 intr[DBRI_NO_INTS * DBRI_INT_BLK];	/* Interrupt field  */
+	struct dbri_mem desc[DBRI_NO_DESCS];	/* Xmit/receive descriptors */
+};
+
+#define dbri_dma_off(member, elem)	\
+	((u32)(unsigned long)		\
+	 (&(((struct dbri_dma *)0)->member[elem])))
+
+enum in_or_out { PIPEinput, PIPEoutput };
+
+struct dbri_pipe {
+	u32 sdp;		/* SDP command word */
+	enum in_or_out direction;
+	int nextpipe;		/* Next pipe in linked list */
+	int prevpipe;
+	int cycle;		/* Offset of timeslot (bits) */
+	int length;		/* Length of timeslot (bits) */
+	int first_desc;		/* Index of first descriptor */
+	int desc;		/* Index of active descriptor */
+	volatile __u32 *recv_fixed_ptr;	/* Ptr to receive fixed data */
+};
+
+struct dbri_desc {
+	int inuse;		/* Boolean flag */
+	int next;		/* Index of next desc, or -1 */
+	unsigned int len;
+};
+
+/* Per stream (playback or record) information */
+typedef struct dbri_streaminfo {
+	snd_pcm_substream_t *substream;
+	u32 dvma_buffer;	/* Device view of Alsa DMA buffer */
+	int left;		/* # of bytes left in DMA buffer  */
+	int size;		/* Size of DMA buffer             */
+	size_t offset;		/* offset in user buffer          */
+	int pipe;		/* Data pipe used                 */
+	int left_gain;		/* mixer elements                 */
+	int right_gain;
+	int balance;
+} dbri_streaminfo_t;
+
+/* This structure holds the information for both chips (DBRI & CS4215) */
+typedef struct snd_dbri {
+	snd_card_t *card;	/* ALSA card */
+	snd_pcm_t *pcm;
+
+	int regs_size, irq;	/* Needed for unload */
+	struct sbus_dev *sdev;	/* SBUS device info */
+	spinlock_t lock;
+
+	volatile struct dbri_dma *dma;	/* Pointer to our DMA block */
+	u32 dma_dvma;		/* DBRI visible DMA address */
+
+	void __iomem *regs;	/* dbri HW regs */
+	int dbri_version;	/* 'e' and up is OK */
+	int dbri_irqp;		/* intr queue pointer */
+	int wait_seen;
+
+	struct dbri_pipe pipes[DBRI_NO_PIPES];	/* DBRI's 32 data pipes */
+	struct dbri_desc descs[DBRI_NO_DESCS];
+
+	int chi_in_pipe;
+	int chi_out_pipe;
+	int chi_bpf;
+
+	struct cs4215 mm;	/* mmcodec special info */
+				/* per stream (playback/record) info */
+	struct dbri_streaminfo stream_info[DBRI_NO_STREAMS];
+
+	struct snd_dbri *next;
+} snd_dbri_t;
+
+/* Needed for the ALSA macros to work */
+#define chip_t snd_dbri_t
+
+#define DBRI_MAX_VOLUME		63	/* Output volume */
+#define DBRI_MAX_GAIN		15	/* Input gain */
+#define DBRI_RIGHT_BALANCE	255
+#define DBRI_MID_BALANCE	(DBRI_RIGHT_BALANCE >> 1)
+
+/* DBRI Reg0 - Status Control Register - defines. (Page 17) */
+#define D_P		(1<<15)	/* Program command & queue pointer valid */
+#define D_G		(1<<14)	/* Allow 4-Word SBus Burst */
+#define D_S		(1<<13)	/* Allow 16-Word SBus Burst */
+#define D_E		(1<<12)	/* Allow 8-Word SBus Burst */
+#define D_X		(1<<7)	/* Sanity Timer Disable */
+#define D_T		(1<<6)	/* Permit activation of the TE interface */
+#define D_N		(1<<5)	/* Permit activation of the NT interface */
+#define D_C		(1<<4)	/* Permit activation of the CHI interface */
+#define D_F		(1<<3)	/* Force Sanity Timer Time-Out */
+#define D_D		(1<<2)	/* Disable Master Mode */
+#define D_H		(1<<1)	/* Halt for Analysis */
+#define D_R		(1<<0)	/* Soft Reset */
+
+/* DBRI Reg1 - Mode and Interrupt Register - defines. (Page 18) */
+#define D_LITTLE_END	(1<<8)	/* Byte Order */
+#define D_BIG_END	(0<<8)	/* Byte Order */
+#define D_MRR		(1<<4)	/* Multiple Error Ack on SBus (readonly) */
+#define D_MLE		(1<<3)	/* Multiple Late Error on SBus (readonly) */
+#define D_LBG		(1<<2)	/* Lost Bus Grant on SBus (readonly) */
+#define D_MBE		(1<<1)	/* Burst Error on SBus (readonly) */
+#define D_IR		(1<<0)	/* Interrupt Indicator (readonly) */
+
+/* DBRI Reg2 - Parallel IO Register - defines. (Page 18) */
+#define D_ENPIO3	(1<<7)	/* Enable Pin 3 */
+#define D_ENPIO2	(1<<6)	/* Enable Pin 2 */
+#define D_ENPIO1	(1<<5)	/* Enable Pin 1 */
+#define D_ENPIO0	(1<<4)	/* Enable Pin 0 */
+#define D_ENPIO		(0xf0)	/* Enable all the pins */
+#define D_PIO3		(1<<3)	/* Pin 3: 1: Data mode, 0: Ctrl mode */
+#define D_PIO2		(1<<2)	/* Pin 2: 1: Onboard PDN */
+#define D_PIO1		(1<<1)	/* Pin 1: 0: Reset */
+#define D_PIO0		(1<<0)	/* Pin 0: 1: Speakerbox PDN */
+
+/* DBRI Commands (Page 20) */
+#define D_WAIT		0x0	/* Stop execution */
+#define D_PAUSE		0x1	/* Flush long pipes */
+#define D_JUMP		0x2	/* New command queue */
+#define D_IIQ		0x3	/* Initialize Interrupt Queue */
+#define D_REX		0x4	/* Report command execution via interrupt */
+#define D_SDP		0x5	/* Setup Data Pipe */
+#define D_CDP		0x6	/* Continue Data Pipe (reread NULL Pointer) */
+#define D_DTS		0x7	/* Define Time Slot */
+#define D_SSP		0x8	/* Set short Data Pipe */
+#define D_CHI		0x9	/* Set CHI Global Mode */
+#define D_NT		0xa	/* NT Command */
+#define D_TE		0xb	/* TE Command */
+#define D_CDEC		0xc	/* Codec setup */
+#define D_TEST		0xd	/* No comment */
+#define D_CDM		0xe	/* CHI Data mode command */
+
+/* Special bits for some commands */
+#define D_PIPE(v)      ((v)<<0)	/* Pipe Nr: 0-15 long, 16-21 short */
+
+/* Setup Data Pipe */
+/* IRM */
+#define D_SDP_2SAME	(1<<18)	/* Report 2nd time in a row value rcvd */
+#define D_SDP_CHANGE	(2<<18)	/* Report any changes */
+#define D_SDP_EVERY	(3<<18)	/* Report any changes */
+#define D_SDP_EOL	(1<<17)	/* EOL interrupt enable */
+#define D_SDP_IDLE	(1<<16)	/* HDLC idle interrupt enable */
+
+/* Pipe data MODE */
+#define D_SDP_MEM	(0<<13)	/* To/from memory */
+#define D_SDP_HDLC	(2<<13)
+#define D_SDP_HDLC_D	(3<<13)	/* D Channel (prio control) */
+#define D_SDP_SER	(4<<13)	/* Serial to serial */
+#define D_SDP_FIXED	(6<<13)	/* Short only */
+#define D_SDP_MODE(v)	((v)&(7<<13))
+
+#define D_SDP_TO_SER	(1<<12)	/* Direction */
+#define D_SDP_FROM_SER	(0<<12)	/* Direction */
+#define D_SDP_MSB	(1<<11)	/* Bit order within Byte */
+#define D_SDP_LSB	(0<<11)	/* Bit order within Byte */
+#define D_SDP_P		(1<<10)	/* Pointer Valid */
+#define D_SDP_A		(1<<8)	/* Abort */
+#define D_SDP_C		(1<<7)	/* Clear */
+
+/* Define Time Slot */
+#define D_DTS_VI	(1<<17)	/* Valid Input Time-Slot Descriptor */
+#define D_DTS_VO	(1<<16)	/* Valid Output Time-Slot Descriptor */
+#define D_DTS_INS	(1<<15)	/* Insert Time Slot */
+#define D_DTS_DEL	(0<<15)	/* Delete Time Slot */
+#define D_DTS_PRVIN(v) ((v)<<10)	/* Previous In Pipe */
+#define D_DTS_PRVOUT(v)        ((v)<<5)	/* Previous Out Pipe */
+
+/* Time Slot defines */
+#define D_TS_LEN(v)	((v)<<24)	/* Number of bits in this time slot */
+#define D_TS_CYCLE(v)	((v)<<14)	/* Bit Count at start of TS */
+#define D_TS_DI		(1<<13)	/* Data Invert */
+#define D_TS_1CHANNEL	(0<<10)	/* Single Channel / Normal mode */
+#define D_TS_MONITOR	(2<<10)	/* Monitor pipe */
+#define D_TS_NONCONTIG	(3<<10)	/* Non contiguous mode */
+#define D_TS_ANCHOR	(7<<10)	/* Starting short pipes */
+#define D_TS_MON(v)    ((v)<<5)	/* Monitor Pipe */
+#define D_TS_NEXT(v)   ((v)<<0)	/* Pipe Nr: 0-15 long, 16-21 short */
+
+/* Concentration Highway Interface Modes */
+#define D_CHI_CHICM(v)	((v)<<16)	/* Clock mode */
+#define D_CHI_IR	(1<<15)	/* Immediate Interrupt Report */
+#define D_CHI_EN	(1<<14)	/* CHIL Interrupt enabled */
+#define D_CHI_OD	(1<<13)	/* Open Drain Enable */
+#define D_CHI_FE	(1<<12)	/* Sample CHIFS on Rising Frame Edge */
+#define D_CHI_FD	(1<<11)	/* Frame Drive */
+#define D_CHI_BPF(v)	((v)<<0)	/* Bits per Frame */
+
+/* NT: These are here for completeness */
+#define D_NT_FBIT	(1<<17)	/* Frame Bit */
+#define D_NT_NBF	(1<<16)	/* Number of bad frames to loose framing */
+#define D_NT_IRM_IMM	(1<<15)	/* Interrupt Report & Mask: Immediate */
+#define D_NT_IRM_EN	(1<<14)	/* Interrupt Report & Mask: Enable */
+#define D_NT_ISNT	(1<<13)	/* Configfure interface as NT */
+#define D_NT_FT		(1<<12)	/* Fixed Timing */
+#define D_NT_EZ		(1<<11)	/* Echo Channel is Zeros */
+#define D_NT_IFA	(1<<10)	/* Inhibit Final Activation */
+#define D_NT_ACT	(1<<9)	/* Activate Interface */
+#define D_NT_MFE	(1<<8)	/* Multiframe Enable */
+#define D_NT_RLB(v)	((v)<<5)	/* Remote Loopback */
+#define D_NT_LLB(v)	((v)<<2)	/* Local Loopback */
+#define D_NT_FACT	(1<<1)	/* Force Activation */
+#define D_NT_ABV	(1<<0)	/* Activate Bipolar Violation */
+
+/* Codec Setup */
+#define D_CDEC_CK(v)	((v)<<24)	/* Clock Select */
+#define D_CDEC_FED(v)	((v)<<12)	/* FSCOD Falling Edge Delay */
+#define D_CDEC_RED(v)	((v)<<0)	/* FSCOD Rising Edge Delay */
+
+/* Test */
+#define D_TEST_RAM(v)	((v)<<16)	/* RAM Pointer */
+#define D_TEST_SIZE(v)	((v)<<11)	/* */
+#define D_TEST_ROMONOFF	0x5	/* Toggle ROM opcode monitor on/off */
+#define D_TEST_PROC	0x6	/* MicroProcessor test */
+#define D_TEST_SER	0x7	/* Serial-Controller test */
+#define D_TEST_RAMREAD	0x8	/* Copy from Ram to system memory */
+#define D_TEST_RAMWRITE	0x9	/* Copy into Ram from system memory */
+#define D_TEST_RAMBIST	0xa	/* RAM Built-In Self Test */
+#define D_TEST_MCBIST	0xb	/* Microcontroller Built-In Self Test */
+#define D_TEST_DUMP	0xe	/* ROM Dump */
+
+/* CHI Data Mode */
+#define D_CDM_THI	(1<<8)	/* Transmit Data on CHIDR Pin */
+#define D_CDM_RHI	(1<<7)	/* Receive Data on CHIDX Pin */
+#define D_CDM_RCE	(1<<6)	/* Receive on Rising Edge of CHICK */
+#define D_CDM_XCE	(1<<2)	/* Transmit Data on Rising Edge of CHICK */
+#define D_CDM_XEN	(1<<1)	/* Transmit Highway Enable */
+#define D_CDM_REN	(1<<0)	/* Receive Highway Enable */
+
+/* The Interrupts */
+#define D_INTR_BRDY	1	/* Buffer Ready for processing */
+#define D_INTR_MINT	2	/* Marked Interrupt in RD/TD */
+#define D_INTR_IBEG	3	/* Flag to idle transition detected (HDLC) */
+#define D_INTR_IEND	4	/* Idle to flag transition detected (HDLC) */
+#define D_INTR_EOL	5	/* End of List */
+#define D_INTR_CMDI	6	/* Command has bean read */
+#define D_INTR_XCMP	8	/* Transmission of frame complete */
+#define D_INTR_SBRI	9	/* BRI status change info */
+#define D_INTR_FXDT	10	/* Fixed data change */
+#define D_INTR_CHIL	11	/* CHI lost frame sync (channel 36 only) */
+#define D_INTR_COLL	11	/* Unrecoverable D-Channel collision */
+#define D_INTR_DBYT	12	/* Dropped by frame slip */
+#define D_INTR_RBYT	13	/* Repeated by frame slip */
+#define D_INTR_LINT	14	/* Lost Interrupt */
+#define D_INTR_UNDR	15	/* DMA underrun */
+
+#define D_INTR_TE	32
+#define D_INTR_NT	34
+#define D_INTR_CHI	36
+#define D_INTR_CMD	38
+
+#define D_INTR_GETCHAN(v)	(((v)>>24) & 0x3f)
+#define D_INTR_GETCODE(v)	(((v)>>20) & 0xf)
+#define D_INTR_GETCMD(v)	(((v)>>16) & 0xf)
+#define D_INTR_GETVAL(v)	((v) & 0xffff)
+#define D_INTR_GETRVAL(v)	((v) & 0xfffff)
+
+#define D_P_0		0	/* TE receive anchor */
+#define D_P_1		1	/* TE transmit anchor */
+#define D_P_2		2	/* NT transmit anchor */
+#define D_P_3		3	/* NT receive anchor */
+#define D_P_4		4	/* CHI send data */
+#define D_P_5		5	/* CHI receive data */
+#define D_P_6		6	/* */
+#define D_P_7		7	/* */
+#define D_P_8		8	/* */
+#define D_P_9		9	/* */
+#define D_P_10		10	/* */
+#define D_P_11		11	/* */
+#define D_P_12		12	/* */
+#define D_P_13		13	/* */
+#define D_P_14		14	/* */
+#define D_P_15		15	/* */
+#define D_P_16		16	/* CHI anchor pipe */
+#define D_P_17		17	/* CHI send */
+#define D_P_18		18	/* CHI receive */
+#define D_P_19		19	/* CHI receive */
+#define D_P_20		20	/* CHI receive */
+#define D_P_21		21	/* */
+#define D_P_22		22	/* */
+#define D_P_23		23	/* */
+#define D_P_24		24	/* */
+#define D_P_25		25	/* */
+#define D_P_26		26	/* */
+#define D_P_27		27	/* */
+#define D_P_28		28	/* */
+#define D_P_29		29	/* */
+#define D_P_30		30	/* */
+#define D_P_31		31	/* */
+
+/* Transmit descriptor defines */
+#define DBRI_TD_F	(1<<31)	/* End of Frame */
+#define DBRI_TD_D	(1<<30)	/* Do not append CRC */
+#define DBRI_TD_CNT(v)	((v)<<16)	/* Number of valid bytes in the buffer */
+#define DBRI_TD_B	(1<<15)	/* Final interrupt */
+#define DBRI_TD_M	(1<<14)	/* Marker interrupt */
+#define DBRI_TD_I	(1<<13)	/* Transmit Idle Characters */
+#define DBRI_TD_FCNT(v)	(v)	/* Flag Count */
+#define DBRI_TD_UNR	(1<<3)	/* Underrun: transmitter is out of data */
+#define DBRI_TD_ABT	(1<<2)	/* Abort: frame aborted */
+#define DBRI_TD_TBC	(1<<0)	/* Transmit buffer Complete */
+#define DBRI_TD_STATUS(v)       ((v)&0xff)	/* Transmit status */
+			/* Maximum buffer size per TD: almost 8Kb */
+#define DBRI_TD_MAXCNT	((1 << 13) - 1)
+
+/* Receive descriptor defines */
+#define DBRI_RD_F	(1<<31)	/* End of Frame */
+#define DBRI_RD_C	(1<<30)	/* Completed buffer */
+#define DBRI_RD_B	(1<<15)	/* Final interrupt */
+#define DBRI_RD_M	(1<<14)	/* Marker interrupt */
+#define DBRI_RD_BCNT(v)	(v)	/* Buffer size */
+#define DBRI_RD_CRC	(1<<7)	/* 0: CRC is correct */
+#define DBRI_RD_BBC	(1<<6)	/* 1: Bad Byte received */
+#define DBRI_RD_ABT	(1<<5)	/* Abort: frame aborted */
+#define DBRI_RD_OVRN	(1<<3)	/* Overrun: data lost */
+#define DBRI_RD_STATUS(v)      ((v)&0xff)	/* Receive status */
+#define DBRI_RD_CNT(v) (((v)>>16)&0x1fff)	/* Valid bytes in the buffer */
+
+/* stream_info[] access */
+/* Translate the ALSA direction into the array index */
+#define DBRI_STREAMNO(substream)				\
+		(substream->stream == 				\
+		 SNDRV_PCM_STREAM_PLAYBACK? DBRI_PLAY: DBRI_REC)
+
+/* Return a pointer to dbri_streaminfo */
+#define DBRI_STREAM(dbri, substream)	&dbri->stream_info[DBRI_STREAMNO(substream)]
+
+static snd_dbri_t *dbri_list = NULL;	/* All DBRI devices */
+
+/*
+ * Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr.
+ * So we have to reverse the bits. Note: not all bit lengths are supported
+ */
+static __u32 reverse_bytes(__u32 b, int len)
+{
+	switch (len) {
+	case 32:
+		b = ((b & 0xffff0000) >> 16) | ((b & 0x0000ffff) << 16);
+	case 16:
+		b = ((b & 0xff00ff00) >> 8) | ((b & 0x00ff00ff) << 8);
+	case 8:
+		b = ((b & 0xf0f0f0f0) >> 4) | ((b & 0x0f0f0f0f) << 4);
+	case 4:
+		b = ((b & 0xcccccccc) >> 2) | ((b & 0x33333333) << 2);
+	case 2:
+		b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1);
+	case 1:
+	case 0:
+		break;
+	default:
+		printk(KERN_ERR "DBRI reverse_bytes: unsupported length\n");
+	};
+
+	return b;
+}
+
+/*
+****************************************************************************
+************** DBRI initialization and command synchronization *************
+****************************************************************************
+
+Commands are sent to the DBRI by building a list of them in memory,
+then writing the address of the first list item to DBRI register 8.
+The list is terminated with a WAIT command, which can generate a
+CPU interrupt if required.
+
+Since the DBRI can run in parallel with the CPU, several means of
+synchronization present themselves.  The original scheme (Rudolf's)
+was to set a flag when we "cmdlock"ed the DBRI, clear the flag when
+an interrupt signaled completion, and wait on a wait_queue if a routine
+attempted to cmdlock while the flag was set.  The problems arose when
+we tried to cmdlock from inside an interrupt handler, which might
+cause scheduling in an interrupt (if we waited), etc, etc
+
+A more sophisticated scheme might involve a circular command buffer
+or an array of command buffers.  A routine could fill one with
+commands and link it onto a list.  When a interrupt signaled
+completion of the current command buffer, look on the list for
+the next one.
+
+I've decided to implement something much simpler - after each command,
+the CPU waits for the DBRI to finish the command by polling the P bit
+in DBRI register 0.  I've tried to implement this in such a way
+that might make implementing a more sophisticated scheme easier.
+
+Every time a routine wants to write commands to the DBRI, it must
+first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd
+in return.  After the commands have been writen, dbri_cmdsend() is
+called with the final pointer value.
+
+*/
+
+enum dbri_lock_t { NoGetLock, GetLock };
+
+static volatile s32 *dbri_cmdlock(snd_dbri_t * dbri, enum dbri_lock_t get)
+{
+#ifndef SMP
+	if ((get == GetLock) && spin_is_locked(&dbri->lock)) {
+		printk(KERN_ERR "DBRI: cmdlock called while in spinlock.");
+	}
+#endif
+
+	/*if (get == GetLock) spin_lock(&dbri->lock); */
+	return &dbri->dma->cmd[0];
+}
+
+static void dbri_process_interrupt_buffer(snd_dbri_t *);
+
+static void dbri_cmdsend(snd_dbri_t * dbri, volatile s32 * cmd)
+{
+	int MAXLOOPS = 1000000;
+	int maxloops = MAXLOOPS;
+	volatile s32 *ptr;
+
+	for (ptr = &dbri->dma->cmd[0]; ptr < cmd; ptr++) {
+		dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
+	}
+
+	if ((cmd - &dbri->dma->cmd[0]) >= DBRI_NO_CMDS - 1) {
+		printk("DBRI: Command buffer overflow! (bug in driver)\n");
+		/* Ignore the last part. */
+		cmd = &dbri->dma->cmd[DBRI_NO_CMDS - 3];
+	}
+
+	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+	*(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
+	dbri->wait_seen = 0;
+	sbus_writel(dbri->dma_dvma, dbri->regs + REG8);
+	while ((--maxloops) > 0 && (sbus_readl(dbri->regs + REG0) & D_P))
+		barrier();
+	if (maxloops == 0) {
+		printk(KERN_ERR "DBRI: Chip never completed command buffer\n");
+		dprintk(D_CMD, "DBRI: Chip never completed command buffer\n");
+	} else {
+		while ((--maxloops) > 0 && (!dbri->wait_seen))
+			dbri_process_interrupt_buffer(dbri);
+		if (maxloops == 0) {
+			printk(KERN_ERR "DBRI: Chip never acked WAIT\n");
+			dprintk(D_CMD, "DBRI: Chip never acked WAIT\n");
+		} else {
+			dprintk(D_CMD, "Chip completed command "
+				"buffer (%d)\n", MAXLOOPS - maxloops);
+		}
+	}
+
+	/*spin_unlock(&dbri->lock); */
+}
+
+/* Lock must be held when calling this */
+static void dbri_reset(snd_dbri_t * dbri)
+{
+	int i;
+
+	dprintk(D_GEN, "reset 0:%x 2:%x 8:%x 9:%x\n",
+		sbus_readl(dbri->regs + REG0),
+		sbus_readl(dbri->regs + REG2),
+		sbus_readl(dbri->regs + REG8), sbus_readl(dbri->regs + REG9));
+
+	sbus_writel(D_R, dbri->regs + REG0);	/* Soft Reset */
+	for (i = 0; (sbus_readl(dbri->regs + REG0) & D_R) && i < 64; i++)
+		udelay(10);
+}
+
+/* Lock must not be held before calling this */
+static void dbri_initialize(snd_dbri_t * dbri)
+{
+	volatile s32 *cmd;
+	u32 dma_addr, tmp;
+	unsigned long flags;
+	int n;
+
+	spin_lock_irqsave(&dbri->lock, flags);
+
+	dbri_reset(dbri);
+
+	cmd = dbri_cmdlock(dbri, NoGetLock);
+	dprintk(D_GEN, "init: cmd: %p, int: %p\n",
+		&dbri->dma->cmd[0], &dbri->dma->intr[0]);
+
+	/*
+	 * Initialize the interrupt ringbuffer.
+	 */
+	for (n = 0; n < DBRI_NO_INTS - 1; n++) {
+		dma_addr = dbri->dma_dvma;
+		dma_addr += dbri_dma_off(intr, ((n + 1) & DBRI_INT_BLK));
+		dbri->dma->intr[n * DBRI_INT_BLK] = dma_addr;
+	}
+	dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0);
+	dbri->dma->intr[n * DBRI_INT_BLK] = dma_addr;
+	dbri->dbri_irqp = 1;
+
+	/* Initialize pipes */
+	for (n = 0; n < DBRI_NO_PIPES; n++)
+		dbri->pipes[n].desc = dbri->pipes[n].first_desc = -1;
+
+	/* We should query the openprom to see what burst sizes this
+	 * SBus supports.  For now, just disable all SBus bursts */
+	tmp = sbus_readl(dbri->regs + REG0);
+	tmp &= ~(D_G | D_S | D_E);
+	sbus_writel(tmp, dbri->regs + REG0);
+
+	/*
+	 * Set up the interrupt queue
+	 */
+	dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0);
+	*(cmd++) = DBRI_CMD(D_IIQ, 0, 0);
+	*(cmd++) = dma_addr;
+
+	dbri_cmdsend(dbri, cmd);
+	spin_unlock_irqrestore(&dbri->lock, flags);
+}
+
+/*
+****************************************************************************
+************************** DBRI data pipe management ***********************
+****************************************************************************
+
+While DBRI control functions use the command and interrupt buffers, the
+main data path takes the form of data pipes, which can be short (command
+and interrupt driven), or long (attached to DMA buffers).  These functions
+provide a rudimentary means of setting up and managing the DBRI's pipes,
+but the calling functions have to make sure they respect the pipes' linked
+list ordering, among other things.  The transmit and receive functions
+here interface closely with the transmit and receive interrupt code.
+
+*/
+static int pipe_active(snd_dbri_t * dbri, int pipe)
+{
+	return ((pipe >= 0) && (dbri->pipes[pipe].desc != -1));
+}
+
+/* reset_pipe(dbri, pipe)
+ *
+ * Called on an in-use pipe to clear anything being transmitted or received
+ * Lock must be held before calling this.
+ */
+static void reset_pipe(snd_dbri_t * dbri, int pipe)
+{
+	int sdp;
+	int desc;
+	volatile int *cmd;
+
+	if (pipe < 0 || pipe > 31) {
+		printk("DBRI: reset_pipe called with illegal pipe number\n");
+		return;
+	}
+
+	sdp = dbri->pipes[pipe].sdp;
+	if (sdp == 0) {
+		printk("DBRI: reset_pipe called on uninitialized pipe\n");
+		return;
+	}
+
+	cmd = dbri_cmdlock(dbri, NoGetLock);
+	*(cmd++) = DBRI_CMD(D_SDP, 0, sdp | D_SDP_C | D_SDP_P);
+	*(cmd++) = 0;
+	dbri_cmdsend(dbri, cmd);
+
+	desc = dbri->pipes[pipe].first_desc;
+	while (desc != -1) {
+		dbri->descs[desc].inuse = 0;
+		desc = dbri->descs[desc].next;
+	}
+
+	dbri->pipes[pipe].desc = -1;
+	dbri->pipes[pipe].first_desc = -1;
+}
+
+/* FIXME: direction as an argument? */
+static void setup_pipe(snd_dbri_t * dbri, int pipe, int sdp)
+{
+	if (pipe < 0 || pipe > 31) {
+		printk("DBRI: setup_pipe called with illegal pipe number\n");
+		return;
+	}
+
+	if ((sdp & 0xf800) != sdp) {
+		printk("DBRI: setup_pipe called with strange SDP value\n");
+		/* sdp &= 0xf800; */
+	}
+
+	/* If this is a fixed receive pipe, arrange for an interrupt
+	 * every time its data changes
+	 */
+	if (D_SDP_MODE(sdp) == D_SDP_FIXED && !(sdp & D_SDP_TO_SER))
+		sdp |= D_SDP_CHANGE;
+
+	sdp |= D_PIPE(pipe);
+	dbri->pipes[pipe].sdp = sdp;
+	dbri->pipes[pipe].desc = -1;
+	dbri->pipes[pipe].first_desc = -1;
+	if (sdp & D_SDP_TO_SER)
+		dbri->pipes[pipe].direction = PIPEoutput;
+	else
+		dbri->pipes[pipe].direction = PIPEinput;
+
+	reset_pipe(dbri, pipe);
+}
+
+/* FIXME: direction not needed */
+static void link_time_slot(snd_dbri_t * dbri, int pipe,
+			   enum in_or_out direction, int basepipe,
+			   int length, int cycle)
+{
+	volatile s32 *cmd;
+	int val;
+	int prevpipe;
+	int nextpipe;
+
+	if (pipe < 0 || pipe > 31 || basepipe < 0 || basepipe > 31) {
+		printk
+		    ("DBRI: link_time_slot called with illegal pipe number\n");
+		return;
+	}
+
+	if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[basepipe].sdp == 0) {
+		printk("DBRI: link_time_slot called on uninitialized pipe\n");
+		return;
+	}
+
+	/* Deal with CHI special case:
+	 * "If transmission on edges 0 or 1 is desired, then cycle n
+	 *  (where n = # of bit times per frame...) must be used."
+	 *                  - DBRI data sheet, page 11
+	 */
+	if (basepipe == 16 && direction == PIPEoutput && cycle == 0)
+		cycle = dbri->chi_bpf;
+
+	if (basepipe == pipe) {
+		prevpipe = pipe;
+		nextpipe = pipe;
+	} else {
+		/* We're not initializing a new linked list (basepipe != pipe),
+		 * so run through the linked list and find where this pipe
+		 * should be sloted in, based on its cycle.  CHI confuses
+		 * things a bit, since it has a single anchor for both its
+		 * transmit and receive lists.
+		 */
+		if (basepipe == 16) {
+			if (direction == PIPEinput) {
+				prevpipe = dbri->chi_in_pipe;
+			} else {
+				prevpipe = dbri->chi_out_pipe;
+			}
+		} else {
+			prevpipe = basepipe;
+		}
+
+		nextpipe = dbri->pipes[prevpipe].nextpipe;
+
+		while (dbri->pipes[nextpipe].cycle < cycle
+		       && dbri->pipes[nextpipe].nextpipe != basepipe) {
+			prevpipe = nextpipe;
+			nextpipe = dbri->pipes[nextpipe].nextpipe;
+		}
+	}
+
+	if (prevpipe == 16) {
+		if (direction == PIPEinput) {
+			dbri->chi_in_pipe = pipe;
+		} else {
+			dbri->chi_out_pipe = pipe;
+		}
+	} else {
+		dbri->pipes[prevpipe].nextpipe = pipe;
+	}
+
+	dbri->pipes[pipe].nextpipe = nextpipe;
+	dbri->pipes[pipe].cycle = cycle;
+	dbri->pipes[pipe].length = length;
+
+	cmd = dbri_cmdlock(dbri, NoGetLock);
+
+	if (direction == PIPEinput) {
+		val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
+		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
+		*(cmd++) =
+		    D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+		*(cmd++) = 0;
+	} else {
+		val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
+		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
+		*(cmd++) = 0;
+		*(cmd++) =
+		    D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+	}
+
+	dbri_cmdsend(dbri, cmd);
+}
+
+static void unlink_time_slot(snd_dbri_t * dbri, int pipe,
+			     enum in_or_out direction, int prevpipe,
+			     int nextpipe)
+{
+	volatile s32 *cmd;
+	int val;
+
+	if (pipe < 0 || pipe > 31 || prevpipe < 0 || prevpipe > 31) {
+		printk
+		    ("DBRI: unlink_time_slot called with illegal pipe number\n");
+		return;
+	}
+
+	cmd = dbri_cmdlock(dbri, NoGetLock);
+
+	if (direction == PIPEinput) {
+		val = D_DTS_VI | D_DTS_DEL | D_DTS_PRVIN(prevpipe) | pipe;
+		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
+		*(cmd++) = D_TS_NEXT(nextpipe);
+		*(cmd++) = 0;
+	} else {
+		val = D_DTS_VO | D_DTS_DEL | D_DTS_PRVOUT(prevpipe) | pipe;
+		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
+		*(cmd++) = 0;
+		*(cmd++) = D_TS_NEXT(nextpipe);
+	}
+
+	dbri_cmdsend(dbri, cmd);
+}
+
+/* xmit_fixed() / recv_fixed()
+ *
+ * Transmit/receive data on a "fixed" pipe - i.e, one whose contents are not
+ * expected to change much, and which we don't need to buffer.
+ * The DBRI only interrupts us when the data changes (receive pipes),
+ * or only changes the data when this function is called (transmit pipes).
+ * Only short pipes (numbers 16-31) can be used in fixed data mode.
+ *
+ * These function operate on a 32-bit field, no matter how large
+ * the actual time slot is.  The interrupt handler takes care of bit
+ * ordering and alignment.  An 8-bit time slot will always end up
+ * in the low-order 8 bits, filled either MSB-first or LSB-first,
+ * depending on the settings passed to setup_pipe()
+ */
+static void xmit_fixed(snd_dbri_t * dbri, int pipe, unsigned int data)
+{
+	volatile s32 *cmd;
+
+	if (pipe < 16 || pipe > 31) {
+		printk("DBRI: xmit_fixed: Illegal pipe number\n");
+		return;
+	}
+
+	if (D_SDP_MODE(dbri->pipes[pipe].sdp) == 0) {
+		printk("DBRI: xmit_fixed: Uninitialized pipe %d\n", pipe);
+		return;
+	}
+
+	if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
+		printk("DBRI: xmit_fixed: Non-fixed pipe %d\n", pipe);
+		return;
+	}
+
+	if (!(dbri->pipes[pipe].sdp & D_SDP_TO_SER)) {
+		printk("DBRI: xmit_fixed: Called on receive pipe %d\n", pipe);
+		return;
+	}
+
+	/* DBRI short pipes always transmit LSB first */
+
+	if (dbri->pipes[pipe].sdp & D_SDP_MSB)
+		data = reverse_bytes(data, dbri->pipes[pipe].length);
+
+	cmd = dbri_cmdlock(dbri, GetLock);
+
+	*(cmd++) = DBRI_CMD(D_SSP, 0, pipe);
+	*(cmd++) = data;
+
+	dbri_cmdsend(dbri, cmd);
+}
+
+static void recv_fixed(snd_dbri_t * dbri, int pipe, volatile __u32 * ptr)
+{
+	if (pipe < 16 || pipe > 31) {
+		printk("DBRI: recv_fixed called with illegal pipe number\n");
+		return;
+	}
+
+	if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
+		printk("DBRI: recv_fixed called on non-fixed pipe %d\n", pipe);
+		return;
+	}
+
+	if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
+		printk("DBRI: recv_fixed called on transmit pipe %d\n", pipe);
+		return;
+	}
+
+	dbri->pipes[pipe].recv_fixed_ptr = ptr;
+}
+
+/* setup_descs()
+ *
+ * Setup transmit/receive data on a "long" pipe - i.e, one associated
+ * with a DMA buffer.
+ *
+ * Only pipe numbers 0-15 can be used in this mode.
+ *
+ * This function takes a stream number pointing to a data buffer,
+ * and work by building chains of descriptors which identify the
+ * data buffers.  Buffers too large for a single descriptor will
+ * be spread across multiple descriptors.
+ */
+static int setup_descs(snd_dbri_t * dbri, int streamno, unsigned int period)
+{
+	dbri_streaminfo_t *info = &dbri->stream_info[streamno];
+	__u32 dvma_buffer;
+	int desc = 0;
+	int len;
+	int first_desc = -1;
+	int last_desc = -1;
+
+	if (info->pipe < 0 || info->pipe > 15) {
+		printk("DBRI: setup_descs: Illegal pipe number\n");
+		return -2;
+	}
+
+	if (dbri->pipes[info->pipe].sdp == 0) {
+		printk("DBRI: setup_descs: Uninitialized pipe %d\n",
+		       info->pipe);
+		return -2;
+	}
+
+	dvma_buffer = info->dvma_buffer;
+	len = info->size;
+
+	if (streamno == DBRI_PLAY) {
+		if (!(dbri->pipes[info->pipe].sdp & D_SDP_TO_SER)) {
+			printk("DBRI: setup_descs: Called on receive pipe %d\n",
+			       info->pipe);
+			return -2;
+		}
+	} else {
+		if (dbri->pipes[info->pipe].sdp & D_SDP_TO_SER) {
+			printk
+			    ("DBRI: setup_descs: Called on transmit pipe %d\n",
+			     info->pipe);
+			return -2;
+		}
+		/* Should be able to queue multiple buffers to receive on a pipe */
+		if (pipe_active(dbri, info->pipe)) {
+			printk("DBRI: recv_on_pipe: Called on active pipe %d\n",
+			       info->pipe);
+			return -2;
+		}
+
+		/* Make sure buffer size is multiple of four */
+		len &= ~3;
+	}
+
+	while (len > 0) {
+		int mylen;
+
+		for (; desc < DBRI_NO_DESCS; desc++) {
+			if (!dbri->descs[desc].inuse)
+				break;
+		}
+		if (desc == DBRI_NO_DESCS) {
+			printk("DBRI: setup_descs: No descriptors\n");
+			return -1;
+		}
+
+		if (len > DBRI_TD_MAXCNT) {
+			mylen = DBRI_TD_MAXCNT;	/* 8KB - 1 */
+		} else {
+			mylen = len;
+		}
+		if (mylen > period) {
+			mylen = period;
+		}
+
+		dbri->descs[desc].inuse = 1;
+		dbri->descs[desc].next = -1;
+		dbri->dma->desc[desc].ba = dvma_buffer;
+		dbri->dma->desc[desc].nda = 0;
+
+		if (streamno == DBRI_PLAY) {
+			dbri->descs[desc].len = mylen;
+			dbri->dma->desc[desc].word1 = DBRI_TD_CNT(mylen);
+			dbri->dma->desc[desc].word4 = 0;
+			if (first_desc != -1)
+				dbri->dma->desc[desc].word1 |= DBRI_TD_M;
+		} else {
+			dbri->descs[desc].len = 0;
+			dbri->dma->desc[desc].word1 = 0;
+			dbri->dma->desc[desc].word4 =
+			    DBRI_RD_B | DBRI_RD_BCNT(mylen);
+		}
+
+		if (first_desc == -1) {
+			first_desc = desc;
+		} else {
+			dbri->descs[last_desc].next = desc;
+			dbri->dma->desc[last_desc].nda =
+			    dbri->dma_dvma + dbri_dma_off(desc, desc);
+		}
+
+		last_desc = desc;
+		dvma_buffer += mylen;
+		len -= mylen;
+	}
+
+	if (first_desc == -1 || last_desc == -1) {
+		printk("DBRI: setup_descs: Not enough descriptors available\n");
+		return -1;
+	}
+
+	dbri->dma->desc[last_desc].word1 &= ~DBRI_TD_M;
+	if (streamno == DBRI_PLAY) {
+		dbri->dma->desc[last_desc].word1 |=
+		    DBRI_TD_I | DBRI_TD_F | DBRI_TD_B;
+	}
+	dbri->pipes[info->pipe].first_desc = first_desc;
+	dbri->pipes[info->pipe].desc = first_desc;
+
+	for (desc = first_desc; desc != -1; desc = dbri->descs[desc].next) {
+		dprintk(D_DESC, "DESC %d: %08x %08x %08x %08x\n",
+			desc,
+			dbri->dma->desc[desc].word1,
+			dbri->dma->desc[desc].ba,
+			dbri->dma->desc[desc].nda, dbri->dma->desc[desc].word4);
+	}
+	return 0;
+}
+
+/*
+****************************************************************************
+************************** DBRI - CHI interface ****************************
+****************************************************************************
+
+The CHI is a four-wire (clock, frame sync, data in, data out) time-division
+multiplexed serial interface which the DBRI can operate in either master
+(give clock/frame sync) or slave (take clock/frame sync) mode.
+
+*/
+
+enum master_or_slave { CHImaster, CHIslave };
+
+static void reset_chi(snd_dbri_t * dbri, enum master_or_slave master_or_slave,
+		      int bits_per_frame)
+{
+	volatile s32 *cmd;
+	int val;
+	static int chi_initialized = 0;	/* FIXME: mutex? */
+
+	if (!chi_initialized) {
+
+		cmd = dbri_cmdlock(dbri, GetLock);
+
+		/* Set CHI Anchor: Pipe 16 */
+
+		val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(16) | D_PIPE(16);
+		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
+		*(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
+		*(cmd++) = 0;
+
+		val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(16) | D_PIPE(16);
+		*(cmd++) = DBRI_CMD(D_DTS, 0, val);
+		*(cmd++) = 0;
+		*(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
+
+		dbri->pipes[16].sdp = 1;
+		dbri->pipes[16].nextpipe = 16;
+		dbri->chi_in_pipe = 16;
+		dbri->chi_out_pipe = 16;
+
+#if 0
+		chi_initialized++;
+#endif
+	} else {
+		int pipe;
+
+		for (pipe = dbri->chi_in_pipe;
+		     pipe != 16; pipe = dbri->pipes[pipe].nextpipe) {
+			unlink_time_slot(dbri, pipe, PIPEinput,
+					 16, dbri->pipes[pipe].nextpipe);
+		}
+		for (pipe = dbri->chi_out_pipe;
+		     pipe != 16; pipe = dbri->pipes[pipe].nextpipe) {
+			unlink_time_slot(dbri, pipe, PIPEoutput,
+					 16, dbri->pipes[pipe].nextpipe);
+		}
+
+		dbri->chi_in_pipe = 16;
+		dbri->chi_out_pipe = 16;
+
+		cmd = dbri_cmdlock(dbri, GetLock);
+	}
+
+	if (master_or_slave == CHIslave) {
+		/* Setup DBRI for CHI Slave - receive clock, frame sync (FS)
+		 *
+		 * CHICM  = 0 (slave mode, 8 kHz frame rate)
+		 * IR     = give immediate CHI status interrupt
+		 * EN     = give CHI status interrupt upon change
+		 */
+		*(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0));
+	} else {
+		/* Setup DBRI for CHI Master - generate clock, FS
+		 *
+		 * BPF                          =  bits per 8 kHz frame
+		 * 12.288 MHz / CHICM_divisor   = clock rate
+		 * FD  =  1 - drive CHIFS on rising edge of CHICK
+		 */
+		int clockrate = bits_per_frame * 8;
+		int divisor = 12288 / clockrate;
+
+		if (divisor > 255 || divisor * clockrate != 12288)
+			printk("DBRI: illegal bits_per_frame in setup_chi\n");
+
+		*(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD
+				    | D_CHI_BPF(bits_per_frame));
+	}
+
+	dbri->chi_bpf = bits_per_frame;
+
+	/* CHI Data Mode
+	 *
+	 * RCE   =  0 - receive on falling edge of CHICK
+	 * XCE   =  1 - transmit on rising edge of CHICK
+	 * XEN   =  1 - enable transmitter
+	 * REN   =  1 - enable receiver
+	 */
+
+	*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+	*(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE | D_CDM_XEN | D_CDM_REN);
+
+	dbri_cmdsend(dbri, cmd);
+}
+
+/*
+****************************************************************************
+*********************** CS4215 audio codec management **********************
+****************************************************************************
+
+In the standard SPARC audio configuration, the CS4215 codec is attached
+to the DBRI via the CHI interface and few of the DBRI's PIO pins.
+
+*/
+static void cs4215_setup_pipes(snd_dbri_t * dbri)
+{
+	/*
+	 * Data mode:
+	 * Pipe  4: Send timeslots 1-4 (audio data)
+	 * Pipe 20: Send timeslots 5-8 (part of ctrl data)
+	 * Pipe  6: Receive timeslots 1-4 (audio data)
+	 * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via
+	 *          interrupt, and the rest of the data (slot 5 and 8) is
+	 *          not relevant for us (only for doublechecking).
+	 *
+	 * Control mode:
+	 * Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly)
+	 * Pipe 18: Receive timeslot 1 (clb).
+	 * Pipe 19: Receive timeslot 7 (version). 
+	 */
+
+	setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB);
+	setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
+	setup_pipe(dbri, 6, D_SDP_MEM | D_SDP_FROM_SER | D_SDP_MSB);
+	setup_pipe(dbri, 21, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+
+	setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
+	setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+	setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+}
+
+static int cs4215_init_data(struct cs4215 *mm)
+{
+	/*
+	 * No action, memory resetting only.
+	 *
+	 * Data Time Slot 5-8
+	 * Speaker,Line and Headphone enable. Gain set to the half.
+	 * Input is mike.
+	 */
+	mm->data[0] = CS4215_LO(0x20) | CS4215_HE | CS4215_LE;
+	mm->data[1] = CS4215_RO(0x20) | CS4215_SE;
+	mm->data[2] = CS4215_LG(0x8) | CS4215_IS | CS4215_PIO0 | CS4215_PIO1;
+	mm->data[3] = CS4215_RG(0x8) | CS4215_MA(0xf);
+
+	/*
+	 * Control Time Slot 1-4
+	 * 0: Default I/O voltage scale
+	 * 1: 8 bit ulaw, 8kHz, mono, high pass filter disabled
+	 * 2: Serial enable, CHI master, 128 bits per frame, clock 1
+	 * 3: Tests disabled
+	 */
+	mm->ctrl[0] = CS4215_RSRVD_1 | CS4215_MLB;
+	mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval;
+	mm->ctrl[2] = CS4215_XCLK | CS4215_BSEL_128 | CS4215_FREQ[0].xtal;
+	mm->ctrl[3] = 0;
+
+	mm->status = 0;
+	mm->version = 0xff;
+	mm->precision = 8;	/* For ULAW */
+	mm->channels = 2;
+
+	return 0;
+}
+
+static void cs4215_setdata(snd_dbri_t * dbri, int muted)
+{
+	if (muted) {
+		dbri->mm.data[0] |= 63;
+		dbri->mm.data[1] |= 63;
+		dbri->mm.data[2] &= ~15;
+		dbri->mm.data[3] &= ~15;
+	} else {
+		/* Start by setting the playback attenuation. */
+		dbri_streaminfo_t *info = &dbri->stream_info[DBRI_PLAY];
+		int left_gain = info->left_gain % 64;
+		int right_gain = info->right_gain % 64;
+
+		if (info->balance < DBRI_MID_BALANCE) {
+			right_gain *= info->balance;
+			right_gain /= DBRI_MID_BALANCE;
+		} else {
+			left_gain *= DBRI_RIGHT_BALANCE - info->balance;
+			left_gain /= DBRI_MID_BALANCE;
+		}
+
+		dbri->mm.data[0] &= ~0x3f;	/* Reset the volume bits */
+		dbri->mm.data[1] &= ~0x3f;
+		dbri->mm.data[0] |= (DBRI_MAX_VOLUME - left_gain);
+		dbri->mm.data[1] |= (DBRI_MAX_VOLUME - right_gain);
+
+		/* Now set the recording gain. */
+		info = &dbri->stream_info[DBRI_REC];
+		left_gain = info->left_gain % 16;
+		right_gain = info->right_gain % 16;
+		dbri->mm.data[2] |= CS4215_LG(left_gain);
+		dbri->mm.data[3] |= CS4215_RG(right_gain);
+	}
+
+	xmit_fixed(dbri, 20, *(int *)dbri->mm.data);
+}
+
+/*
+ * Set the CS4215 to data mode.
+ */
+static void cs4215_open(snd_dbri_t * dbri)
+{
+	int data_width;
+	u32 tmp;
+
+	dprintk(D_MM, "cs4215_open: %d channels, %d bits\n",
+		dbri->mm.channels, dbri->mm.precision);
+
+	/* Temporarily mute outputs, and wait 1/8000 sec (125 us)
+	 * to make sure this takes.  This avoids clicking noises.
+	 */
+
+	cs4215_setdata(dbri, 1);
+	udelay(125);
+
+	/*
+	 * Data mode:
+	 * Pipe  4: Send timeslots 1-4 (audio data)
+	 * Pipe 20: Send timeslots 5-8 (part of ctrl data)
+	 * Pipe  6: Receive timeslots 1-4 (audio data)
+	 * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via
+	 *          interrupt, and the rest of the data (slot 5 and 8) is
+	 *          not relevant for us (only for doublechecking).
+	 *
+	 * Just like in control mode, the time slots are all offset by eight
+	 * bits.  The CS4215, it seems, observes TSIN (the delayed signal)
+	 * even if it's the CHI master.  Don't ask me...
+	 */
+	tmp = sbus_readl(dbri->regs + REG0);
+	tmp &= ~(D_C);		/* Disable CHI */
+	sbus_writel(tmp, dbri->regs + REG0);
+
+	/* Switch CS4215 to data mode - set PIO3 to 1 */
+	sbus_writel(D_ENPIO | D_PIO1 | D_PIO3 |
+		    (dbri->mm.onboard ? D_PIO0 : D_PIO2), dbri->regs + REG2);
+
+	reset_chi(dbri, CHIslave, 128);
+
+	/* Note: this next doesn't work for 8-bit stereo, because the two
+	 * channels would be on timeslots 1 and 3, with 2 and 4 idle.
+	 * (See CS4215 datasheet Fig 15)
+	 *
+	 * DBRI non-contiguous mode would be required to make this work.
+	 */
+	data_width = dbri->mm.channels * dbri->mm.precision;
+
+	link_time_slot(dbri, 20, PIPEoutput, 16, 32, dbri->mm.offset + 32);
+	link_time_slot(dbri, 4, PIPEoutput, 16, data_width, dbri->mm.offset);
+	link_time_slot(dbri, 6, PIPEinput, 16, data_width, dbri->mm.offset);
+	link_time_slot(dbri, 21, PIPEinput, 16, 16, dbri->mm.offset + 40);
+
+	/* FIXME: enable CHI after _setdata? */
+	tmp = sbus_readl(dbri->regs + REG0);
+	tmp |= D_C;		/* Enable CHI */
+	sbus_writel(tmp, dbri->regs + REG0);
+
+	cs4215_setdata(dbri, 0);
+}
+
+/*
+ * Send the control information (i.e. audio format)
+ */
+static int cs4215_setctrl(snd_dbri_t * dbri)
+{
+	int i, val;
+	u32 tmp;
+
+	/* FIXME - let the CPU do something useful during these delays */
+
+	/* Temporarily mute outputs, and wait 1/8000 sec (125 us)
+	 * to make sure this takes.  This avoids clicking noises.
+	 */
+
+	cs4215_setdata(dbri, 1);
+	udelay(125);
+
+	/*
+	 * Enable Control mode: Set DBRI's PIO3 (4215's D/~C) to 0, then wait
+	 * 12 cycles <= 12/(5512.5*64) sec = 34.01 usec
+	 */
+	val = D_ENPIO | D_PIO1 | (dbri->mm.onboard ? D_PIO0 : D_PIO2);
+	sbus_writel(val, dbri->regs + REG2);
+	dprintk(D_MM, "cs4215_setctrl: reg2=0x%x\n", val);
+	udelay(34);
+
+	/* In Control mode, the CS4215 is a slave device, so the DBRI must
+	 * operate as CHI master, supplying clocking and frame synchronization.
+	 *
+	 * In Data mode, however, the CS4215 must be CHI master to insure
+	 * that its data stream is synchronous with its codec.
+	 *
+	 * The upshot of all this?  We start by putting the DBRI into master
+	 * mode, program the CS4215 in Control mode, then switch the CS4215
+	 * into Data mode and put the DBRI into slave mode.  Various timing
+	 * requirements must be observed along the way.
+	 *
+	 * Oh, and one more thing, on a SPARCStation 20 (and maybe
+	 * others?), the addressing of the CS4215's time slots is
+	 * offset by eight bits, so we add eight to all the "cycle"
+	 * values in the Define Time Slot (DTS) commands.  This is
+	 * done in hardware by a TI 248 that delays the DBRI->4215
+	 * frame sync signal by eight clock cycles.  Anybody know why?
+	 */
+	tmp = sbus_readl(dbri->regs + REG0);
+	tmp &= ~D_C;		/* Disable CHI */
+	sbus_writel(tmp, dbri->regs + REG0);
+
+	reset_chi(dbri, CHImaster, 128);
+
+	/*
+	 * Control mode:
+	 * Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly)
+	 * Pipe 18: Receive timeslot 1 (clb).
+	 * Pipe 19: Receive timeslot 7 (version). 
+	 */
+
+	link_time_slot(dbri, 17, PIPEoutput, 16, 32, dbri->mm.offset);
+	link_time_slot(dbri, 18, PIPEinput, 16, 8, dbri->mm.offset);
+	link_time_slot(dbri, 19, PIPEinput, 16, 8, dbri->mm.offset + 48);
+
+	/* Wait for the chip to echo back CLB (Control Latch Bit) as zero */
+	dbri->mm.ctrl[0] &= ~CS4215_CLB;
+	xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl);
+
+	tmp = sbus_readl(dbri->regs + REG0);
+	tmp |= D_C;		/* Enable CHI */
+	sbus_writel(tmp, dbri->regs + REG0);
+
+	for (i = 64; ((dbri->mm.status & 0xe4) != 0x20); --i) {
+		udelay(125);
+	}
+	if (i == 0) {
+		dprintk(D_MM, "CS4215 didn't respond to CLB (0x%02x)\n",
+			dbri->mm.status);
+		return -1;
+	}
+
+	/* Disable changes to our copy of the version number, as we are about
+	 * to leave control mode.
+	 */
+	recv_fixed(dbri, 19, NULL);
+
+	/* Terminate CS4215 control mode - data sheet says
+	 * "Set CLB=1 and send two more frames of valid control info"
+	 */
+	dbri->mm.ctrl[0] |= CS4215_CLB;
+	xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl);
+
+	/* Two frames of control info @ 8kHz frame rate = 250 us delay */
+	udelay(250);
+
+	cs4215_setdata(dbri, 0);
+
+	return 0;
+}
+
+/*
+ * Setup the codec with the sampling rate, audio format and number of
+ * channels.
+ * As part of the process we resend the settings for the data
+ * timeslots as well.
+ */
+static int cs4215_prepare(snd_dbri_t * dbri, unsigned int rate,
+			  snd_pcm_format_t format, unsigned int channels)
+{
+	int freq_idx;
+	int ret = 0;
+
+	/* Lookup index for this rate */
+	for (freq_idx = 0; CS4215_FREQ[freq_idx].freq != 0; freq_idx++) {
+		if (CS4215_FREQ[freq_idx].freq == rate)
+			break;
+	}
+	if (CS4215_FREQ[freq_idx].freq != rate) {
+		printk(KERN_WARNING "DBRI: Unsupported rate %d Hz\n", rate);
+		return -1;
+	}
+
+	switch (format) {
+	case SNDRV_PCM_FORMAT_MU_LAW:
+		dbri->mm.ctrl[1] = CS4215_DFR_ULAW;
+		dbri->mm.precision = 8;
+		break;
+	case SNDRV_PCM_FORMAT_A_LAW:
+		dbri->mm.ctrl[1] = CS4215_DFR_ALAW;
+		dbri->mm.precision = 8;
+		break;
+	case SNDRV_PCM_FORMAT_U8:
+		dbri->mm.ctrl[1] = CS4215_DFR_LINEAR8;
+		dbri->mm.precision = 8;
+		break;
+	case SNDRV_PCM_FORMAT_S16_BE:
+		dbri->mm.ctrl[1] = CS4215_DFR_LINEAR16;
+		dbri->mm.precision = 16;
+		break;
+	default:
+		printk(KERN_WARNING "DBRI: Unsupported format %d\n", format);
+		return -1;
+	}
+
+	/* Add rate parameters */
+	dbri->mm.ctrl[1] |= CS4215_FREQ[freq_idx].csval;
+	dbri->mm.ctrl[2] = CS4215_XCLK |
+	    CS4215_BSEL_128 | CS4215_FREQ[freq_idx].xtal;
+
+	dbri->mm.channels = channels;
+	/* Stereo bit: 8 bit stereo not working yet. */
+	if ((channels > 1) && (dbri->mm.precision == 16))
+		dbri->mm.ctrl[1] |= CS4215_DFR_STEREO;
+
+	ret = cs4215_setctrl(dbri);
+	if (ret == 0)
+		cs4215_open(dbri);	/* set codec to data mode */
+
+	return ret;
+}
+
+/*
+ *
+ */
+static int cs4215_init(snd_dbri_t * dbri)
+{
+	u32 reg2 = sbus_readl(dbri->regs + REG2);
+	dprintk(D_MM, "cs4215_init: reg2=0x%x\n", reg2);
+
+	/* Look for the cs4215 chips */
+	if (reg2 & D_PIO2) {
+		dprintk(D_MM, "Onboard CS4215 detected\n");
+		dbri->mm.onboard = 1;
+	}
+	if (reg2 & D_PIO0) {
+		dprintk(D_MM, "Speakerbox detected\n");
+		dbri->mm.onboard = 0;
+
+		if (reg2 & D_PIO2) {
+			printk(KERN_INFO "DBRI: Using speakerbox / "
+			       "ignoring onboard mmcodec.\n");
+			sbus_writel(D_ENPIO2, dbri->regs + REG2);
+		}
+	}
+
+	if (!(reg2 & (D_PIO0 | D_PIO2))) {
+		printk(KERN_ERR "DBRI: no mmcodec found.\n");
+		return -EIO;
+	}
+
+	cs4215_setup_pipes(dbri);
+
+	cs4215_init_data(&dbri->mm);
+
+	/* Enable capture of the status & version timeslots. */
+	recv_fixed(dbri, 18, &dbri->mm.status);
+	recv_fixed(dbri, 19, &dbri->mm.version);
+
+	dbri->mm.offset = dbri->mm.onboard ? 0 : 8;
+	if (cs4215_setctrl(dbri) == -1 || dbri->mm.version == 0xff) {
+		dprintk(D_MM, "CS4215 failed probe at offset %d\n",
+			dbri->mm.offset);
+		return -EIO;
+	}
+	dprintk(D_MM, "Found CS4215 at offset %d\n", dbri->mm.offset);
+
+	return 0;
+}
+
+/*
+****************************************************************************
+*************************** DBRI interrupt handler *************************
+****************************************************************************
+
+The DBRI communicates with the CPU mainly via a circular interrupt
+buffer.  When an interrupt is signaled, the CPU walks through the
+buffer and calls dbri_process_one_interrupt() for each interrupt word.
+Complicated interrupts are handled by dedicated functions (which
+appear first in this file).  Any pending interrupts can be serviced by
+calling dbri_process_interrupt_buffer(), which works even if the CPU's
+interrupts are disabled.  This function is used by dbri_cmdsend()
+to make sure we're synced up with the chip after each command sequence,
+even if we're running cli'ed.
+
+*/
+
+/* xmit_descs()
+ *
+ * Transmit the current TD's for recording/playing, if needed.
+ * For playback, ALSA has filled the DMA memory with new data (we hope).
+ */
+static void xmit_descs(unsigned long data)
+{
+	snd_dbri_t *dbri = (snd_dbri_t *) data;
+	dbri_streaminfo_t *info;
+	volatile s32 *cmd;
+	unsigned long flags;
+	int first_td;
+
+	if (dbri == NULL)
+		return;		/* Disabled */
+
+	/* First check the recording stream for buffer overflow */
+	info = &dbri->stream_info[DBRI_REC];
+	spin_lock_irqsave(&dbri->lock, flags);
+
+	if ((info->left >= info->size) && (info->pipe >= 0)) {
+		first_td = dbri->pipes[info->pipe].first_desc;
+
+		dprintk(D_DESC, "xmit_descs rec @ TD %d\n", first_td);
+
+		/* Stream could be closed by the time we run. */
+		if (first_td < 0) {
+			goto play;
+		}
+
+		cmd = dbri_cmdlock(dbri, NoGetLock);
+		*(cmd++) = DBRI_CMD(D_SDP, 0,
+				    dbri->pipes[info->pipe].sdp
+				    | D_SDP_P | D_SDP_EVERY | D_SDP_C);
+		*(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td);
+		dbri_cmdsend(dbri, cmd);
+
+		/* Reset our admin of the pipe & bytes read. */
+		dbri->pipes[info->pipe].desc = first_td;
+		info->left = 0;
+	}
+
+play:
+	spin_unlock_irqrestore(&dbri->lock, flags);
+
+	/* Now check the playback stream for buffer underflow */
+	info = &dbri->stream_info[DBRI_PLAY];
+	spin_lock_irqsave(&dbri->lock, flags);
+
+	if ((info->left <= 0) && (info->pipe >= 0)) {
+		first_td = dbri->pipes[info->pipe].first_desc;
+
+		dprintk(D_DESC, "xmit_descs play @ TD %d\n", first_td);
+
+		/* Stream could be closed by the time we run. */
+		if (first_td < 0) {
+			spin_unlock_irqrestore(&dbri->lock, flags);
+			return;
+		}
+
+		cmd = dbri_cmdlock(dbri, NoGetLock);
+		*(cmd++) = DBRI_CMD(D_SDP, 0,
+				    dbri->pipes[info->pipe].sdp
+				    | D_SDP_P | D_SDP_EVERY | D_SDP_C);
+		*(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td);
+		dbri_cmdsend(dbri, cmd);
+
+		/* Reset our admin of the pipe & bytes written. */
+		dbri->pipes[info->pipe].desc = first_td;
+		info->left = info->size;
+	}
+	spin_unlock_irqrestore(&dbri->lock, flags);
+}
+
+DECLARE_TASKLET(xmit_descs_task, xmit_descs, 0);
+
+/* transmission_complete_intr()
+ *
+ * Called by main interrupt handler when DBRI signals transmission complete
+ * on a pipe (interrupt triggered by the B bit in a transmit descriptor).
+ *
+ * Walks through the pipe's list of transmit buffer descriptors, releasing
+ * each one's DMA buffer (if present), flagging the descriptor available,
+ * and signaling its callback routine (if present), before proceeding
+ * to the next one.  Stops when the first descriptor is found without
+ * TBC (Transmit Buffer Complete) set, or we've run through them all.
+ */
+
+static void transmission_complete_intr(snd_dbri_t * dbri, int pipe)
+{
+	dbri_streaminfo_t *info;
+	int td;
+	int status;
+
+	info = &dbri->stream_info[DBRI_PLAY];
+
+	td = dbri->pipes[pipe].desc;
+	while (td >= 0) {
+		if (td >= DBRI_NO_DESCS) {
+			printk(KERN_ERR "DBRI: invalid td on pipe %d\n", pipe);
+			return;
+		}
+
+		status = DBRI_TD_STATUS(dbri->dma->desc[td].word4);
+		if (!(status & DBRI_TD_TBC)) {
+			break;
+		}
+
+		dprintk(D_INT, "TD %d, status 0x%02x\n", td, status);
+
+		dbri->dma->desc[td].word4 = 0;	/* Reset it for next time. */
+		info->offset += dbri->descs[td].len;
+		info->left -= dbri->descs[td].len;
+
+		/* On the last TD, transmit them all again. */
+		if (dbri->descs[td].next == -1) {
+			if (info->left > 0) {
+				printk(KERN_WARNING
+				       "%d bytes left after last transfer.\n",
+				       info->left);
+				info->left = 0;
+			}
+			tasklet_schedule(&xmit_descs_task);
+		}
+
+		td = dbri->descs[td].next;
+		dbri->pipes[pipe].desc = td;
+	}
+
+	/* Notify ALSA */
+	if (spin_is_locked(&dbri->lock)) {
+		spin_unlock(&dbri->lock);
+		snd_pcm_period_elapsed(info->substream);
+		spin_lock(&dbri->lock);
+	} else
+		snd_pcm_period_elapsed(info->substream);
+}
+
+static void reception_complete_intr(snd_dbri_t * dbri, int pipe)
+{
+	dbri_streaminfo_t *info;
+	int rd = dbri->pipes[pipe].desc;
+	s32 status;
+
+	if (rd < 0 || rd >= DBRI_NO_DESCS) {
+		printk(KERN_ERR "DBRI: invalid rd on pipe %d\n", pipe);
+		return;
+	}
+
+	dbri->descs[rd].inuse = 0;
+	dbri->pipes[pipe].desc = dbri->descs[rd].next;
+	status = dbri->dma->desc[rd].word1;
+	dbri->dma->desc[rd].word1 = 0;	/* Reset it for next time. */
+
+	info = &dbri->stream_info[DBRI_REC];
+	info->offset += DBRI_RD_CNT(status);
+	info->left += DBRI_RD_CNT(status);
+
+	/* FIXME: Check status */
+
+	dprintk(D_INT, "Recv RD %d, status 0x%02x, len %d\n",
+		rd, DBRI_RD_STATUS(status), DBRI_RD_CNT(status));
+
+	/* On the last TD, transmit them all again. */
+	if (dbri->descs[rd].next == -1) {
+		if (info->left > info->size) {
+			printk(KERN_WARNING
+			       "%d bytes recorded in %d size buffer.\n",
+			       info->left, info->size);
+		}
+		tasklet_schedule(&xmit_descs_task);
+	}
+
+	/* Notify ALSA */
+	if (spin_is_locked(&dbri->lock)) {
+		spin_unlock(&dbri->lock);
+		snd_pcm_period_elapsed(info->substream);
+		spin_lock(&dbri->lock);
+	} else
+		snd_pcm_period_elapsed(info->substream);
+}
+
+static void dbri_process_one_interrupt(snd_dbri_t * dbri, int x)
+{
+	int val = D_INTR_GETVAL(x);
+	int channel = D_INTR_GETCHAN(x);
+	int command = D_INTR_GETCMD(x);
+	int code = D_INTR_GETCODE(x);
+#ifdef DBRI_DEBUG
+	int rval = D_INTR_GETRVAL(x);
+#endif
+
+	if (channel == D_INTR_CMD) {
+		dprintk(D_CMD, "INTR: Command: %-5s  Value:%d\n",
+			cmds[command], val);
+	} else {
+		dprintk(D_INT, "INTR: Chan:%d Code:%d Val:%#x\n",
+			channel, code, rval);
+	}
+
+	if (channel == D_INTR_CMD && command == D_WAIT) {
+		dbri->wait_seen++;
+		return;
+	}
+
+	switch (code) {
+	case D_INTR_BRDY:
+		reception_complete_intr(dbri, channel);
+		break;
+	case D_INTR_XCMP:
+	case D_INTR_MINT:
+		transmission_complete_intr(dbri, channel);
+		break;
+	case D_INTR_UNDR:
+		/* UNDR - Transmission underrun
+		 * resend SDP command with clear pipe bit (C) set
+		 */
+		{
+			volatile s32 *cmd;
+
+			int pipe = channel;
+			int td = dbri->pipes[pipe].desc;
+
+			dbri->dma->desc[td].word4 = 0;
+			cmd = dbri_cmdlock(dbri, NoGetLock);
+			*(cmd++) = DBRI_CMD(D_SDP, 0,
+					    dbri->pipes[pipe].sdp
+					    | D_SDP_P | D_SDP_C | D_SDP_2SAME);
+			*(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, td);
+			dbri_cmdsend(dbri, cmd);
+		}
+		break;
+	case D_INTR_FXDT:
+		/* FXDT - Fixed data change */
+		if (dbri->pipes[channel].sdp & D_SDP_MSB)
+			val = reverse_bytes(val, dbri->pipes[channel].length);
+
+		if (dbri->pipes[channel].recv_fixed_ptr)
+			*(dbri->pipes[channel].recv_fixed_ptr) = val;
+		break;
+	default:
+		if (channel != D_INTR_CMD)
+			printk(KERN_WARNING
+			       "DBRI: Ignored Interrupt: %d (0x%x)\n", code, x);
+	}
+}
+
+/* dbri_process_interrupt_buffer advances through the DBRI's interrupt
+ * buffer until it finds a zero word (indicating nothing more to do
+ * right now).  Non-zero words require processing and are handed off
+ * to dbri_process_one_interrupt AFTER advancing the pointer.  This
+ * order is important since we might recurse back into this function
+ * and need to make sure the pointer has been advanced first.
+ */
+static void dbri_process_interrupt_buffer(snd_dbri_t * dbri)
+{
+	s32 x;
+
+	while ((x = dbri->dma->intr[dbri->dbri_irqp]) != 0) {
+		dbri->dma->intr[dbri->dbri_irqp] = 0;
+		dbri->dbri_irqp++;
+		if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK))
+			dbri->dbri_irqp = 1;
+		else if ((dbri->dbri_irqp & (DBRI_INT_BLK - 1)) == 0)
+			dbri->dbri_irqp++;
+
+		dbri_process_one_interrupt(dbri, x);
+	}
+}
+
+static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id,
+				      struct pt_regs *regs)
+{
+	snd_dbri_t *dbri = dev_id;
+	static int errcnt = 0;
+	int x;
+
+	if (dbri == NULL)
+		return IRQ_NONE;
+	spin_lock(&dbri->lock);
+
+	/*
+	 * Read it, so the interrupt goes away.
+	 */
+	x = sbus_readl(dbri->regs + REG1);
+
+	if (x & (D_MRR | D_MLE | D_LBG | D_MBE)) {
+		u32 tmp;
+
+		if (x & D_MRR)
+			printk(KERN_ERR
+			       "DBRI: Multiple Error Ack on SBus reg1=0x%x\n",
+			       x);
+		if (x & D_MLE)
+			printk(KERN_ERR
+			       "DBRI: Multiple Late Error on SBus reg1=0x%x\n",
+			       x);
+		if (x & D_LBG)
+			printk(KERN_ERR
+			       "DBRI: Lost Bus Grant on SBus reg1=0x%x\n", x);
+		if (x & D_MBE)
+			printk(KERN_ERR
+			       "DBRI: Burst Error on SBus reg1=0x%x\n", x);
+
+		/* Some of these SBus errors cause the chip's SBus circuitry
+		 * to be disabled, so just re-enable and try to keep going.
+		 *
+		 * The only one I've seen is MRR, which will be triggered
+		 * if you let a transmit pipe underrun, then try to CDP it.
+		 *
+		 * If these things persist, we should probably reset
+		 * and re-init the chip.
+		 */
+		if ((++errcnt) % 10 == 0) {
+			dprintk(D_INT, "Interrupt errors exceeded.\n");
+			dbri_reset(dbri);
+		} else {
+			tmp = sbus_readl(dbri->regs + REG0);
+			tmp &= ~(D_D);
+			sbus_writel(tmp, dbri->regs + REG0);
+		}
+	}
+
+	dbri_process_interrupt_buffer(dbri);
+
+	/* FIXME: Write 0 into regs to ACK interrupt */
+
+	spin_unlock(&dbri->lock);
+
+	return IRQ_HANDLED;
+}
+
+/****************************************************************************
+		PCM Interface
+****************************************************************************/
+static snd_pcm_hardware_t snd_dbri_pcm_hw = {
+	.info			= (SNDRV_PCM_INFO_MMAP |
+				   SNDRV_PCM_INFO_INTERLEAVED |
+				   SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				   SNDRV_PCM_INFO_MMAP_VALID),
+	.formats		= SNDRV_PCM_FMTBIT_MU_LAW |
+				  SNDRV_PCM_FMTBIT_A_LAW |
+				  SNDRV_PCM_FMTBIT_U8 |
+				  SNDRV_PCM_FMTBIT_S16_BE,
+	.rates			= SNDRV_PCM_RATE_8000_48000,
+	.rate_min		= 8000,
+	.rate_max		= 48000,
+	.channels_min		= 1,
+	.channels_max		= 2,
+	.buffer_bytes_max	= (64 * 1024),
+	.period_bytes_min	= 1,
+	.period_bytes_max	= DBRI_TD_MAXCNT,
+	.periods_min		= 1,
+	.periods_max		= 1024,
+};
+
+static int snd_dbri_open(snd_pcm_substream_t * substream)
+{
+	snd_dbri_t *dbri = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream);
+	unsigned long flags;
+
+	dprintk(D_USR, "open audio output.\n");
+	runtime->hw = snd_dbri_pcm_hw;
+
+	spin_lock_irqsave(&dbri->lock, flags);
+	info->substream = substream;
+	info->left = 0;
+	info->offset = 0;
+	info->dvma_buffer = 0;
+	info->pipe = -1;
+	spin_unlock_irqrestore(&dbri->lock, flags);
+
+	cs4215_open(dbri);
+
+	return 0;
+}
+
+static int snd_dbri_close(snd_pcm_substream_t * substream)
+{
+	snd_dbri_t *dbri = snd_pcm_substream_chip(substream);
+	dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream);
+
+	dprintk(D_USR, "close audio output.\n");
+	info->substream = NULL;
+	info->left = 0;
+	info->offset = 0;
+
+	return 0;
+}
+
+static int snd_dbri_hw_params(snd_pcm_substream_t * substream,
+			      snd_pcm_hw_params_t * hw_params)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_dbri_t *dbri = snd_pcm_substream_chip(substream);
+	dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream);
+	int direction;
+	int ret;
+
+	/* set sampling rate, audio format and number of channels */
+	ret = cs4215_prepare(dbri, params_rate(hw_params),
+			     params_format(hw_params),
+			     params_channels(hw_params));
+	if (ret != 0)
+		return ret;
+
+	if ((ret = snd_pcm_lib_malloc_pages(substream,
+				params_buffer_bytes(hw_params))) < 0) {
+		snd_printk(KERN_ERR "malloc_pages failed with %d\n", ret);
+		return ret;
+	}
+
+	/* hw_params can get called multiple times. Only map the DMA once.
+	 */
+	if (info->dvma_buffer == 0) {
+		if (DBRI_STREAMNO(substream) == DBRI_PLAY)
+			direction = SBUS_DMA_TODEVICE;
+		else
+			direction = SBUS_DMA_FROMDEVICE;
+
+		info->dvma_buffer = sbus_map_single(dbri->sdev,
+					runtime->dma_area,
+					params_buffer_bytes(hw_params),
+					direction);
+	}
+
+	direction = params_buffer_bytes(hw_params);
+	dprintk(D_USR, "hw_params: %d bytes, dvma=%x\n",
+		direction, info->dvma_buffer);
+	return 0;
+}
+
+static int snd_dbri_hw_free(snd_pcm_substream_t * substream)
+{
+	snd_dbri_t *dbri = snd_pcm_substream_chip(substream);
+	dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream);
+	int direction;
+	dprintk(D_USR, "hw_free.\n");
+
+	/* hw_free can get called multiple times. Only unmap the DMA once.
+	 */
+	if (info->dvma_buffer) {
+		if (DBRI_STREAMNO(substream) == DBRI_PLAY)
+			direction = SBUS_DMA_TODEVICE;
+		else
+			direction = SBUS_DMA_FROMDEVICE;
+
+		sbus_unmap_single(dbri->sdev, info->dvma_buffer,
+				  substream->runtime->buffer_size, direction);
+		info->dvma_buffer = 0;
+	}
+	info->pipe = -1;
+
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_dbri_prepare(snd_pcm_substream_t * substream)
+{
+	snd_dbri_t *dbri = snd_pcm_substream_chip(substream);
+	dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int ret;
+
+	info->size = snd_pcm_lib_buffer_bytes(substream);
+	if (DBRI_STREAMNO(substream) == DBRI_PLAY)
+		info->pipe = 4;	/* Send pipe */
+	else {
+		info->pipe = 6;	/* Receive pipe */
+		info->left = info->size;	/* To trigger submittal */
+	}
+
+	spin_lock_irq(&dbri->lock);
+
+	/* Setup the all the transmit/receive desciptors to cover the
+	 * whole DMA buffer.
+	 */
+	ret = setup_descs(dbri, DBRI_STREAMNO(substream),
+			  snd_pcm_lib_period_bytes(substream));
+
+	runtime->stop_threshold = DBRI_TD_MAXCNT / runtime->channels;
+
+	spin_unlock_irq(&dbri->lock);
+
+	dprintk(D_USR, "prepare audio output. %d bytes\n", info->size);
+	return ret;
+}
+
+static int snd_dbri_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+	snd_dbri_t *dbri = snd_pcm_substream_chip(substream);
+	dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream);
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dprintk(D_USR, "start audio, period is %d bytes\n",
+			(int)snd_pcm_lib_period_bytes(substream));
+		/* Enable & schedule the tasklet that re-submits the TDs. */
+		xmit_descs_task.data = (unsigned long)dbri;
+		tasklet_schedule(&xmit_descs_task);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		dprintk(D_USR, "stop audio.\n");
+		/* Make the tasklet bail out immediately. */
+		xmit_descs_task.data = 0;
+		reset_pipe(dbri, info->pipe);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t snd_dbri_pointer(snd_pcm_substream_t * substream)
+{
+	snd_dbri_t *dbri = snd_pcm_substream_chip(substream);
+	dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream);
+	snd_pcm_uframes_t ret;
+
+	ret = bytes_to_frames(substream->runtime, info->offset)
+		% substream->runtime->buffer_size;
+	dprintk(D_USR, "I/O pointer: %ld frames, %d bytes left.\n",
+		ret, info->left);
+	return ret;
+}
+
+static snd_pcm_ops_t snd_dbri_ops = {
+	.open = snd_dbri_open,
+	.close = snd_dbri_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = snd_dbri_hw_params,
+	.hw_free = snd_dbri_hw_free,
+	.prepare = snd_dbri_prepare,
+	.trigger = snd_dbri_trigger,
+	.pointer = snd_dbri_pointer,
+};
+
+static int __devinit snd_dbri_pcm(snd_dbri_t * dbri)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(dbri->card,
+			       /* ID */		    "sun_dbri",
+			       /* device */	    0,
+			       /* playback count */ 1,
+			       /* capture count */  1, &pcm)) < 0)
+		return err;
+	snd_assert(pcm != NULL, return -EINVAL);
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dbri_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_dbri_ops);
+
+	pcm->private_data = dbri;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, dbri->card->shortname);
+	dbri->pcm = pcm;
+
+	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm,
+			SNDRV_DMA_TYPE_CONTINUOUS,
+			snd_dma_continuous_data(GFP_KERNEL),
+			64 * 1024, 64 * 1024)) < 0) {
+		return err;
+	}
+
+	return 0;
+}
+
+/*****************************************************************************
+			Mixer interface
+*****************************************************************************/
+
+static int snd_cs4215_info_volume(snd_kcontrol_t * kcontrol,
+				  snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	if (kcontrol->private_value == DBRI_PLAY) {
+		uinfo->value.integer.max = DBRI_MAX_VOLUME;
+	} else {
+		uinfo->value.integer.max = DBRI_MAX_GAIN;
+	}
+	return 0;
+}
+
+static int snd_cs4215_get_volume(snd_kcontrol_t * kcontrol,
+				 snd_ctl_elem_value_t * ucontrol)
+{
+	snd_dbri_t *dbri = snd_kcontrol_chip(kcontrol);
+	dbri_streaminfo_t *info;
+	snd_assert(dbri != NULL, return -EINVAL);
+	info = &dbri->stream_info[kcontrol->private_value];
+	snd_assert(info != NULL, return -EINVAL);
+
+	ucontrol->value.integer.value[0] = info->left_gain;
+	ucontrol->value.integer.value[1] = info->right_gain;
+	return 0;
+}
+
+static int snd_cs4215_put_volume(snd_kcontrol_t * kcontrol,
+				 snd_ctl_elem_value_t * ucontrol)
+{
+	snd_dbri_t *dbri = snd_kcontrol_chip(kcontrol);
+	dbri_streaminfo_t *info = &dbri->stream_info[kcontrol->private_value];
+	unsigned long flags;
+	int changed = 0;
+
+	if (info->left_gain != ucontrol->value.integer.value[0]) {
+		info->left_gain = ucontrol->value.integer.value[0];
+		changed = 1;
+	}
+	if (info->right_gain != ucontrol->value.integer.value[1]) {
+		info->right_gain = ucontrol->value.integer.value[1];
+		changed = 1;
+	}
+	if (changed == 1) {
+		/* First mute outputs, and wait 1/8000 sec (125 us)
+		 * to make sure this takes.  This avoids clicking noises.
+		 */
+		spin_lock_irqsave(&dbri->lock, flags);
+
+		cs4215_setdata(dbri, 1);
+		udelay(125);
+		cs4215_setdata(dbri, 0);
+
+		spin_unlock_irqrestore(&dbri->lock, flags);
+	}
+	return changed;
+}
+
+static int snd_cs4215_info_single(snd_kcontrol_t * kcontrol,
+				  snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = (mask == 1) ?
+	    SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_cs4215_get_single(snd_kcontrol_t * kcontrol,
+				 snd_ctl_elem_value_t * ucontrol)
+{
+	snd_dbri_t *dbri = snd_kcontrol_chip(kcontrol);
+	int elem = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 1;
+	snd_assert(dbri != NULL, return -EINVAL);
+
+	if (elem < 4) {
+		ucontrol->value.integer.value[0] =
+		    (dbri->mm.data[elem] >> shift) & mask;
+	} else {
+		ucontrol->value.integer.value[0] =
+		    (dbri->mm.ctrl[elem - 4] >> shift) & mask;
+	}
+
+	if (invert == 1) {
+		ucontrol->value.integer.value[0] =
+		    mask - ucontrol->value.integer.value[0];
+	}
+	return 0;
+}
+
+static int snd_cs4215_put_single(snd_kcontrol_t * kcontrol,
+				 snd_ctl_elem_value_t * ucontrol)
+{
+	snd_dbri_t *dbri = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int elem = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 1;
+	int changed = 0;
+	unsigned short val;
+	snd_assert(dbri != NULL, return -EINVAL);
+
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert == 1)
+		val = mask - val;
+	val <<= shift;
+
+	if (elem < 4) {
+		dbri->mm.data[elem] = (dbri->mm.data[elem] &
+				       ~(mask << shift)) | val;
+		changed = (val != dbri->mm.data[elem]);
+	} else {
+		dbri->mm.ctrl[elem - 4] = (dbri->mm.ctrl[elem - 4] &
+					   ~(mask << shift)) | val;
+		changed = (val != dbri->mm.ctrl[elem - 4]);
+	}
+
+	dprintk(D_GEN, "put_single: mask=0x%x, changed=%d, "
+		"mixer-value=%ld, mm-value=0x%x\n",
+		mask, changed, ucontrol->value.integer.value[0],
+		dbri->mm.data[elem & 3]);
+
+	if (changed) {
+		/* First mute outputs, and wait 1/8000 sec (125 us)
+		 * to make sure this takes.  This avoids clicking noises.
+		 */
+		spin_lock_irqsave(&dbri->lock, flags);
+
+		cs4215_setdata(dbri, 1);
+		udelay(125);
+		cs4215_setdata(dbri, 0);
+
+		spin_unlock_irqrestore(&dbri->lock, flags);
+	}
+	return changed;
+}
+
+/* Entries 0-3 map to the 4 data timeslots, entries 4-7 map to the 4 control
+   timeslots. Shift is the bit offset in the timeslot, mask defines the
+   number of bits. invert is a boolean for use with attenuation.
+ */
+#define CS4215_SINGLE(xname, entry, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+  .info = snd_cs4215_info_single, \
+  .get = snd_cs4215_get_single, .put = snd_cs4215_put_single, \
+  .private_value = entry | (shift << 8) | (mask << 16) | (invert << 24) },
+
+static snd_kcontrol_new_t dbri_controls[] __devinitdata = {
+	{
+	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	 .name  = "Playback Volume",
+	 .info  = snd_cs4215_info_volume,
+	 .get   = snd_cs4215_get_volume,
+	 .put   = snd_cs4215_put_volume,
+	 .private_value = DBRI_PLAY,
+	 },
+	CS4215_SINGLE("Headphone switch", 0, 7, 1, 0)
+	CS4215_SINGLE("Line out switch", 0, 6, 1, 0)
+	CS4215_SINGLE("Speaker switch", 1, 6, 1, 0)
+	{
+	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	 .name  = "Capture Volume",
+	 .info  = snd_cs4215_info_volume,
+	 .get   = snd_cs4215_get_volume,
+	 .put   = snd_cs4215_put_volume,
+	 .private_value = DBRI_REC,
+	 },
+	/* FIXME: mic/line switch */
+	CS4215_SINGLE("Line in switch", 2, 4, 1, 0)
+	CS4215_SINGLE("High Pass Filter switch", 5, 7, 1, 0)
+	CS4215_SINGLE("Monitor Volume", 3, 4, 0xf, 1)
+	CS4215_SINGLE("Mic boost", 4, 4, 1, 1)
+};
+
+#define NUM_CS4215_CONTROLS (sizeof(dbri_controls)/sizeof(snd_kcontrol_new_t))
+
+static int __init snd_dbri_mixer(snd_dbri_t * dbri)
+{
+	snd_card_t *card;
+	int idx, err;
+
+	snd_assert(dbri != NULL && dbri->card != NULL, return -EINVAL);
+
+	card = dbri->card;
+	strcpy(card->mixername, card->shortname);
+
+	for (idx = 0; idx < NUM_CS4215_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card,
+				       snd_ctl_new1(&dbri_controls[idx],
+						    dbri))) < 0)
+			return err;
+	}
+
+	for (idx = DBRI_REC; idx < DBRI_NO_STREAMS; idx++) {
+		dbri->stream_info[idx].left_gain = 0;
+		dbri->stream_info[idx].right_gain = 0;
+		dbri->stream_info[idx].balance = DBRI_MID_BALANCE;
+	}
+
+	return 0;
+}
+
+/****************************************************************************
+			/proc interface
+****************************************************************************/
+static void dbri_regs_read(snd_info_entry_t * entry, snd_info_buffer_t * buffer)
+{
+	snd_dbri_t *dbri = entry->private_data;
+
+	snd_iprintf(buffer, "REG0: 0x%x\n", sbus_readl(dbri->regs + REG0));
+	snd_iprintf(buffer, "REG2: 0x%x\n", sbus_readl(dbri->regs + REG2));
+	snd_iprintf(buffer, "REG8: 0x%x\n", sbus_readl(dbri->regs + REG8));
+	snd_iprintf(buffer, "REG9: 0x%x\n", sbus_readl(dbri->regs + REG9));
+}
+
+#ifdef DBRI_DEBUG
+static void dbri_debug_read(snd_info_entry_t * entry,
+			    snd_info_buffer_t * buffer)
+{
+	snd_dbri_t *dbri = entry->private_data;
+	int pipe;
+	snd_iprintf(buffer, "debug=%d\n", dbri_debug);
+
+	snd_iprintf(buffer, "CHI pipe in=%d, out=%d\n",
+		    dbri->chi_in_pipe, dbri->chi_out_pipe);
+	for (pipe = 0; pipe < 32; pipe++) {
+		if (pipe_active(dbri, pipe)) {
+			struct dbri_pipe *pptr = &dbri->pipes[pipe];
+			snd_iprintf(buffer,
+				    "Pipe %d: %s SDP=0x%x desc=%d, "
+				    "len=%d @ %d prev: %d next %d\n",
+				    pipe,
+				    (pptr->direction ==
+				     PIPEinput ? "input" : "output"), pptr->sdp,
+				    pptr->desc, pptr->length, pptr->cycle,
+				    pptr->prevpipe, pptr->nextpipe);
+		}
+	}
+}
+
+static void dbri_debug_write(snd_info_entry_t * entry,
+			     snd_info_buffer_t * buffer)
+{
+	char line[80];
+	int i;
+
+	if (snd_info_get_line(buffer, line, 80) == 0) {
+		sscanf(line, "%d\n", &i);
+		dbri_debug = i & 0x3f;
+	}
+}
+#endif
+
+void snd_dbri_proc(snd_dbri_t * dbri)
+{
+	snd_info_entry_t *entry;
+	int err;
+
+	err = snd_card_proc_new(dbri->card, "regs", &entry);
+	snd_info_set_text_ops(entry, dbri, 1024, dbri_regs_read);
+
+#ifdef DBRI_DEBUG
+	err = snd_card_proc_new(dbri->card, "debug", &entry);
+	snd_info_set_text_ops(entry, dbri, 4096, dbri_debug_read);
+	entry->mode = S_IFREG | S_IRUGO | S_IWUSR; /* Writable for root */
+	entry->c.text.write_size = 256;
+	entry->c.text.write = dbri_debug_write;
+#endif
+}
+
+/*
+****************************************************************************
+**************************** Initialization ********************************
+****************************************************************************
+*/
+static void snd_dbri_free(snd_dbri_t * dbri);
+
+static int __init snd_dbri_create(snd_card_t * card,
+				  struct sbus_dev *sdev,
+				  struct linux_prom_irqs *irq, int dev)
+{
+	snd_dbri_t *dbri = card->private_data;
+	int err;
+
+	spin_lock_init(&dbri->lock);
+	dbri->card = card;
+	dbri->sdev = sdev;
+	dbri->irq = irq->pri;
+	dbri->dbri_version = sdev->prom_name[9];
+
+	dbri->dma = sbus_alloc_consistent(sdev, sizeof(struct dbri_dma),
+					  &dbri->dma_dvma);
+	memset((void *)dbri->dma, 0, sizeof(struct dbri_dma));
+
+	dprintk(D_GEN, "DMA Cmd Block 0x%p (0x%08x)\n",
+		dbri->dma, dbri->dma_dvma);
+
+	/* Map the registers into memory. */
+	dbri->regs_size = sdev->reg_addrs[0].reg_size;
+	dbri->regs = sbus_ioremap(&sdev->resource[0], 0,
+				  dbri->regs_size, "DBRI Registers");
+	if (!dbri->regs) {
+		printk(KERN_ERR "DBRI: could not allocate registers\n");
+		sbus_free_consistent(sdev, sizeof(struct dbri_dma),
+				     (void *)dbri->dma, dbri->dma_dvma);
+		return -EIO;
+	}
+
+	err = request_irq(dbri->irq, snd_dbri_interrupt, SA_SHIRQ,
+			  "DBRI audio", dbri);
+	if (err) {
+		printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq);
+		sbus_iounmap(dbri->regs, dbri->regs_size);
+		sbus_free_consistent(sdev, sizeof(struct dbri_dma),
+				     (void *)dbri->dma, dbri->dma_dvma);
+		return err;
+	}
+
+	/* Do low level initialization of the DBRI and CS4215 chips */
+	dbri_initialize(dbri);
+	err = cs4215_init(dbri);
+	if (err) {
+		snd_dbri_free(dbri);
+		return err;
+	}
+
+	dbri->next = dbri_list;
+	dbri_list = dbri;
+
+	return 0;
+}
+
+static void snd_dbri_free(snd_dbri_t * dbri)
+{
+	dprintk(D_GEN, "snd_dbri_free\n");
+	dbri_reset(dbri);
+
+	if (dbri->irq)
+		free_irq(dbri->irq, dbri);
+
+	if (dbri->regs)
+		sbus_iounmap(dbri->regs, dbri->regs_size);
+
+	if (dbri->dma)
+		sbus_free_consistent(dbri->sdev, sizeof(struct dbri_dma),
+				     (void *)dbri->dma, dbri->dma_dvma);
+}
+
+static int __init dbri_attach(int prom_node, struct sbus_dev *sdev)
+{
+	snd_dbri_t *dbri;
+	struct linux_prom_irqs irq;
+	struct resource *rp;
+	snd_card_t *card;
+	static int dev = 0;
+	int err;
+
+	if (sdev->prom_name[9] < 'e') {
+		printk(KERN_ERR "DBRI: unsupported chip version %c found.\n",
+		       sdev->prom_name[9]);
+		return -EIO;
+	}
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	prom_getproperty(prom_node, "intr", (char *)&irq, sizeof(irq));
+
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+			    sizeof(snd_dbri_t));
+	if (card == NULL)
+		return -ENOMEM;
+
+	strcpy(card->driver, "DBRI");
+	strcpy(card->shortname, "Sun DBRI");
+	rp = &sdev->resource[0];
+	sprintf(card->longname, "%s at 0x%02lx:0x%08lx, irq %s",
+		card->shortname,
+		rp->flags & 0xffL, rp->start, __irq_itoa(irq.pri));
+
+	if ((err = snd_dbri_create(card, sdev, &irq, dev)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	dbri = (snd_dbri_t *) card->private_data;
+	if ((err = snd_dbri_pcm(dbri)) < 0) {
+		snd_dbri_free(dbri);
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_dbri_mixer(dbri)) < 0) {
+		snd_dbri_free(dbri);
+		snd_card_free(card);
+		return err;
+	}
+
+	/* /proc file handling */
+	snd_dbri_proc(dbri);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_dbri_free(dbri);
+		snd_card_free(card);
+		return err;
+	}
+
+	printk(KERN_INFO "audio%d at %p (irq %d) is DBRI(%c)+CS4215(%d)\n",
+	       dev, dbri->regs,
+	       dbri->irq, dbri->dbri_version, dbri->mm.version);
+	dev++;
+
+	return 0;
+}
+
+/* Probe for the dbri chip and then attach the driver. */
+static int __init dbri_init(void)
+{
+	struct sbus_bus *sbus;
+	struct sbus_dev *sdev;
+	int found = 0;
+
+	/* Probe each SBUS for the DBRI chip(s). */
+	for_all_sbusdev(sdev, sbus) {
+		/*
+		 * The version is coded in the last character
+		 */
+		if (!strncmp(sdev->prom_name, "SUNW,DBRI", 9)) {
+			dprintk(D_GEN, "DBRI: Found %s in SBUS slot %d\n",
+				sdev->prom_name, sdev->slot);
+
+			if (dbri_attach(sdev->prom_node, sdev) == 0)
+				found++;
+		}
+	}
+
+	return (found > 0) ? 0 : -EIO;
+}
+
+static void __exit dbri_exit(void)
+{
+	snd_dbri_t *this = dbri_list;
+
+	while (this != NULL) {
+		snd_dbri_t *next = this->next;
+		snd_card_t *card = this->card;
+
+		snd_dbri_free(this);
+		snd_card_free(card);
+		this = next;
+	}
+	dbri_list = NULL;
+}
+
+module_init(dbri_init);
+module_exit(dbri_exit);
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index b5e734d..8298c46 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -153,6 +153,7 @@
 	unsigned int format;     /* USB data format */
 	unsigned int datapipe;   /* the data i/o pipe */
 	unsigned int syncpipe;   /* 1 - async out or adaptive in */
+	unsigned int datainterval;	/* log_2 of data packet interval */
 	unsigned int syncinterval;  /* P for adaptive mode, 0 otherwise */
 	unsigned int freqn;      /* nominal sampling rate in fs/fps in Q16.16 format */
 	unsigned int freqm;      /* momentary sampling rate in fs/fps in Q16.16 format */
@@ -518,7 +519,8 @@
 		if (subs->fill_max)
 			counts = subs->maxframesize; /* fixed */
 		else {
-			subs->phase = (subs->phase & 0xffff) + subs->freqm;
+			subs->phase = (subs->phase & 0xffff)
+				+ (subs->freqm << subs->datainterval);
 			counts = subs->phase >> 16;
 			if (counts > subs->maxframesize)
 				counts = subs->maxframesize;
@@ -790,7 +792,7 @@
  */
 static int wait_clear_urbs(snd_usb_substream_t *subs)
 {
-	int timeout = HZ;
+	unsigned long end_time = jiffies + msecs_to_jiffies(1000);
 	unsigned int i;
 	int alive;
 
@@ -810,7 +812,7 @@
 			break;
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout(1);
-	} while (--timeout > 0);
+	} while (time_before(jiffies, end_time));
 	if (alive)
 		snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
 	return 0;
@@ -899,16 +901,19 @@
 	else
 		subs->freqn = get_usb_high_speed_rate(rate);
 	subs->freqm = subs->freqn;
-	subs->freqmax = subs->freqn + (subs->freqn >> 2); /* max. allowed frequency */
-	subs->phase = 0;
-
-	/* calculate the max. size of packet */
-	maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3)) >> 16;
-	if (subs->maxpacksize && maxsize > subs->maxpacksize) {
-		//snd_printd(KERN_DEBUG "maxsize %d is greater than defined size %d\n",
-		//	   maxsize, subs->maxpacksize);
+	/* calculate max. frequency */
+	if (subs->maxpacksize) {
+		/* whatever fits into a max. size packet */
 		maxsize = subs->maxpacksize;
+		subs->freqmax = (maxsize / (frame_bits >> 3))
+				<< (16 - subs->datainterval);
+	} else {
+		/* no max. packet size: just take 25% higher than nominal */
+		subs->freqmax = subs->freqn + (subs->freqn >> 2);
+		maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))
+				>> (16 - subs->datainterval);
 	}
+	subs->phase = 0;
 
 	if (subs->fill_max)
 		subs->curpacksize = subs->maxpacksize;
@@ -918,7 +923,7 @@
 	if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
 		urb_packs = nrpacks;
 	else
-		urb_packs = nrpacks * 8;
+		urb_packs = (nrpacks * 8) >> subs->datainterval;
 
 	/* allocate a temporary buffer for playback */
 	if (is_playback) {
@@ -991,7 +996,7 @@
 		u->urb->pipe = subs->datapipe;
 		u->urb->transfer_flags = URB_ISO_ASAP;
 		u->urb->number_of_packets = u->packets;
-		u->urb->interval = 1;
+		u->urb->interval = 1 << subs->datainterval;
 		u->urb->context = u;
 		u->urb->complete = snd_usb_complete_callback(snd_complete_urb);
 	}
@@ -1195,6 +1200,12 @@
 		subs->datapipe = usb_sndisocpipe(dev, ep);
 	else
 		subs->datapipe = usb_rcvisocpipe(dev, ep);
+	if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH &&
+	    get_endpoint(alts, 0)->bInterval >= 1 &&
+	    get_endpoint(alts, 0)->bInterval <= 4)
+		subs->datainterval = get_endpoint(alts, 0)->bInterval - 1;
+	else
+		subs->datainterval = 0;
 	subs->syncpipe = subs->syncinterval = 0;
 	subs->maxpacksize = fmt->maxpacksize;
 	subs->fill_max = 0;
@@ -2397,10 +2408,9 @@
 	if (chip->usb_id == USB_ID(0x041e, 0x3000) ||
 	    chip->usb_id == USB_ID(0x041e, 0x3020)) {
 		if (fmt[3] == USB_FORMAT_TYPE_I &&
-		    stream == SNDRV_PCM_STREAM_PLAYBACK &&
 		    fp->rates != SNDRV_PCM_RATE_48000 &&
 		    fp->rates != SNDRV_PCM_RATE_96000)
-			return -1; /* use 48k only */
+			return -1;
 	}
 #endif
 	return 0;
@@ -2492,8 +2502,10 @@
 		fp->altset_idx = i;
 		fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
 		fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
-		/* FIXME: decode wMaxPacketSize of high bandwith endpoints */
 		fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+		if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
+			fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
+					* (fp->maxpacksize & 0x7ff);
 		fp->attributes = csep[3];
 
 		/* some quirks for attributes here */
@@ -2723,7 +2735,8 @@
  * to detect the sample rate is by looking at wMaxPacketSize.
  */
 static int create_ua700_ua25_quirk(snd_usb_audio_t *chip,
-				   struct usb_interface *iface)
+				   struct usb_interface *iface,
+				   const snd_usb_audio_quirk_t *quirk)
 {
 	static const struct audioformat ua_format = {
 		.format = SNDRV_PCM_FORMAT_S24_3LE,
@@ -2814,7 +2827,9 @@
 /*
  * Create a stream for an Edirol UA-1000 interface.
  */
-static int create_ua1000_quirk(snd_usb_audio_t *chip, struct usb_interface *iface)
+static int create_ua1000_quirk(snd_usb_audio_t *chip,
+			       struct usb_interface *iface,
+			       const snd_usb_audio_quirk_t *quirk)
 {
 	static const struct audioformat ua1000_format = {
 		.format = SNDRV_PCM_FORMAT_S32_LE,
@@ -2891,6 +2906,13 @@
 	return 0;
 }
 
+static int ignore_interface_quirk(snd_usb_audio_t *chip,
+				  struct usb_interface *iface,
+				  const snd_usb_audio_quirk_t *quirk)
+{
+	return 0;
+}
+
 
 /*
  * boot quirks
@@ -2926,8 +2948,6 @@
 
 static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
 {
-#if 0
-	/* TODO: enable this when high speed synchronization actually works */
 	u8 buf = 1;
 
 	snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a,
@@ -2939,7 +2959,6 @@
 				1, 2000, NULL, 0, 1000);
 		return -ENODEV;
 	}
-#endif
 	return 0;
 }
 
@@ -2956,28 +2975,28 @@
 				struct usb_interface *iface,
 				const snd_usb_audio_quirk_t *quirk)
 {
-	switch (quirk->type) {
-	case QUIRK_MIDI_FIXED_ENDPOINT:
-	case QUIRK_MIDI_YAMAHA:
-	case QUIRK_MIDI_MIDIMAN:
-	case QUIRK_MIDI_NOVATION:
-	case QUIRK_MIDI_MOTU:
-	case QUIRK_MIDI_EMAGIC:
-		return snd_usb_create_midi_interface(chip, iface, quirk);
-	case QUIRK_COMPOSITE:
-		return create_composite_quirk(chip, iface, quirk);
-	case QUIRK_AUDIO_FIXED_ENDPOINT:
-		return create_fixed_stream_quirk(chip, iface, quirk);
-	case QUIRK_AUDIO_STANDARD_INTERFACE:
-	case QUIRK_MIDI_STANDARD_INTERFACE:
-		return create_standard_interface_quirk(chip, iface, quirk);
-	case QUIRK_AUDIO_EDIROL_UA700_UA25:
-		return create_ua700_ua25_quirk(chip, iface);
-	case QUIRK_AUDIO_EDIROL_UA1000:
-		return create_ua1000_quirk(chip, iface);
-	case QUIRK_IGNORE_INTERFACE:
-		return 0;
-	default:
+	typedef int (*quirk_func_t)(snd_usb_audio_t *, struct usb_interface *,
+				    const snd_usb_audio_quirk_t *);
+	static const quirk_func_t quirk_funcs[] = {
+		[QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
+		[QUIRK_COMPOSITE] = create_composite_quirk,
+		[QUIRK_MIDI_STANDARD_INTERFACE] = snd_usb_create_midi_interface,
+		[QUIRK_MIDI_FIXED_ENDPOINT] = snd_usb_create_midi_interface,
+		[QUIRK_MIDI_YAMAHA] = snd_usb_create_midi_interface,
+		[QUIRK_MIDI_MIDIMAN] = snd_usb_create_midi_interface,
+		[QUIRK_MIDI_NOVATION] = snd_usb_create_midi_interface,
+		[QUIRK_MIDI_RAW] = snd_usb_create_midi_interface,
+		[QUIRK_MIDI_EMAGIC] = snd_usb_create_midi_interface,
+		[QUIRK_MIDI_MIDITECH] = snd_usb_create_midi_interface,
+		[QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_interface_quirk,
+		[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
+		[QUIRK_AUDIO_EDIROL_UA700_UA25] = create_ua700_ua25_quirk,
+		[QUIRK_AUDIO_EDIROL_UA1000] = create_ua1000_quirk,
+	};
+
+	if (quirk->type < QUIRK_TYPE_COUNT) {
+		return quirk_funcs[quirk->type](chip, iface, quirk);
+	} else {
 		snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type);
 		return -ENXIO;
 	}
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index aedb42a..ad9eab2 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -153,20 +153,24 @@
 #define QUIRK_NO_INTERFACE		-2
 #define QUIRK_ANY_INTERFACE		-1
 
-/* quirk type */
-#define QUIRK_MIDI_FIXED_ENDPOINT	0
-#define QUIRK_MIDI_YAMAHA		1
-#define QUIRK_MIDI_MIDIMAN		2
-#define QUIRK_COMPOSITE			3
-#define QUIRK_AUDIO_FIXED_ENDPOINT	4
-#define QUIRK_AUDIO_STANDARD_INTERFACE	5
-#define QUIRK_MIDI_STANDARD_INTERFACE	6
-#define QUIRK_AUDIO_EDIROL_UA700_UA25	7
-#define QUIRK_AUDIO_EDIROL_UA1000	8
-#define QUIRK_IGNORE_INTERFACE		9
-#define QUIRK_MIDI_NOVATION		10
-#define QUIRK_MIDI_MOTU			11
-#define QUIRK_MIDI_EMAGIC		12
+enum quirk_type {
+	QUIRK_IGNORE_INTERFACE,
+	QUIRK_COMPOSITE,
+	QUIRK_MIDI_STANDARD_INTERFACE,
+	QUIRK_MIDI_FIXED_ENDPOINT,
+	QUIRK_MIDI_YAMAHA,
+	QUIRK_MIDI_MIDIMAN,
+	QUIRK_MIDI_NOVATION,
+	QUIRK_MIDI_RAW,
+	QUIRK_MIDI_EMAGIC,
+	QUIRK_MIDI_MIDITECH,
+	QUIRK_AUDIO_STANDARD_INTERFACE,
+	QUIRK_AUDIO_FIXED_ENDPOINT,
+	QUIRK_AUDIO_EDIROL_UA700_UA25,
+	QUIRK_AUDIO_EDIROL_UA1000,
+
+	QUIRK_TYPE_COUNT
+};
 
 typedef struct snd_usb_audio_quirk snd_usb_audio_quirk_t;
 typedef struct snd_usb_midi_endpoint_info snd_usb_midi_endpoint_info_t;
@@ -175,7 +179,7 @@
 	const char *vendor_name;
 	const char *product_name;
 	int16_t ifnum;
-	int16_t type;
+	uint16_t type;
 	const void *data;
 };
 
@@ -205,11 +209,13 @@
 
 /* for QUIRK_IGNORE_INTERFACE, data is NULL */
 
-/* for QUIRK_MIDI_NOVATION and _MOTU, data is NULL */
+/* for QUIRK_MIDI_NOVATION and _RAW, data is NULL */
 
 /* for QUIRK_MIDI_EMAGIC, data points to a snd_usb_midi_endpoint_info
  * structure (out_cables and in_cables only) */
 
+/* for QUIRK_MIDI_MIDITECH, data is NULL */
+
 /*
  */
 
diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index bee7006..5778a9b 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -524,16 +524,16 @@
 };
 
 /*
- * Mark of the Unicorn USB MIDI protocol: raw MIDI.
+ * "raw" protocol: used by the MOTU FastLane.
  */
 
-static void snd_usbmidi_motu_input(snd_usb_midi_in_endpoint_t* ep,
-				   uint8_t* buffer, int buffer_length)
+static void snd_usbmidi_raw_input(snd_usb_midi_in_endpoint_t* ep,
+				  uint8_t* buffer, int buffer_length)
 {
 	snd_usbmidi_input_data(ep, 0, buffer, buffer_length);
 }
 
-static void snd_usbmidi_motu_output(snd_usb_midi_out_endpoint_t* ep)
+static void snd_usbmidi_raw_output(snd_usb_midi_out_endpoint_t* ep)
 {
 	int count;
 
@@ -549,9 +549,9 @@
 	ep->urb->transfer_buffer_length = count;
 }
 
-static struct usb_protocol_ops snd_usbmidi_motu_ops = {
-	.input = snd_usbmidi_motu_input,
-	.output = snd_usbmidi_motu_output,
+static struct usb_protocol_ops snd_usbmidi_raw_ops = {
+	.input = snd_usbmidi_raw_input,
+	.output = snd_usbmidi_raw_output,
 };
 
 /*
@@ -1505,8 +1505,8 @@
 			umidi->usb_protocol_ops = &snd_usbmidi_novation_ops;
 			err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
 			break;
-		case QUIRK_MIDI_MOTU:
-			umidi->usb_protocol_ops = &snd_usbmidi_motu_ops;
+		case QUIRK_MIDI_RAW:
+			umidi->usb_protocol_ops = &snd_usbmidi_raw_ops;
 			err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
 			break;
 		case QUIRK_MIDI_EMAGIC:
@@ -1515,6 +1515,9 @@
 			       sizeof(snd_usb_midi_endpoint_info_t));
 			err = snd_usbmidi_detect_endpoints(umidi, &endpoints[0], 1);
 			break;
+		case QUIRK_MIDI_MIDITECH:
+			err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
+			break;
 		default:
 			snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type);
 			err = -ENXIO;
diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
index f513564..f74e652 100644
--- a/sound/usb/usbquirks.h
+++ b/sound/usb/usbquirks.h
@@ -116,6 +116,7 @@
 YAMAHA_DEVICE(0x103a, NULL),
 YAMAHA_DEVICE(0x103b, NULL),
 YAMAHA_DEVICE(0x103c, NULL),
+YAMAHA_DEVICE(0x103d, NULL),
 YAMAHA_DEVICE(0x2000, "DGP-7"),
 YAMAHA_DEVICE(0x2001, "DGP-5"),
 YAMAHA_DEVICE(0x2002, NULL),
@@ -1259,7 +1260,12 @@
 /* Mark of the Unicorn devices */
 {
 	/* thanks to Robert A. Lerche <ral 'at' msbit.com> */
-	USB_DEVICE(0x07fd, 0x0001),
+	.match_flags = USB_DEVICE_ID_MATCH_VENDOR |
+		       USB_DEVICE_ID_MATCH_PRODUCT |
+		       USB_DEVICE_ID_MATCH_DEV_SUBCLASS,
+	.idVendor = 0x07fd,
+	.idProduct = 0x0001,
+	.bDeviceSubClass = 2,
 	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
 		.vendor_name = "MOTU",
 		.product_name = "Fastlane",
@@ -1268,7 +1274,7 @@
 		.data = & (const snd_usb_audio_quirk_t[]) {
 			{
 				.ifnum = 0,
-				.type = QUIRK_MIDI_MOTU
+				.type = QUIRK_MIDI_RAW
 			},
 			{
 				.ifnum = 1,
@@ -1373,6 +1379,25 @@
 },
 
 {
+	USB_DEVICE(0x4752, 0x0011),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Miditech",
+		.product_name = "Midistart-2",
+		.ifnum = 0,
+		.type = QUIRK_MIDI_MIDITECH
+	}
+},
+{
+	USB_DEVICE(0x7104, 0x2202),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Miditech",
+		.product_name = "MidiStudio-2",
+		.ifnum = 0,
+		.type = QUIRK_MIDI_MIDITECH
+	}
+},
+
+{
 	/*
 	 * Some USB MIDI devices don't have an audio control interface,
 	 * so we have to grab MIDI streaming interfaces here.
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index bef9b0c..0281a36 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -232,8 +232,7 @@
 	if (err)
 		return err;
 	if (dsp->index == 1) {
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(HZ/4);			// give the device some time 
+		msleep(250);				// give the device some time
 		err = usX2Y_AsyncSeq04_init(priv);
 		if (err) {
 			snd_printk("usX2Y_AsyncSeq04_init error \n");
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
index bb2c8e9..ef280612 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.c
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -50,6 +50,7 @@
    Currently rawusb dma pcm buffer transport (this file) is only available to snd-usb-usx2y. 
 */
 
+#include <linux/delay.h>
 #include "usbusx2yaudio.c"
 
 #if defined(USX2Y_NRPACKS_VARIABLE) || (!defined(USX2Y_NRPACKS_VARIABLE) &&  USX2Y_NRPACKS == 1)
@@ -520,11 +521,8 @@
 		usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
 		if (atomic_read(&subs->state) < state_PREPARED) {
 			while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) > usX2Y->hwdep_pcm_shm->captured_iso_frames) {
-				signed long timeout;
 				snd_printd("Wait: iso_frames_per_buffer=%i,captured_iso_frames=%i\n", usX2Y_iso_frames_per_buffer(runtime, usX2Y), usX2Y->hwdep_pcm_shm->captured_iso_frames);
-				set_current_state(TASK_INTERRUPTIBLE);
-				timeout = schedule_timeout(HZ/100 + 1);
-				if (signal_pending(current)) {
+				if (msleep_interruptible(10)) {
 					err = -ERESTARTSYS;
 					goto up_prepare_mutex;
 				}