summary refs log tree commit diff
path: root/sound
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-06-29 11:53:31 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-29 11:53:31 -0700
commit0950c358ee8e969fce45ba363ca1deaf211e57b0 (patch)
tree4c3b66e8457e1568aa26696d268e0e9c264382cb /sound
parent3aa590c6b7c89d844f81c2e96f295cf2c6967773 (diff)
parent8caf7aa26e0797e5706043f94c491acd1a08636a (diff)
downloadlinux-0950c358ee8e969fce45ba363ca1deaf211e57b0.tar.gz
Merge master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa
* master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa:
  [ALSA] echoaudio - Remove kfree_nocheck()
  [ALSA] echoaudio - Fix Makefile
  [ALSA] Add Intel D965 board support
  [ALSA] Fix/add support of Realtek ALC883 / ALC888 and ALC861 codecs
  [ALSA] Fix a typo in echoaudio/midi.c
  [ALSA] snd-aoa: enable dual-edge in GPIOs
  [ALSA] snd-aoa: support iMac G5 iSight
  [ALSA] snd-aoa: not experimental
  [ALSA] Add echoaudio sound drivers
  [ALSA] ak4xxx-adda - Code clean-up
  [ALSA] Remove CONFIG_EXPERIMENTAL from intel8x0m driver
  [ALSA] Stereo controls for M-Audio Revolution cards
  [ALSA] Fix misuse of __list_add() in seq_ports.c
  [ALSA] hda-codec - Add model entry for Samsung X60 Chane
  [ALSA] make CONFIG_SND_DYNAMIC_MINORS non-experimental
  [ALSA] Fix wrong dependencies of snd-aoa driver
  [ALSA] fix build failure due to snd-aoa
  [ALSA] AD1888 mixer controls for DC mode
  [ALSA] Suppress irq handler mismatch messages in ALSA ISA drivers
  [ALSA] usb-audio support for Turtle Beach Roadie
Diffstat (limited to 'sound')
-rw-r--r--sound/Makefile3
-rw-r--r--sound/aoa/Kconfig3
-rw-r--r--sound/aoa/core/snd-aoa-gpio-feature.c15
-rw-r--r--sound/aoa/fabrics/snd-aoa-fabric-layout.c14
-rw-r--r--sound/aoa/soundbus/Kconfig3
-rw-r--r--sound/core/Kconfig4
-rw-r--r--sound/core/seq/seq_ports.c6
-rw-r--r--sound/i2c/other/ak4xxx-adda.c284
-rw-r--r--sound/pci/Kconfig141
-rw-r--r--sound/pci/Makefile1
-rw-r--r--sound/pci/ac97/ac97_patch.c2
-rw-r--r--sound/pci/echoaudio/Makefile30
-rw-r--r--sound/pci/echoaudio/darla20.c99
-rw-r--r--sound/pci/echoaudio/darla20_dsp.c125
-rw-r--r--sound/pci/echoaudio/darla24.c106
-rw-r--r--sound/pci/echoaudio/darla24_dsp.c156
-rw-r--r--sound/pci/echoaudio/echo3g.c118
-rw-r--r--sound/pci/echoaudio/echo3g_dsp.c131
-rw-r--r--sound/pci/echoaudio/echoaudio.c2196
-rw-r--r--sound/pci/echoaudio/echoaudio.h590
-rw-r--r--sound/pci/echoaudio/echoaudio_3g.c431
-rw-r--r--sound/pci/echoaudio/echoaudio_dsp.c1125
-rw-r--r--sound/pci/echoaudio/echoaudio_dsp.h694
-rw-r--r--sound/pci/echoaudio/echoaudio_gml.c198
-rw-r--r--sound/pci/echoaudio/gina20.c103
-rw-r--r--sound/pci/echoaudio/gina20_dsp.c215
-rw-r--r--sound/pci/echoaudio/gina24.c123
-rw-r--r--sound/pci/echoaudio/gina24_dsp.c346
-rw-r--r--sound/pci/echoaudio/indigo.c104
-rw-r--r--sound/pci/echoaudio/indigo_dsp.c170
-rw-r--r--sound/pci/echoaudio/indigodj.c104
-rw-r--r--sound/pci/echoaudio/indigodj_dsp.c170
-rw-r--r--sound/pci/echoaudio/indigoio.c105
-rw-r--r--sound/pci/echoaudio/indigoio_dsp.c141
-rw-r--r--sound/pci/echoaudio/layla20.c112
-rw-r--r--sound/pci/echoaudio/layla20_dsp.c290
-rw-r--r--sound/pci/echoaudio/layla24.c121
-rw-r--r--sound/pci/echoaudio/layla24_dsp.c394
-rw-r--r--sound/pci/echoaudio/mia.c117
-rw-r--r--sound/pci/echoaudio/mia_dsp.c229
-rw-r--r--sound/pci/echoaudio/midi.c327
-rw-r--r--sound/pci/echoaudio/mona.c129
-rw-r--r--sound/pci/echoaudio/mona_dsp.c428
-rw-r--r--sound/pci/hda/hda_codec.c4
-rw-r--r--sound/pci/hda/patch_analog.c2
-rw-r--r--sound/pci/hda/patch_realtek.c1084
-rw-r--r--sound/pci/hda/patch_sigmatel.c110
-rw-r--r--sound/pci/ice1712/revo.c23
-rw-r--r--sound/usb/usbaudio.c32
49 files changed, 11263 insertions, 195 deletions
diff --git a/sound/Makefile b/sound/Makefile
index a682ea30f0c9..1f60797afa8a 100644
--- a/sound/Makefile
+++ b/sound/Makefile
@@ -4,7 +4,8 @@
 obj-$(CONFIG_SOUND) += soundcore.o
 obj-$(CONFIG_SOUND_PRIME) += oss/
 obj-$(CONFIG_DMASOUND) += oss/
-obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ aoa/
+obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/
+obj-$(CONFIG_SND_AOA) += aoa/
 
 ifeq ($(CONFIG_SND),y)
   obj-y += last.o
diff --git a/sound/aoa/Kconfig b/sound/aoa/Kconfig
index a85194fe0b06..2f4334d19ccd 100644
--- a/sound/aoa/Kconfig
+++ b/sound/aoa/Kconfig
@@ -3,7 +3,8 @@ menu "Apple Onboard Audio driver"
 
 config SND_AOA
 	tristate "Apple Onboard Audio driver"
-	depends on SOUND && SND_PCM
+	depends on SND
+	select SND_PCM
 	---help---
 	This option enables the new driver for the various
 	Apple Onboard Audio components.
diff --git a/sound/aoa/core/snd-aoa-gpio-feature.c b/sound/aoa/core/snd-aoa-gpio-feature.c
index 2c6eb7784cc9..bab97547a052 100644
--- a/sound/aoa/core/snd-aoa-gpio-feature.c
+++ b/sound/aoa/core/snd-aoa-gpio-feature.c
@@ -207,6 +207,17 @@ static void ftr_handle_notify(void *data)
 	mutex_unlock(&notif->mutex);
 }
 
+static void gpio_enable_dual_edge(int gpio)
+{
+	int v;
+
+	if (gpio == -1)
+		return;
+	v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0);
+	v |= 0x80; /* enable dual edge */
+	pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio, v);
+}
+
 static void ftr_gpio_init(struct gpio_runtime *rt)
 {
 	get_gpio("headphone-mute", NULL,
@@ -234,6 +245,10 @@ static void ftr_gpio_init(struct gpio_runtime *rt)
 				      &linein_detect_gpio,
 				      &linein_detect_gpio_activestate);
 
+	gpio_enable_dual_edge(headphone_detect_gpio);
+	gpio_enable_dual_edge(lineout_detect_gpio);
+	gpio_enable_dual_edge(linein_detect_gpio);
+
 	get_irq(headphone_detect_node, &headphone_detect_irq);
 	get_irq(lineout_detect_node, &lineout_detect_irq);
 	get_irq(linein_detect_node, &linein_detect_irq);
diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c
index 04a7238e9494..cbc8a3b5cea4 100644
--- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c
+++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c
@@ -94,6 +94,7 @@ MODULE_ALIAS("sound-layout-82");
 MODULE_ALIAS("sound-layout-84");
 MODULE_ALIAS("sound-layout-86");
 MODULE_ALIAS("sound-layout-92");
+MODULE_ALIAS("sound-layout-96");
 
 /* onyx with all but microphone connected */
 static struct codec_connection onyx_connections_nomic[] = {
@@ -381,6 +382,13 @@ static struct layout layouts[] = {
 		.connections = toonie_connections,
 	  },
 	},
+	{
+	  .layout_id = 96,
+	  .codecs[0] = {
+	  	.name = "onyx",
+	  	.connections = onyx_connections_noheadphones,
+	  },
+	},
 	/* unknown, untested, but this comes from Apple */
 	{ .layout_id = 41,
 	  .codecs[0] = {
@@ -479,12 +487,6 @@ static struct layout layouts[] = {
 		.connections = onyx_connections_noheadphones,
 	  },
 	},
-	{ .layout_id = 96,
-	  .codecs[0] = {
-		.name = "onyx",
-		.connections = onyx_connections_noheadphones,
-	  },
-	},
 	{ .layout_id = 98,
 	  .codecs[0] = {
 		.name = "toonie",
diff --git a/sound/aoa/soundbus/Kconfig b/sound/aoa/soundbus/Kconfig
index d532d27a9f54..7368b7ddfe0d 100644
--- a/sound/aoa/soundbus/Kconfig
+++ b/sound/aoa/soundbus/Kconfig
@@ -1,6 +1,7 @@
 config SND_AOA_SOUNDBUS
 	tristate "Apple Soundbus support"
-	depends on SOUND && SND_PCM && EXPERIMENTAL
+	depends on SOUND
+	select SND_PCM
 	---help---
 	This option enables the generic driver for the soundbus
 	support on Apple machines.
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 4262a1c87731..b2927523d79d 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -122,8 +122,8 @@ config SND_SEQ_RTCTIMER_DEFAULT
 	  If in doubt, say Y.
 
 config SND_DYNAMIC_MINORS
-	bool "Dynamic device file minor numbers (EXPERIMENTAL)"
-	depends on SND && EXPERIMENTAL
+	bool "Dynamic device file minor numbers"
+	depends on SND
 	help
 	  If you say Y here, the minor numbers of ALSA device files in
 	  /dev/snd/ are allocated dynamically.  This allows you to have
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 334579a9f268..d467b4f0ff2b 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -322,10 +322,8 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
 	mutex_lock(&client->ports_mutex);
 	write_lock_irqsave(&client->ports_lock, flags);
 	if (! list_empty(&client->ports_list_head)) {
-		__list_add(&deleted_list,
-			   client->ports_list_head.prev,
-			   client->ports_list_head.next);
-		INIT_LIST_HEAD(&client->ports_list_head);
+		list_add(&deleted_list, &client->ports_list_head);
+		list_del_init(&client->ports_list_head);
 	} else {
 		INIT_LIST_HEAD(&deleted_list);
 	}
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index 045e32a311e0..dc7cc2001b74 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -34,7 +34,8 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>");
 MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx  AD/DA converters");
 MODULE_LICENSE("GPL");
 
-void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg, unsigned char val)
+void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg,
+		       unsigned char val)
 {
 	ak->ops.lock(ak, chip);
 	ak->ops.write(ak, chip, reg, val);
@@ -52,6 +53,67 @@ void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg, unsi
 	ak->ops.unlock(ak, chip);
 }
 
+EXPORT_SYMBOL(snd_akm4xxx_write);
+
+/* reset procedure for AK4524 and AK4528 */
+static void ak4524_reset(struct snd_akm4xxx *ak, int state)
+{
+	unsigned int chip;
+	unsigned char reg, maxreg;
+
+	if (ak->type == SND_AK4528)
+		maxreg = 0x06;
+	else
+		maxreg = 0x08;
+	for (chip = 0; chip < ak->num_dacs/2; chip++) {
+		snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
+		if (state)
+			continue;
+		/* DAC volumes */
+		for (reg = 0x04; reg < maxreg; reg++)
+			snd_akm4xxx_write(ak, chip, reg,
+					  snd_akm4xxx_get(ak, chip, reg));
+		if (ak->type == SND_AK4528)
+			continue;
+		/* IPGA */
+		for (reg = 0x04; reg < 0x06; reg++)
+			snd_akm4xxx_write(ak, chip, reg,
+					  snd_akm4xxx_get_ipga(ak, chip, reg));
+	}
+}
+
+/* reset procedure for AK4355 and AK4358 */
+static void ak4355_reset(struct snd_akm4xxx *ak, int state)
+{
+	unsigned char reg;
+
+	if (state) {
+		snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
+		return;
+	}
+	for (reg = 0x00; reg < 0x0b; reg++)
+		if (reg != 0x01)
+			snd_akm4xxx_write(ak, 0, reg,
+					  snd_akm4xxx_get(ak, 0, reg));
+	snd_akm4xxx_write(ak, 0, 0x01, 0x01); /* un-reset, unmute */
+}
+
+/* reset procedure for AK4381 */
+static void ak4381_reset(struct snd_akm4xxx *ak, int state)
+{
+	unsigned int chip;
+	unsigned char reg;
+
+	for (chip = 0; chip < ak->num_dacs/2; chip++) {
+		snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
+		if (state)
+			continue;
+		for (reg = 0x01; reg < 0x05; reg++)
+			snd_akm4xxx_write(ak, chip, reg,
+					  snd_akm4xxx_get(ak, chip, reg));
+	}
+}
+
 /*
  * reset the AKM codecs
  * @state: 1 = reset codec, 0 = restore the registers
@@ -60,52 +122,26 @@ void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg, unsi
  */
 void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state)
 {
-	unsigned int chip;
-	unsigned char reg;
-	
 	switch (ak->type) {
 	case SND_AK4524:
 	case SND_AK4528:
-		for (chip = 0; chip < ak->num_dacs/2; chip++) {
-			snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
-			if (state)
-				continue;
-			/* DAC volumes */
-			for (reg = 0x04; reg < (ak->type == SND_AK4528 ? 0x06 : 0x08); reg++)
-				snd_akm4xxx_write(ak, chip, reg, snd_akm4xxx_get(ak, chip, reg));
-			if (ak->type == SND_AK4528)
-				continue;
-			/* IPGA */
-			for (reg = 0x04; reg < 0x06; reg++)
-				snd_akm4xxx_write(ak, chip, reg, snd_akm4xxx_get_ipga(ak, chip, reg));
-		}
+		ak4524_reset(ak, state);
 		break;
 	case SND_AK4529:
 		/* FIXME: needed for ak4529? */
 		break;
 	case SND_AK4355:
 	case SND_AK4358:
-		if (state) {
-			snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
-			return;
-		}
-		for (reg = 0x00; reg < 0x0b; reg++)
-			if (reg != 0x01)
-				snd_akm4xxx_write(ak, 0, reg, snd_akm4xxx_get(ak, 0, reg));
-		snd_akm4xxx_write(ak, 0, 0x01, 0x01); /* un-reset, unmute */
+		ak4355_reset(ak, state);
 		break;
 	case SND_AK4381:
-		for (chip = 0; chip < ak->num_dacs/2; chip++) {
-			snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
-			if (state)
-				continue;
-			for (reg = 0x01; reg < 0x05; reg++)
-				snd_akm4xxx_write(ak, chip, reg, snd_akm4xxx_get(ak, chip, reg));
-		}
+		ak4381_reset(ak, state);
 		break;
 	}
 }
 
+EXPORT_SYMBOL(snd_akm4xxx_reset);
+
 /*
  * initialize all the ak4xxx chips
  */
@@ -153,7 +189,8 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
 	};
 	static unsigned char inits_ak4355[] = {
 		0x01, 0x02, /* 1: reset and soft-mute */
-		0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, disable DZF, sharp roll-off, RSTN#=0 */
+		0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect,
+			     * disable DZF, sharp roll-off, RSTN#=0 */
 		0x02, 0x0e, /* 2: DA's power up, normal speed, RSTN#=0 */
 		// 0x02, 0x2e, /* quad speed */
 		0x03, 0x01, /* 3: de-emphasis off */
@@ -169,7 +206,8 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
 	};
 	static unsigned char inits_ak4358[] = {
 		0x01, 0x02, /* 1: reset and soft-mute */
-		0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, disable DZF, sharp roll-off, RSTN#=0 */
+		0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect,
+			     * disable DZF, sharp roll-off, RSTN#=0 */
 		0x02, 0x0e, /* 2: DA's power up, normal speed, RSTN#=0 */
 		// 0x02, 0x2e, /* quad speed */
 		0x03, 0x01, /* 3: de-emphasis off */
@@ -187,7 +225,8 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
 	};
 	static unsigned char inits_ak4381[] = {
 		0x00, 0x0c, /* 0: mode3(i2s), disable auto-clock detect */
-		0x01, 0x02, /* 1: de-emphasis off, normal speed, sharp roll-off, DZF off */
+		0x01, 0x02, /* 1: de-emphasis off, normal speed,
+			     * sharp roll-off, DZF off */
 		// 0x01, 0x12, /* quad speed */
 		0x02, 0x00, /* 2: DZF disabled */
 		0x03, 0x00, /* 3: LATT 0 */
@@ -239,12 +278,15 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
 	}
 }
 
+EXPORT_SYMBOL(snd_akm4xxx_init);
+
 #define AK_GET_CHIP(val)		(((val) >> 8) & 0xff)
 #define AK_GET_ADDR(val)		((val) & 0xff)
 #define AK_GET_SHIFT(val)		(((val) >> 16) & 0x7f)
 #define AK_GET_INVERT(val)		(((val) >> 23) & 1)
 #define AK_GET_MASK(val)		(((val) >> 24) & 0xff)
-#define AK_COMPOSE(chip,addr,shift,mask) (((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24))
+#define AK_COMPOSE(chip,addr,shift,mask) \
+	(((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24))
 #define AK_INVERT 			(1<<23)
 
 static int snd_akm4xxx_volume_info(struct snd_kcontrol *kcontrol,
@@ -292,6 +334,64 @@ static int snd_akm4xxx_volume_put(struct snd_kcontrol *kcontrol,
 	return change;
 }
 
+static int snd_akm4xxx_stereo_volume_info(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_info *uinfo)
+{
+	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_akm4xxx_stereo_volume_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+	int chip = AK_GET_CHIP(kcontrol->private_value);
+	int addr = AK_GET_ADDR(kcontrol->private_value);
+	int invert = AK_GET_INVERT(kcontrol->private_value);
+	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+	unsigned char val = snd_akm4xxx_get(ak, chip, addr);
+	
+	ucontrol->value.integer.value[0] = invert ? mask - val : val;
+
+	val = snd_akm4xxx_get(ak, chip, addr+1);
+	ucontrol->value.integer.value[1] = invert ? mask - val : val;
+
+	return 0;
+}
+
+static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+	int chip = AK_GET_CHIP(kcontrol->private_value);
+	int addr = AK_GET_ADDR(kcontrol->private_value);
+	int invert = AK_GET_INVERT(kcontrol->private_value);
+	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+	unsigned char nval = ucontrol->value.integer.value[0] % (mask+1);
+	int change0, change1;
+
+	if (invert)
+		nval = mask - nval;
+	change0 = snd_akm4xxx_get(ak, chip, addr) != nval;
+	if (change0)
+		snd_akm4xxx_write(ak, chip, addr, nval);
+
+	nval = ucontrol->value.integer.value[1] % (mask+1);
+	if (invert)
+		nval = mask - nval;
+	change1 = snd_akm4xxx_get(ak, chip, addr+1) != nval;
+	if (change1)
+		snd_akm4xxx_write(ak, chip, addr+1, nval);
+
+
+	return change0 || change1;
+}
+
 static int snd_akm4xxx_ipga_gain_info(struct snd_kcontrol *kcontrol,
 				      struct snd_ctl_elem_info *uinfo)
 {
@@ -308,7 +408,8 @@ static int snd_akm4xxx_ipga_gain_get(struct snd_kcontrol *kcontrol,
 	struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
 	int chip = AK_GET_CHIP(kcontrol->private_value);
 	int addr = AK_GET_ADDR(kcontrol->private_value);
-	ucontrol->value.integer.value[0] = snd_akm4xxx_get_ipga(ak, chip, addr) & 0x7f;
+	ucontrol->value.integer.value[0] =
+		snd_akm4xxx_get_ipga(ak, chip, addr) & 0x7f;
 	return 0;
 }
 
@@ -336,7 +437,8 @@ static int snd_akm4xxx_deemphasis_info(struct snd_kcontrol *kcontrol,
 	uinfo->value.enumerated.items = 4;
 	if (uinfo->value.enumerated.item >= 4)
 		uinfo->value.enumerated.item = 3;
-	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	strcpy(uinfo->value.enumerated.name,
+	       texts[uinfo->value.enumerated.item]);
 	return 0;
 }
 
@@ -347,7 +449,8 @@ static int snd_akm4xxx_deemphasis_get(struct snd_kcontrol *kcontrol,
 	int chip = AK_GET_CHIP(kcontrol->private_value);
 	int addr = AK_GET_ADDR(kcontrol->private_value);
 	int shift = AK_GET_SHIFT(kcontrol->private_value);
-	ucontrol->value.enumerated.item[0] = (snd_akm4xxx_get(ak, chip, addr) >> shift) & 3;
+	ucontrol->value.enumerated.item[0] =
+		(snd_akm4xxx_get(ak, chip, addr) >> shift) & 3;
 	return 0;
 }
 
@@ -361,7 +464,8 @@ static int snd_akm4xxx_deemphasis_put(struct snd_kcontrol *kcontrol,
 	unsigned char nval = ucontrol->value.enumerated.item[0] & 3;
 	int change;
 	
-	nval = (nval << shift) | (snd_akm4xxx_get(ak, chip, addr) & ~(3 << shift));
+	nval = (nval << shift) |
+		(snd_akm4xxx_get(ak, chip, addr) & ~(3 << shift));
 	change = snd_akm4xxx_get(ak, chip, addr) != nval;
 	if (change)
 		snd_akm4xxx_write(ak, chip, addr, nval);
@@ -377,51 +481,86 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
 	unsigned int idx, num_emphs;
 	struct snd_kcontrol *ctl;
 	int err;
+	int mixer_ch = 0;
+	int num_stereo;
 
 	ctl = kmalloc(sizeof(*ctl), GFP_KERNEL);
 	if (! ctl)
 		return -ENOMEM;
 
-	for (idx = 0; idx < ak->num_dacs; ++idx) {
+	for (idx = 0; idx < ak->num_dacs; ) {
 		memset(ctl, 0, sizeof(*ctl));
-		strcpy(ctl->id.name, "DAC Volume");
-		ctl->id.index = idx + ak->idx_offset * 2;
+		if (ak->channel_names == NULL) {
+			strcpy(ctl->id.name, "DAC Volume");
+			num_stereo = 1;
+			ctl->id.index = mixer_ch + ak->idx_offset * 2;
+		} else {
+			strcpy(ctl->id.name, ak->channel_names[mixer_ch]);
+			num_stereo = ak->num_stereo[mixer_ch];
+			ctl->id.index = 0;
+		}
 		ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 		ctl->count = 1;
-		ctl->info = snd_akm4xxx_volume_info;
-		ctl->get = snd_akm4xxx_volume_get;
-		ctl->put = snd_akm4xxx_volume_put;
+		if (num_stereo == 2) {
+			ctl->info = snd_akm4xxx_stereo_volume_info;
+			ctl->get = snd_akm4xxx_stereo_volume_get;
+			ctl->put = snd_akm4xxx_stereo_volume_put;
+		} else {
+			ctl->info = snd_akm4xxx_volume_info;
+			ctl->get = snd_akm4xxx_volume_get;
+			ctl->put = snd_akm4xxx_volume_put;
+		}
 		switch (ak->type) {
 		case SND_AK4524:
-			ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127); /* register 6 & 7 */
+			/* register 6 & 7 */
+			ctl->private_value =
+				AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127);
 			break;
 		case SND_AK4528:
-			ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); /* register 4 & 5 */
+			/* register 4 & 5 */
+			ctl->private_value =
+				AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127);
 			break;
 		case SND_AK4529: {
-			int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb; /* registers 2-7 and b,c */
-			ctl->private_value = AK_COMPOSE(0, val, 0, 255) | AK_INVERT;
+			/* registers 2-7 and b,c */
+			int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb;
+			ctl->private_value =
+				AK_COMPOSE(0, val, 0, 255) | AK_INVERT;
 			break;
 		}
 		case SND_AK4355:
-			ctl->private_value = AK_COMPOSE(0, idx + 4, 0, 255); /* register 4-9, chip #0 only */
+			/* register 4-9, chip #0 only */
+			ctl->private_value = AK_COMPOSE(0, idx + 4, 0, 255);
 			break;
 		case SND_AK4358:
 			if (idx >= 6)
-				ctl->private_value = AK_COMPOSE(0, idx + 5, 0, 255); /* register 4-9, chip #0 only */
+				/* register 4-9, chip #0 only */
+				ctl->private_value =
+					AK_COMPOSE(0, idx + 5, 0, 255);
 			else
-				ctl->private_value = AK_COMPOSE(0, idx + 4, 0, 255); /* register 4-9, chip #0 only */
+				/* register 4-9, chip #0 only */
+				ctl->private_value =
+					AK_COMPOSE(0, idx + 4, 0, 255);
 			break;
 		case SND_AK4381:
-			ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255); /* register 3 & 4 */
+			/* register 3 & 4 */
+			ctl->private_value =
+				AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255);
 			break;
 		default:
 			err = -EINVAL;
 			goto __error;
 		}
+
 		ctl->private_data = ak;
-		if ((err = snd_ctl_add(ak->card, snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
+		err = snd_ctl_add(ak->card,
+				  snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
+					      SNDRV_CTL_ELEM_ACCESS_WRITE));
+		if (err < 0)
 			goto __error;
+
+		idx += num_stereo;
+		mixer_ch++;
 	}
 	for (idx = 0; idx < ak->num_adcs && ak->type == SND_AK4524; ++idx) {
 		memset(ctl, 0, sizeof(*ctl));
@@ -432,9 +571,14 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
 		ctl->info = snd_akm4xxx_volume_info;
 		ctl->get = snd_akm4xxx_volume_get;
 		ctl->put = snd_akm4xxx_volume_put;
-		ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); /* register 4 & 5 */
+		/* register 4 & 5 */
+		ctl->private_value =
+			AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127);
 		ctl->private_data = ak;
-		if ((err = snd_ctl_add(ak->card, snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
+		err = snd_ctl_add(ak->card,
+				  snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
+					      SNDRV_CTL_ELEM_ACCESS_WRITE));
+		if (err < 0)
 			goto __error;
 
 		memset(ctl, 0, sizeof(*ctl));
@@ -445,9 +589,13 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
 		ctl->info = snd_akm4xxx_ipga_gain_info;
 		ctl->get = snd_akm4xxx_ipga_gain_get;
 		ctl->put = snd_akm4xxx_ipga_gain_put;
-		ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 0); /* register 4 & 5 */
+		/* register 4 & 5 */
+		ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 0);
 		ctl->private_data = ak;
-		if ((err = snd_ctl_add(ak->card, snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
+		err = snd_ctl_add(ak->card,
+				  snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
+					      SNDRV_CTL_ELEM_ACCESS_WRITE));
+		if (err < 0)
 			goto __error;
 	}
 	if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
@@ -466,11 +614,13 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
 		switch (ak->type) {
 		case SND_AK4524:
 		case SND_AK4528:
-			ctl->private_value = AK_COMPOSE(idx, 3, 0, 0); /* register 3 */
+			/* register 3 */
+			ctl->private_value = AK_COMPOSE(idx, 3, 0, 0);
 			break;
 		case SND_AK4529: {
 			int shift = idx == 3 ? 6 : (2 - idx) * 2;
-			ctl->private_value = AK_COMPOSE(0, 8, shift, 0); /* register 8 with shift */
+			/* register 8 with shift */
+			ctl->private_value = AK_COMPOSE(0, 8, shift, 0);
 			break;
 		}
 		case SND_AK4355:
@@ -482,7 +632,10 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
 			break;
 		}
 		ctl->private_data = ak;
-		if ((err = snd_ctl_add(ak->card, snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
+		err = snd_ctl_add(ak->card,
+				  snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
+					      SNDRV_CTL_ELEM_ACCESS_WRITE));
+		if (err < 0)
 			goto __error;
 	}
 	err = 0;
@@ -492,6 +645,8 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
 	return err;
 }
 
+EXPORT_SYMBOL(snd_akm4xxx_build_controls);
+
 static int __init alsa_akm4xxx_module_init(void)
 {
 	return 0;
@@ -503,8 +658,3 @@ static void __exit alsa_akm4xxx_module_exit(void)
         
 module_init(alsa_akm4xxx_module_init)
 module_exit(alsa_akm4xxx_module_exit)
-
-EXPORT_SYMBOL(snd_akm4xxx_write);
-EXPORT_SYMBOL(snd_akm4xxx_reset);
-EXPORT_SYMBOL(snd_akm4xxx_init);
-EXPORT_SYMBOL(snd_akm4xxx_build_controls);
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index d37346b12dc0..23e54cedfd4a 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -233,6 +233,143 @@ config SND_CS5535AUDIO
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-cs5535audio.
 
+config SND_DARLA20
+	tristate "(Echoaudio) Darla20"
+	depends on SND
+	depends on FW_LOADER
+	select SND_PCM
+	help
+	  Say 'Y' or 'M' to include support for Echoaudio Darla.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-darla20
+
+config SND_GINA20
+	tristate "(Echoaudio) Gina20"
+	depends on SND
+	depends on FW_LOADER
+	select SND_PCM
+	help
+	  Say 'Y' or 'M' to include support for Echoaudio Gina.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-gina20
+
+config SND_LAYLA20
+	tristate "(Echoaudio) Layla20"
+	depends on SND
+	depends on FW_LOADER
+	select SND_RAWMIDI
+	select SND_PCM
+	help
+	  Say 'Y' or 'M' to include support for Echoaudio Layla.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-layla20
+
+config SND_DARLA24
+	tristate "(Echoaudio) Darla24"
+	depends on SND
+	depends on FW_LOADER
+	select SND_PCM
+	help
+	  Say 'Y' or 'M' to include support for Echoaudio Darla24.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-darla24
+
+config SND_GINA24
+	tristate "(Echoaudio) Gina24"
+	depends on SND
+	depends on FW_LOADER
+	select SND_PCM
+	help
+	  Say 'Y' or 'M' to include support for Echoaudio Gina24.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-gina24
+
+config SND_LAYLA24
+	tristate "(Echoaudio) Layla24"
+	depends on SND
+	depends on FW_LOADER
+	select SND_RAWMIDI
+	select SND_PCM
+	help
+	  Say 'Y' or 'M' to include support for Echoaudio Layla24.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-layla24
+
+config SND_MONA
+	tristate "(Echoaudio) Mona"
+	depends on SND
+	depends on FW_LOADER
+	select SND_RAWMIDI
+	select SND_PCM
+	help
+	  Say 'Y' or 'M' to include support for Echoaudio Mona.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-mona
+
+config SND_MIA
+	tristate "(Echoaudio) Mia"
+	depends on SND
+	depends on FW_LOADER
+	select SND_RAWMIDI
+	select SND_PCM
+	help
+	  Say 'Y' or 'M' to include support for Echoaudio Mia and Mia-midi.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-mia
+
+config SND_ECHO3G
+	tristate "(Echoaudio) 3G cards"
+	depends on SND
+	depends on FW_LOADER
+	select SND_RAWMIDI
+	select SND_PCM
+	help
+	  Say 'Y' or 'M' to include support for Echoaudio Gina3G and Layla3G.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-echo3g
+
+config SND_INDIGO
+	tristate "(Echoaudio) Indigo"
+	depends on SND
+	depends on FW_LOADER
+	select SND_PCM
+	help
+	  Say 'Y' or 'M' to include support for Echoaudio Indigo.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-indigo
+
+config SND_INDIGOIO
+	tristate "(Echoaudio) Indigo IO"
+	depends on SND
+	depends on FW_LOADER
+	select SND_PCM
+	help
+	  Say 'Y' or 'M' to include support for Echoaudio Indigo IO.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-indigoio
+
+config SND_INDIGODJ
+	tristate "(Echoaudio) Indigo DJ"
+	depends on SND
+	depends on FW_LOADER
+	select SND_PCM
+	help
+	  Say 'Y' or 'M' to include support for Echoaudio Indigo DJ.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-indigodj
+
 config SND_EMU10K1
 	tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)"
 	depends on SND
@@ -420,8 +557,8 @@ config SND_INTEL8X0
 	  will be called snd-intel8x0.
 
 config SND_INTEL8X0M
-	tristate "Intel/SiS/nVidia/AMD MC97 Modem (EXPERIMENTAL)"
-	depends on SND && EXPERIMENTAL
+	tristate "Intel/SiS/nVidia/AMD MC97 Modem"
+	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the integrated MC97 modem on
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index cba5105aafea..e06736da9ef1 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_SND) += \
 	ca0106/ \
 	cs46xx/ \
 	cs5535audio/ \
+	echoaudio/ \
 	emu10k1/ \
 	hda/ \
 	ice1712/ \
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 7f197c780816..094cfc1f3a19 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -1824,6 +1824,8 @@ static const struct snd_kcontrol_new snd_ac97_ad1888_controls[] = {
 		.get = snd_ac97_ad1888_lohpsel_get,
 		.put = snd_ac97_ad1888_lohpsel_put
 	},
+	AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, 2, 1, 1),
+	AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1),
 	AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0),
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
diff --git a/sound/pci/echoaudio/Makefile b/sound/pci/echoaudio/Makefile
new file mode 100644
index 000000000000..7b576aeb3f8d
--- /dev/null
+++ b/sound/pci/echoaudio/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for ALSA Echoaudio soundcard drivers
+# Copyright (c) 2003 by Giuliano Pochini <pochini@shiny.it>
+#
+
+snd-darla20-objs := darla20.o
+snd-gina20-objs := gina20.o
+snd-layla20-objs := layla20.o
+snd-darla24-objs := darla24.o
+snd-gina24-objs := gina24.o
+snd-layla24-objs := layla24.o
+snd-mona-objs := mona.o
+snd-mia-objs := mia.o
+snd-echo3g-objs := echo3g.o
+snd-indigo-objs := indigo.o
+snd-indigoio-objs := indigoio.o
+snd-indigodj-objs := indigodj.o
+
+obj-$(CONFIG_SND_DARLA20) += snd-darla20.o
+obj-$(CONFIG_SND_GINA20) += snd-gina20.o
+obj-$(CONFIG_SND_LAYLA20) += snd-layla20.o
+obj-$(CONFIG_SND_DARLA24) += snd-darla24.o
+obj-$(CONFIG_SND_GINA24) += snd-gina24.o
+obj-$(CONFIG_SND_LAYLA24) += snd-layla24.o
+obj-$(CONFIG_SND_MONA) += snd-mona.o
+obj-$(CONFIG_SND_MIA) += snd-mia.o
+obj-$(CONFIG_SND_ECHO3G) += snd-echo3g.o
+obj-$(CONFIG_SND_INDIGO) += snd-indigo.o
+obj-$(CONFIG_SND_INDIGOIO) += snd-indigoio.o
+obj-$(CONFIG_SND_INDIGODJ) += snd-indigodj.o
diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c
new file mode 100644
index 000000000000..b7108e29a668
--- /dev/null
+++ b/sound/pci/echoaudio/darla20.c
@@ -0,0 +1,99 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHOGALS_FAMILY
+#define ECHOCARD_DARLA20
+#define ECHOCARD_NAME "Darla20"
+#define ECHOCARD_HAS_MONITOR
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT	0	/* 8 */
+#define PX_DIGITAL_OUT	8	/* 0 */
+#define PX_ANALOG_IN	8	/* 2 */
+#define PX_DIGITAL_IN	10	/* 0 */
+#define PX_NUM		10
+
+/* Bus indexes */
+#define BX_ANALOG_OUT	0	/* 8 */
+#define BX_DIGITAL_OUT	8	/* 0 */
+#define BX_ANALOG_IN	8	/* 2 */
+#define BX_DIGITAL_IN	10	/* 0 */
+#define BX_NUM		10
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_DARLA20_DSP	0
+
+static const struct firmware card_fw[] = {
+	{0, "darla20_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+	{0x1057, 0x1801, 0xECC0, 0x0010, 0, 0, 0},	/* DSP 56301 Darla20 rev.0 */
+	{0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_SYNC_START,
+	.formats =	SNDRV_PCM_FMTBIT_U8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_3LE |
+			SNDRV_PCM_FMTBIT_S32_LE |
+			SNDRV_PCM_FMTBIT_S32_BE,
+	.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+	.rate_min = 44100,
+	.rate_max = 48000,
+	.channels_min = 1,
+	.channels_max = 2,
+	.buffer_bytes_max = 262144,
+	.period_bytes_min = 32,
+	.period_bytes_max = 131072,
+	.periods_min = 2,
+	.periods_max = 220,
+	/* One page (4k) contains 512 instructions. I don't know if the hw
+	supports lists longer than this. In this case periods_max=220 is a
+	safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "darla20_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c
new file mode 100644
index 000000000000..4159e3bc186f
--- /dev/null
+++ b/sound/pci/echoaudio/darla20_dsp.c
@@ -0,0 +1,125 @@
+/***************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+	int err;
+
+	DE_INIT(("init_hw() - Darla20\n"));
+	snd_assert((subdevice_id & 0xfff0) == DARLA20, return -ENODEV);
+
+	if ((err = init_dsp_comm_page(chip))) {
+		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+		return err;
+	}
+
+	chip->device_id = device_id;
+	chip->subdevice_id = subdevice_id;
+	chip->bad_board = TRUE;
+	chip->dsp_code_to_load = &card_fw[FW_DARLA20_DSP];
+	chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
+	chip->clock_state = GD_CLOCK_UNDEF;
+	/* Since this card has no ASIC, mark it as loaded so everything
+	   works OK */
+	chip->asic_loaded = TRUE;
+	chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
+
+	if ((err = load_firmware(chip)) < 0)
+		return err;
+	chip->bad_board = FALSE;
+
+	if ((err = init_line_levels(chip)) < 0)
+		return err;
+
+	DE_INIT(("init_hw done\n"));
+	return err;
+}
+
+
+
+/* The Darla20 has no external clock sources */
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+	return ECHO_CLOCK_BIT_INTERNAL;
+}
+
+
+
+/* The Darla20 has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+	return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+	u8 clock_state, spdif_status;
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	switch (rate) {
+	case 44100:
+		clock_state = GD_CLOCK_44;
+		spdif_status = GD_SPDIF_STATUS_44;
+		break;
+	case 48000:
+		clock_state = GD_CLOCK_48;
+		spdif_status = GD_SPDIF_STATUS_48;
+		break;
+	default:
+		clock_state = GD_CLOCK_NOCHANGE;
+		spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+		break;
+	}
+
+	if (chip->clock_state == clock_state)
+		clock_state = GD_CLOCK_NOCHANGE;
+	if (spdif_status == chip->spdif_status)
+		spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+
+	chip->comm_page->sample_rate = cpu_to_le32(rate);
+	chip->comm_page->gd_clock_state = clock_state;
+	chip->comm_page->gd_spdif_status = spdif_status;
+	chip->comm_page->gd_resampler_state = 3;	/* magic number - should always be 3 */
+
+	/* Save the new audio state if it changed */
+	if (clock_state != GD_CLOCK_NOCHANGE)
+		chip->clock_state = clock_state;
+	if (spdif_status != GD_SPDIF_STATUS_NOCHANGE)
+		chip->spdif_status = spdif_status;
+	chip->sample_rate = rate;
+
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE);
+}
diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c
new file mode 100644
index 000000000000..e59a982ee361
--- /dev/null
+++ b/sound/pci/echoaudio/darla24.c
@@ -0,0 +1,106 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHOGALS_FAMILY
+#define ECHOCARD_DARLA24
+#define ECHOCARD_NAME "Darla24"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT	0	/* 8 */
+#define PX_DIGITAL_OUT	8	/* 0 */
+#define PX_ANALOG_IN	8	/* 2 */
+#define PX_DIGITAL_IN	10	/* 0 */
+#define PX_NUM		10
+
+/* Bus indexes */
+#define BX_ANALOG_OUT	0	/* 8 */
+#define BX_DIGITAL_OUT	8	/* 0 */
+#define BX_ANALOG_IN	8	/* 2 */
+#define BX_DIGITAL_IN	10	/* 0 */
+#define BX_NUM		10
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_DARLA24_DSP	0
+
+static const struct firmware card_fw[] = {
+	{0, "darla24_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+	{0x1057, 0x1801, 0xECC0, 0x0040, 0, 0, 0},	/* DSP 56301 Darla24 rev.0 */
+	{0x1057, 0x1801, 0xECC0, 0x0041, 0, 0, 0},	/* DSP 56301 Darla24 rev.1 */
+	{0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_SYNC_START,
+	.formats =	SNDRV_PCM_FMTBIT_U8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_3LE |
+			SNDRV_PCM_FMTBIT_S32_LE |
+			SNDRV_PCM_FMTBIT_S32_BE,
+	.rates =	SNDRV_PCM_RATE_8000_48000 |
+			SNDRV_PCM_RATE_88200 |
+			SNDRV_PCM_RATE_96000,
+	.rate_min = 8000,
+	.rate_max = 96000,
+	.channels_min = 1,
+	.channels_max = 8,
+	.buffer_bytes_max = 262144,
+	.period_bytes_min = 32,
+	.period_bytes_max = 131072,
+	.periods_min = 2,
+	.periods_max = 220,
+	/* One page (4k) contains 512 instructions. I don't know if the hw
+	supports lists longer than this. In this case periods_max=220 is a
+	safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "darla24_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c
new file mode 100644
index 000000000000..79938eed7e9c
--- /dev/null
+++ b/sound/pci/echoaudio/darla24_dsp.c
@@ -0,0 +1,156 @@
+/***************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+	int err;
+
+	DE_INIT(("init_hw() - Darla24\n"));
+	snd_assert((subdevice_id & 0xfff0) == DARLA24, return -ENODEV);
+
+	if ((err = init_dsp_comm_page(chip))) {
+		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+		return err;
+	}
+
+	chip->device_id = device_id;
+	chip->subdevice_id = subdevice_id;
+	chip->bad_board = TRUE;
+	chip->dsp_code_to_load = &card_fw[FW_DARLA24_DSP];
+	/* Since this card has no ASIC, mark it as loaded so everything
+	   works OK */
+	chip->asic_loaded = TRUE;
+	chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
+		ECHO_CLOCK_BIT_ESYNC;
+
+	if ((err = load_firmware(chip)) < 0)
+		return err;
+	chip->bad_board = FALSE;
+
+	if ((err = init_line_levels(chip)) < 0)
+		return err;
+
+	DE_INIT(("init_hw done\n"));
+	return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+	u32 clocks_from_dsp, clock_bits;
+
+	/* Map the DSP clock detect bits to the generic driver clock
+	   detect bits */
+	clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+	clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+	if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_ESYNC)
+		clock_bits |= ECHO_CLOCK_BIT_ESYNC;
+
+	return clock_bits;
+}
+
+
+
+/* The Darla24 has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+	return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+	u8 clock;
+
+	switch (rate) {
+	case 96000:
+		clock = GD24_96000;
+		break;
+	case 88200:
+		clock = GD24_88200;
+		break;
+	case 48000:
+		clock = GD24_48000;
+		break;
+	case 44100:
+		clock = GD24_44100;
+		break;
+	case 32000:
+		clock = GD24_32000;
+		break;
+	case 22050:
+		clock = GD24_22050;
+		break;
+	case 16000:
+		clock = GD24_16000;
+		break;
+	case 11025:
+		clock = GD24_11025;
+		break;
+	case 8000:
+		clock = GD24_8000;
+		break;
+	default:
+		DE_ACT(("set_sample_rate: Error, invalid sample rate %d\n",
+			rate));
+		return -EINVAL;
+	}
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+	chip->sample_rate = rate;
+
+	/* Override the sample rate if this card is set to Echo sync. */
+	if (chip->input_clock == ECHO_CLOCK_ESYNC)
+		clock = GD24_EXT_SYNC;
+
+	chip->comm_page->sample_rate = cpu_to_le32(rate);	/* ignored by the DSP ? */
+	chip->comm_page->gd_clock_state = clock;
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+	snd_assert(clock == ECHO_CLOCK_INTERNAL ||
+		   clock == ECHO_CLOCK_ESYNC, return -EINVAL);
+	chip->input_clock = clock;
+	return set_sample_rate(chip, chip->sample_rate);
+}
+
diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c
new file mode 100644
index 000000000000..12099fe1547d
--- /dev/null
+++ b/sound/pci/echoaudio/echo3g.c
@@ -0,0 +1,118 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHO3G_FAMILY
+#define ECHOCARD_ECHO3G
+#define ECHOCARD_NAME "Echo3G"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+#define ECHOCARD_HAS_ADAT	6
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+#define ECHOCARD_HAS_MIDI
+#define ECHOCARD_HAS_PHANTOM_POWER
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT	0
+#define PX_DIGITAL_OUT	chip->px_digital_out
+#define PX_ANALOG_IN	chip->px_analog_in
+#define PX_DIGITAL_IN	chip->px_digital_in
+#define PX_NUM		chip->px_num
+
+/* Bus indexes */
+#define BX_ANALOG_OUT	0
+#define BX_DIGITAL_OUT	chip->bx_digital_out
+#define BX_ANALOG_IN	chip->bx_analog_in
+#define BX_DIGITAL_IN	chip->bx_digital_in
+#define BX_NUM		chip->bx_num
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER	0
+#define FW_ECHO3G_DSP	1
+#define FW_3G_ASIC	2
+
+static const struct firmware card_fw[] = {
+	{0, "loader_dsp.fw"},
+	{0, "echo3g_dsp.fw"},
+	{0, "3g_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+	{0x1057, 0x3410, 0xECC0, 0x0100, 0, 0, 0},	/* Echo 3G */
+	{0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_SYNC_START,
+	.formats =	SNDRV_PCM_FMTBIT_U8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_3LE |
+			SNDRV_PCM_FMTBIT_S32_LE |
+			SNDRV_PCM_FMTBIT_S32_BE,
+	.rates = 	SNDRV_PCM_RATE_32000 |
+			SNDRV_PCM_RATE_44100 |
+			SNDRV_PCM_RATE_48000 |
+			SNDRV_PCM_RATE_88200 |
+			SNDRV_PCM_RATE_96000 |
+			SNDRV_PCM_RATE_CONTINUOUS,
+	.rate_min = 32000,
+	.rate_max = 100000,
+	.channels_min = 1,
+	.channels_max = 8,
+	.buffer_bytes_max = 262144,
+	.period_bytes_min = 32,
+	.period_bytes_max = 131072,
+	.periods_min = 2,
+	.periods_max = 220,
+};
+
+#include "echo3g_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio_3g.c"
+#include "echoaudio.c"
+#include "midi.c"
diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c
new file mode 100644
index 000000000000..d26a1d1f3ed1
--- /dev/null
+++ b/sound/pci/echoaudio/echo3g_dsp.c
@@ -0,0 +1,131 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+static int load_asic(struct echoaudio *chip);
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode);
+static int set_digital_mode(struct echoaudio *chip, u8 mode);
+static int check_asic_status(struct echoaudio *chip);
+static int set_sample_rate(struct echoaudio *chip, u32 rate);
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int set_phantom_power(struct echoaudio *chip, char on);
+static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq,
+			     char force);
+
+#include <linux/irq.h>
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+	int err;
+
+	local_irq_enable();
+	DE_INIT(("init_hw() - Echo3G\n"));
+	snd_assert((subdevice_id & 0xfff0) == ECHO3G, return -ENODEV);
+
+	if ((err = init_dsp_comm_page(chip))) {
+		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+		return err;
+	}
+
+	chip->comm_page->e3g_frq_register =
+		__constant_cpu_to_le32((E3G_MAGIC_NUMBER / 48000) - 2);
+	chip->device_id = device_id;
+	chip->subdevice_id = subdevice_id;
+	chip->bad_board = TRUE;
+	chip->has_midi = TRUE;
+	chip->dsp_code_to_load = &card_fw[FW_ECHO3G_DSP];
+
+	/* Load the DSP code and the ASIC on the PCI card and get
+	what type of external box is attached */
+	err = load_firmware(chip);
+
+	if (err < 0) {
+		return err;
+	} else if (err == E3G_GINA3G_BOX_TYPE) {
+		chip->input_clock_types =	ECHO_CLOCK_BIT_INTERNAL |
+						ECHO_CLOCK_BIT_SPDIF |
+						ECHO_CLOCK_BIT_ADAT;
+		chip->card_name = "Gina3G";
+		chip->px_digital_out = chip->bx_digital_out = 6;
+		chip->px_analog_in = chip->bx_analog_in = 14;
+		chip->px_digital_in = chip->bx_digital_in = 16;
+		chip->px_num = chip->bx_num = 24;
+		chip->has_phantom_power = TRUE;
+		chip->hasnt_input_nominal_level = TRUE;
+	} else if (err == E3G_LAYLA3G_BOX_TYPE) {
+		chip->input_clock_types =	ECHO_CLOCK_BIT_INTERNAL |
+						ECHO_CLOCK_BIT_SPDIF |
+						ECHO_CLOCK_BIT_ADAT |
+						ECHO_CLOCK_BIT_WORD;
+		chip->card_name = "Layla3G";
+		chip->px_digital_out = chip->bx_digital_out = 8;
+		chip->px_analog_in = chip->bx_analog_in = 16;
+		chip->px_digital_in = chip->bx_digital_in = 24;
+		chip->px_num = chip->bx_num = 32;
+	} else {
+		return -ENODEV;
+	}
+
+	chip->digital_modes =	ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+				ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+				ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
+	chip->digital_mode =	DIGITAL_MODE_SPDIF_RCA;
+	chip->professional_spdif = FALSE;
+	chip->non_audio_spdif = FALSE;
+	chip->bad_board = FALSE;
+
+	if ((err = init_line_levels(chip)) < 0)
+		return err;
+	err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
+	snd_assert(err >= 0, return err);
+	err = set_phantom_power(chip, 0);
+	snd_assert(err >= 0, return err);
+	err = set_professional_spdif(chip, TRUE);
+
+	DE_INIT(("init_hw done\n"));
+	return err;
+}
+
+
+
+static int set_phantom_power(struct echoaudio *chip, char on)
+{
+	u32 control_reg = le32_to_cpu(chip->comm_page->control_register);
+
+	if (on)
+		control_reg |= E3G_PHANTOM_POWER;
+	else
+		control_reg &= ~E3G_PHANTOM_POWER;
+
+	chip->phantom_power = on;
+	return write_control_reg(chip, control_reg,
+				 le32_to_cpu(chip->comm_page->e3g_frq_register),
+				 0);
+}
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
new file mode 100644
index 000000000000..43b408ada1da
--- /dev/null
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -0,0 +1,2196 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+MODULE_AUTHOR("Giuliano Pochini <pochini@shiny.it>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Echoaudio " ECHOCARD_NAME " soundcards driver");
+MODULE_SUPPORTED_DEVICE("{{Echoaudio," ECHOCARD_NAME "}}");
+MODULE_DEVICE_TABLE(pci, snd_echo_ids);
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " ECHOCARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " ECHOCARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard.");
+
+static unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999};
+
+static int get_firmware(const struct firmware **fw_entry,
+			const struct firmware *frm, struct echoaudio *chip)
+{
+	int err;
+	char name[30];
+	DE_ACT(("firmware requested: %s\n", frm->data));
+	snprintf(name, sizeof(name), "ea/%s", frm->data);
+	if ((err = request_firmware(fw_entry, name, pci_device(chip))) < 0)
+		snd_printk(KERN_ERR "get_firmware(): Firmware not available (%d)\n", err);
+	return err;
+}
+
+static void free_firmware(const struct firmware *fw_entry)
+{
+	release_firmware(fw_entry);
+	DE_ACT(("firmware released\n"));
+}
+
+
+
+/******************************************************************************
+	PCM interface
+******************************************************************************/
+
+static void audiopipe_free(struct snd_pcm_runtime *runtime)
+{
+	struct audiopipe *pipe = runtime->private_data;
+
+	if (pipe->sgpage.area)
+		snd_dma_free_pages(&pipe->sgpage);
+	kfree(pipe);
+}
+
+
+
+static int hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params,
+					      struct snd_pcm_hw_rule *rule)
+{
+	struct snd_interval *c = hw_param_interval(params,
+						   SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+	struct snd_mask fmt;
+
+	snd_mask_any(&fmt);
+
+#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+	/* >=2 channels cannot be S32_BE */
+	if (c->min == 2) {
+		fmt.bits[0] &= ~SNDRV_PCM_FMTBIT_S32_BE;
+		return snd_mask_refine(f, &fmt);
+	}
+#endif
+	/* > 2 channels cannot be U8 and S32_BE */
+	if (c->min > 2) {
+		fmt.bits[0] &= ~(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_BE);
+		return snd_mask_refine(f, &fmt);
+	}
+	/* Mono is ok with any format */
+	return 0;
+}
+
+
+
+static int hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params,
+					      struct snd_pcm_hw_rule *rule)
+{
+	struct snd_interval *c = hw_param_interval(params,
+						   SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+	struct snd_interval ch;
+
+	snd_interval_any(&ch);
+
+	/* S32_BE is mono (and stereo) only */
+	if (f->bits[0] == SNDRV_PCM_FMTBIT_S32_BE) {
+		ch.min = 1;
+#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+		ch.max = 2;
+#else
+		ch.max = 1;
+#endif
+		ch.integer = 1;
+		return snd_interval_refine(c, &ch);
+	}
+	/* U8 can be only mono or stereo */
+	if (f->bits[0] == SNDRV_PCM_FMTBIT_U8) {
+		ch.min = 1;
+		ch.max = 2;
+		ch.integer = 1;
+		return snd_interval_refine(c, &ch);
+	}
+	/* S16_LE, S24_3LE and S32_LE support any number of channels. */
+	return 0;
+}
+
+
+
+static int hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params,
+					       struct snd_pcm_hw_rule *rule)
+{
+	struct snd_interval *c = hw_param_interval(params,
+						   SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+	struct snd_mask fmt;
+	u64 fmask;
+	snd_mask_any(&fmt);
+
+	fmask = fmt.bits[0] + ((u64)fmt.bits[1] << 32);
+
+	/* >2 channels must be S16_LE, S24_3LE or S32_LE */
+	if (c->min > 2) {
+		fmask &= SNDRV_PCM_FMTBIT_S16_LE |
+			 SNDRV_PCM_FMTBIT_S24_3LE |
+			 SNDRV_PCM_FMTBIT_S32_LE;
+	/* 1 channel must be S32_BE or S32_LE */
+	} else if (c->max == 1)
+		fmask &= SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE;
+#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+	/* 2 channels cannot be S32_BE */
+	else if (c->min == 2 && c->max == 2)
+		fmask &= ~SNDRV_PCM_FMTBIT_S32_BE;
+#endif
+	else
+		return 0;
+
+	fmt.bits[0] &= (u32)fmask;
+	fmt.bits[1] &= (u32)(fmask >> 32);
+	return snd_mask_refine(f, &fmt);
+}
+
+
+
+static int hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,
+					       struct snd_pcm_hw_rule *rule)
+{
+	struct snd_interval *c = hw_param_interval(params,
+						   SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+	struct snd_interval ch;
+	u64 fmask;
+
+	snd_interval_any(&ch);
+	ch.integer = 1;
+	fmask = f->bits[0] + ((u64)f->bits[1] << 32);
+
+	/* S32_BE is mono (and stereo) only */
+	if (fmask == SNDRV_PCM_FMTBIT_S32_BE) {
+		ch.min = 1;
+#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+		ch.max = 2;
+#else
+		ch.max = 1;
+#endif
+	/* U8 is stereo only */
+	} else if (fmask == SNDRV_PCM_FMTBIT_U8)
+		ch.min = ch.max = 2;
+	/* S16_LE and S24_3LE must be at least stereo */
+	else if (!(fmask & ~(SNDRV_PCM_FMTBIT_S16_LE |
+			       SNDRV_PCM_FMTBIT_S24_3LE)))
+		ch.min = 2;
+	else
+		return 0;
+
+	return snd_interval_refine(c, &ch);
+}
+
+
+
+/* Since the sample rate is a global setting, do allow the user to change the
+sample rate only if there is only one pcm device open. */
+static int hw_rule_sample_rate(struct snd_pcm_hw_params *params,
+			       struct snd_pcm_hw_rule *rule)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+						      SNDRV_PCM_HW_PARAM_RATE);
+	struct echoaudio *chip = rule->private;
+	struct snd_interval fixed;
+
+	if (!chip->can_set_rate) {
+		snd_interval_any(&fixed);
+		fixed.min = fixed.max = chip->sample_rate;
+		return snd_interval_refine(rate, &fixed);
+	}
+	return 0;
+}
+
+
+static int pcm_open(struct snd_pcm_substream *substream,
+		    signed char max_channels)
+{
+	struct echoaudio *chip;
+	struct snd_pcm_runtime *runtime;
+	struct audiopipe *pipe;
+	int err, i;
+
+	if (max_channels <= 0)
+		return -EAGAIN;
+
+	chip = snd_pcm_substream_chip(substream);
+	runtime = substream->runtime;
+
+	if (!(pipe = kmalloc(sizeof(struct audiopipe), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(pipe, 0, sizeof(struct audiopipe));
+	pipe->index = -1;		/* Not configured yet */
+
+	/* Set up hw capabilities and contraints */
+	memcpy(&pipe->hw, &pcm_hardware_skel, sizeof(struct snd_pcm_hardware));
+	DE_HWP(("max_channels=%d\n", max_channels));
+	pipe->constr.list = channels_list;
+	pipe->constr.mask = 0;
+	for (i = 0; channels_list[i] <= max_channels; i++);
+	pipe->constr.count = i;
+	if (pipe->hw.channels_max > max_channels)
+		pipe->hw.channels_max = max_channels;
+	if (chip->digital_mode == DIGITAL_MODE_ADAT) {
+		pipe->hw.rate_max = 48000;
+		pipe->hw.rates &= SNDRV_PCM_RATE_8000_48000;
+	}
+
+	runtime->hw = pipe->hw;
+	runtime->private_data = pipe;
+	runtime->private_free = audiopipe_free;
+	snd_pcm_set_sync(substream);
+
+	/* Only mono and any even number of channels are allowed */
+	if ((err = snd_pcm_hw_constraint_list(runtime, 0,
+					      SNDRV_PCM_HW_PARAM_CHANNELS,
+					      &pipe->constr)) < 0)
+		return err;
+
+	/* All periods should have the same size */
+	if ((err = snd_pcm_hw_constraint_integer(runtime,
+						 SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+
+	/* The hw accesses memory in chunks 32 frames long and they should be
+	32-bytes-aligned. It's not a requirement, but it seems that IRQs are
+	generated with a resolution of 32 frames. Thus we need the following */
+	if ((err = snd_pcm_hw_constraint_step(runtime, 0,
+					      SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+					      32)) < 0)
+		return err;
+	if ((err = snd_pcm_hw_constraint_step(runtime, 0,
+					      SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+					      32)) < 0)
+		return err;
+
+	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+				       SNDRV_PCM_HW_PARAM_RATE,
+					hw_rule_sample_rate, chip,
+				       SNDRV_PCM_HW_PARAM_RATE, -1)) < 0)
+		return err;
+
+	/* Finally allocate a page for the scatter-gather list */
+	if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+				       snd_dma_pci_data(chip->pci),
+				       PAGE_SIZE, &pipe->sgpage)) < 0) {
+		DE_HWP(("s-g list allocation failed\n"));
+		return err;
+	}
+
+	return 0;
+}
+
+
+
+static int pcm_analog_in_open(struct snd_pcm_substream *substream)
+{
+	struct echoaudio *chip = snd_pcm_substream_chip(substream);
+	int err;
+
+	DE_ACT(("pcm_analog_in_open\n"));
+	if ((err = pcm_open(substream, num_analog_busses_in(chip) -
+			    substream->number)) < 0)
+		return err;
+	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+				       SNDRV_PCM_HW_PARAM_CHANNELS,
+				       hw_rule_capture_channels_by_format, NULL,
+				       SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
+		return err;
+	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+				       SNDRV_PCM_HW_PARAM_FORMAT,
+				       hw_rule_capture_format_by_channels, NULL,
+				       SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
+		return err;
+	atomic_inc(&chip->opencount);
+	if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
+		chip->can_set_rate=0;
+	DE_HWP(("pcm_analog_in_open  cs=%d  oc=%d  r=%d\n",
+		chip->can_set_rate, atomic_read(&chip->opencount),
+		chip->sample_rate));
+	return 0;
+}
+
+
+
+static int pcm_analog_out_open(struct snd_pcm_substream *substream)
+{
+	struct echoaudio *chip = snd_pcm_substream_chip(substream);
+	int max_channels, err;
+
+#ifdef ECHOCARD_HAS_VMIXER
+	max_channels = num_pipes_out(chip);
+#else
+	max_channels = num_analog_busses_out(chip);
+#endif
+	DE_ACT(("pcm_analog_out_open\n"));
+	if ((err = pcm_open(substream, max_channels - substream->number)) < 0)
+		return err;
+	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+				       SNDRV_PCM_HW_PARAM_CHANNELS,
+				       hw_rule_playback_channels_by_format,
+				       NULL,
+				       SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
+		return err;
+	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+				       SNDRV_PCM_HW_PARAM_FORMAT,
+				       hw_rule_playback_format_by_channels,
+				       NULL,
+				       SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
+		return err;
+	atomic_inc(&chip->opencount);
+	if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
+		chip->can_set_rate=0;
+	DE_HWP(("pcm_analog_out_open  cs=%d  oc=%d  r=%d\n",
+		chip->can_set_rate, atomic_read(&chip->opencount),
+		chip->sample_rate));
+	return 0;
+}
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+
+static int pcm_digital_in_open(struct snd_pcm_substream *substream)
+{
+	struct echoaudio *chip = snd_pcm_substream_chip(substream);
+	int err, max_channels;
+
+	DE_ACT(("pcm_digital_in_open\n"));
+	max_channels = num_digital_busses_in(chip) - substream->number;
+	down(&chip->mode_mutex);
+	if (chip->digital_mode == DIGITAL_MODE_ADAT)
+		err = pcm_open(substream, max_channels);
+	else	/* If the card has ADAT, subtract the 6 channels
+		 * that S/PDIF doesn't have
+		 */
+		err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
+
+	if (err < 0)
+		goto din_exit;
+
+	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+				       SNDRV_PCM_HW_PARAM_CHANNELS,
+				       hw_rule_capture_channels_by_format, NULL,
+				       SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
+		goto din_exit;
+	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+				       SNDRV_PCM_HW_PARAM_FORMAT,
+				       hw_rule_capture_format_by_channels, NULL,
+				       SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
+		goto din_exit;
+
+	atomic_inc(&chip->opencount);
+	if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
+		chip->can_set_rate=0;
+
+din_exit:
+	up(&chip->mode_mutex);
+	return err;
+}
+
+
+
+#ifndef ECHOCARD_HAS_VMIXER	/* See the note in snd_echo_new_pcm() */
+
+static int pcm_digital_out_open(struct snd_pcm_substream *substream)
+{
+	struct echoaudio *chip = snd_pcm_substream_chip(substream);
+	int err, max_channels;
+
+	DE_ACT(("pcm_digital_out_open\n"));
+	max_channels = num_digital_busses_out(chip) - substream->number;
+	down(&chip->mode_mutex);
+	if (chip->digital_mode == DIGITAL_MODE_ADAT)
+		err = pcm_open(substream, max_channels);
+	else	/* If the card has ADAT, subtract the 6 channels
+		 * that S/PDIF doesn't have
+		 */
+		err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
+
+	if (err < 0)
+		goto dout_exit;
+
+	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+				       SNDRV_PCM_HW_PARAM_CHANNELS,
+				       hw_rule_playback_channels_by_format,
+				       NULL, SNDRV_PCM_HW_PARAM_FORMAT,
+				       -1)) < 0)
+		goto dout_exit;
+	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+				       SNDRV_PCM_HW_PARAM_FORMAT,
+				       hw_rule_playback_format_by_channels,
+				       NULL, SNDRV_PCM_HW_PARAM_CHANNELS,
+				       -1)) < 0)
+		goto dout_exit;
+	atomic_inc(&chip->opencount);
+	if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
+		chip->can_set_rate=0;
+dout_exit:
+	up(&chip->mode_mutex);
+	return err;
+}
+
+#endif /* !ECHOCARD_HAS_VMIXER */
+
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+	struct echoaudio *chip = snd_pcm_substream_chip(substream);
+	int oc;
+
+	/* Nothing to do here. Audio is already off and pipe will be
+	 * freed by its callback
+	 */
+	DE_ACT(("pcm_close\n"));
+
+	atomic_dec(&chip->opencount);
+	oc = atomic_read(&chip->opencount);
+	DE_ACT(("pcm_close  oc=%d  cs=%d  rs=%d\n", oc,
+		chip->can_set_rate, chip->rate_set));
+	if (oc < 2)
+		chip->can_set_rate = 1;
+	if (oc == 0)
+		chip->rate_set = 0;
+	DE_ACT(("pcm_close2 oc=%d  cs=%d  rs=%d\n", oc,
+		chip->can_set_rate,chip->rate_set));
+
+	return 0;
+}
+
+
+
+/* Channel allocation and scatter-gather list setup */
+static int init_engine(struct snd_pcm_substream *substream,
+		       struct snd_pcm_hw_params *hw_params,
+		       int pipe_index, int interleave)
+{
+	struct echoaudio *chip;
+	int err, per, rest, page, edge, offs;
+	struct snd_sg_buf *sgbuf;
+	struct audiopipe *pipe;
+
+	chip = snd_pcm_substream_chip(substream);
+	pipe = (struct audiopipe *) substream->runtime->private_data;
+
+	/* Sets up che hardware. If it's already initialized, reset and
+	 * redo with the new parameters
+	 */
+	spin_lock_irq(&chip->lock);
+	if (pipe->index >= 0) {
+		DE_HWP(("hwp_ie free(%d)\n", pipe->index));
+		err = free_pipes(chip, pipe);
+		snd_assert(!err);
+		chip->substream[pipe->index] = NULL;
+	}
+
+	err = allocate_pipes(chip, pipe, pipe_index, interleave);
+	if (err < 0) {
+		spin_unlock_irq(&chip->lock);
+		DE_ACT((KERN_NOTICE "allocate_pipes(%d) err=%d\n",
+			pipe_index, err));
+		return err;
+	}
+	spin_unlock_irq(&chip->lock);
+	DE_ACT((KERN_NOTICE "allocate_pipes()=%d\n", pipe_index));
+
+	DE_HWP(("pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n",
+		params_buffer_bytes(hw_params), params_periods(hw_params),
+		params_period_bytes(hw_params)));
+	err = snd_pcm_lib_malloc_pages(substream,
+				       params_buffer_bytes(hw_params));
+	if (err < 0) {
+		snd_printk(KERN_ERR "malloc_pages err=%d\n", err);
+		spin_lock_irq(&chip->lock);
+		free_pipes(chip, pipe);
+		spin_unlock_irq(&chip->lock);
+		pipe->index = -1;
+		return err;
+	}
+
+	sgbuf = snd_pcm_substream_sgbuf(substream);
+
+	DE_HWP(("pcm_hw_params table size=%d pages=%d\n",
+		sgbuf->size, sgbuf->pages));
+	sglist_init(chip, pipe);
+	edge = PAGE_SIZE;
+	for (offs = page = per = 0; offs < params_buffer_bytes(hw_params);
+	     per++) {
+		rest = params_period_bytes(hw_params);
+		if (offs + rest > params_buffer_bytes(hw_params))
+			rest = params_buffer_bytes(hw_params) - offs;
+		while (rest) {
+			if (rest <= edge - offs) {
+				sglist_add_mapping(chip, pipe,
+						   snd_sgbuf_get_addr(sgbuf, offs),
+						   rest);
+				sglist_add_irq(chip, pipe);
+				offs += rest;
+				rest = 0;
+			} else {
+				sglist_add_mapping(chip, pipe,
+						   snd_sgbuf_get_addr(sgbuf, offs),
+						   edge - offs);
+				rest -= edge - offs;
+				offs = edge;
+			}
+			if (offs == edge) {
+				edge += PAGE_SIZE;
+				page++;
+			}
+		}
+	}
+
+	/* Close the ring buffer */
+	sglist_wrap(chip, pipe);
+
+	/* This stuff is used by the irq handler, so it must be
+	 * initialized before chip->substream
+	 */
+	chip->last_period[pipe_index] = 0;
+	pipe->last_counter = 0;
+	pipe->position = 0;
+	smp_wmb();
+	chip->substream[pipe_index] = substream;
+	chip->rate_set = 1;
+	spin_lock_irq(&chip->lock);
+	set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den);
+	spin_unlock_irq(&chip->lock);
+	DE_HWP(("pcm_hw_params ok\n"));
+	return 0;
+}
+
+
+
+static int pcm_analog_in_hw_params(struct snd_pcm_substream *substream,
+				   struct snd_pcm_hw_params *hw_params)
+{
+	struct echoaudio *chip = snd_pcm_substream_chip(substream);
+
+	return init_engine(substream, hw_params, px_analog_in(chip) +
+			substream->number, params_channels(hw_params));
+}
+
+
+
+static int pcm_analog_out_hw_params(struct snd_pcm_substream *substream,
+				    struct snd_pcm_hw_params *hw_params)
+{
+	return init_engine(substream, hw_params, substream->number,
+			   params_channels(hw_params));
+}
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+
+static int pcm_digital_in_hw_params(struct snd_pcm_substream *substream,
+				    struct snd_pcm_hw_params *hw_params)
+{
+	struct echoaudio *chip = snd_pcm_substream_chip(substream);
+
+	return init_engine(substream, hw_params, px_digital_in(chip) +
+			substream->number, params_channels(hw_params));
+}
+
+
+
+#ifndef ECHOCARD_HAS_VMIXER	/* See the note in snd_echo_new_pcm() */
+static int pcm_digital_out_hw_params(struct snd_pcm_substream *substream,
+				     struct snd_pcm_hw_params *hw_params)
+{
+	struct echoaudio *chip = snd_pcm_substream_chip(substream);
+
+	return init_engine(substream, hw_params, px_digital_out(chip) +
+			substream->number, params_channels(hw_params));
+}
+#endif /* !ECHOCARD_HAS_VMIXER */
+
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct echoaudio *chip;
+	struct audiopipe *pipe;
+
+	chip = snd_pcm_substream_chip(substream);
+	pipe = (struct audiopipe *) substream->runtime->private_data;
+
+	spin_lock_irq(&chip->lock);
+	if (pipe->index >= 0) {
+		DE_HWP(("pcm_hw_free(%d)\n", pipe->index));
+		free_pipes(chip, pipe);
+		chip->substream[pipe->index] = NULL;
+		pipe->index = -1;
+	}
+	spin_unlock_irq(&chip->lock);
+
+	DE_HWP(("pcm_hw_freed\n"));
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+
+
+static int pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct echoaudio *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct audioformat format;
+	int pipe_index = ((struct audiopipe *)runtime->private_data)->index;
+
+	DE_HWP(("Prepare rate=%d format=%d channels=%d\n",
+		runtime->rate, runtime->format, runtime->channels));
+	format.interleave = runtime->channels;
+	format.data_are_bigendian = 0;
+	format.mono_to_stereo = 0;
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_U8:
+		format.bits_per_sample = 8;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		format.bits_per_sample = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		format.bits_per_sample = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_BE:
+		format.data_are_bigendian = 1;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		format.bits_per_sample = 32;
+		break;
+	default:
+		DE_HWP(("Prepare error: unsupported format %d\n",
+			runtime->format));
+		return -EINVAL;
+	}
+
+	snd_assert(pipe_index < px_num(chip), return -EINVAL);
+	snd_assert(is_pipe_allocated(chip, pipe_index), return -EINVAL);
+	set_audio_format(chip, pipe_index, &format);
+	return 0;
+}
+
+
+
+static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct echoaudio *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct audiopipe *pipe = runtime->private_data;
+	int i, err;
+	u32 channelmask = 0;
+	struct list_head *pos;
+	struct snd_pcm_substream *s;
+
+	snd_pcm_group_for_each(pos, substream) {
+		s = snd_pcm_group_substream_entry(pos);
+		for (i = 0; i < DSP_MAXPIPES; i++) {
+			if (s == chip->substream[i]) {
+				channelmask |= 1 << i;
+				snd_pcm_trigger_done(s, substream);
+			}
+		}
+	}
+
+	spin_lock(&chip->lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		DE_ACT(("pcm_trigger start\n"));
+		for (i = 0; i < DSP_MAXPIPES; i++) {
+			if (channelmask & (1 << i)) {
+				pipe = chip->substream[i]->runtime->private_data;
+				switch (pipe->state) {
+				case PIPE_STATE_STOPPED:
+					chip->last_period[i] = 0;
+					pipe->last_counter = 0;
+					pipe->position = 0;
+					*pipe->dma_counter = 0;
+				case PIPE_STATE_PAUSED:
+					pipe->state = PIPE_STATE_STARTED;
+					break;
+				case PIPE_STATE_STARTED:
+					break;
+				}
+			}
+		}
+		err = start_transport(chip, channelmask,
+				      chip->pipe_cyclic_mask);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		DE_ACT(("pcm_trigger stop\n"));
+		for (i = 0; i < DSP_MAXPIPES; i++) {
+			if (channelmask & (1 << i)) {
+				pipe = chip->substream[i]->runtime->private_data;
+				pipe->state = PIPE_STATE_STOPPED;
+			}
+		}
+		err = stop_transport(chip, channelmask);
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		DE_ACT(("pcm_trigger pause\n"));
+		for (i = 0; i < DSP_MAXPIPES; i++) {
+			if (channelmask & (1 << i)) {
+				pipe = chip->substream[i]->runtime->private_data;
+				pipe->state = PIPE_STATE_PAUSED;
+			}
+		}
+		err = pause_transport(chip, channelmask);
+		break;
+	default:
+		err = -EINVAL;
+	}
+	spin_unlock(&chip->lock);
+	return err;
+}
+
+
+
+static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct audiopipe *pipe = runtime->private_data;
+	size_t cnt, bufsize, pos;
+
+	cnt = le32_to_cpu(*pipe->dma_counter);
+	pipe->position += cnt - pipe->last_counter;
+	pipe->last_counter = cnt;
+	bufsize = substream->runtime->buffer_size;
+	pos = bytes_to_frames(substream->runtime, pipe->position);
+
+	while (pos >= bufsize) {
+		pipe->position -= frames_to_bytes(substream->runtime, bufsize);
+		pos -= bufsize;
+	}
+	return pos;
+}
+
+
+
+/* pcm *_ops structures */
+static struct snd_pcm_ops analog_playback_ops = {
+	.open = pcm_analog_out_open,
+	.close = pcm_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = pcm_analog_out_hw_params,
+	.hw_free = pcm_hw_free,
+	.prepare = pcm_prepare,
+	.trigger = pcm_trigger,
+	.pointer = pcm_pointer,
+	.page = snd_pcm_sgbuf_ops_page,
+};
+static struct snd_pcm_ops analog_capture_ops = {
+	.open = pcm_analog_in_open,
+	.close = pcm_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = pcm_analog_in_hw_params,
+	.hw_free = pcm_hw_free,
+	.prepare = pcm_prepare,
+	.trigger = pcm_trigger,
+	.pointer = pcm_pointer,
+	.page = snd_pcm_sgbuf_ops_page,
+};
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+#ifndef ECHOCARD_HAS_VMIXER
+static struct snd_pcm_ops digital_playback_ops = {
+	.open = pcm_digital_out_open,
+	.close = pcm_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = pcm_digital_out_hw_params,
+	.hw_free = pcm_hw_free,
+	.prepare = pcm_prepare,
+	.trigger = pcm_trigger,
+	.pointer = pcm_pointer,
+	.page = snd_pcm_sgbuf_ops_page,
+};
+#endif /* !ECHOCARD_HAS_VMIXER */
+static struct snd_pcm_ops digital_capture_ops = {
+	.open = pcm_digital_in_open,
+	.close = pcm_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = pcm_digital_in_hw_params,
+	.hw_free = pcm_hw_free,
+	.prepare = pcm_prepare,
+	.trigger = pcm_trigger,
+	.pointer = pcm_pointer,
+	.page = snd_pcm_sgbuf_ops_page,
+};
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+
+
+/* Preallocate memory only for the first substream because it's the most
+ * used one
+ */
+static int snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev)
+{
+	struct snd_pcm_substream *ss;
+	int stream, err;
+
+	for (stream = 0; stream < 2; stream++)
+		for (ss = pcm->streams[stream].substream; ss; ss = ss->next) {
+			err = snd_pcm_lib_preallocate_pages(ss, SNDRV_DMA_TYPE_DEV_SG,
+							    dev,
+							    ss->number ? 0 : 128<<10,
+							    256<<10);
+			if (err < 0)
+				return err;
+		}
+	return 0;
+}
+
+
+
+/*<--snd_echo_probe() */
+static int __devinit snd_echo_new_pcm(struct echoaudio *chip)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+#ifdef ECHOCARD_HAS_VMIXER
+	/* This card has a Vmixer, that is there is no direct mapping from PCM
+	streams to physical outputs. The user can mix the streams as he wishes
+	via control interface and it's possible to send any stream to any
+	output, thus it makes no sense to keep analog and digital outputs
+	separated */
+
+	/* PCM#0 Virtual outputs and analog inputs */
+	if ((err = snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip),
+				num_analog_busses_in(chip), &pcm)) < 0)
+		return err;
+	pcm->private_data = chip;
+	chip->analog_pcm = pcm;
+	strcpy(pcm->name, chip->card->shortname);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
+	if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
+		return err;
+	DE_INIT(("Analog PCM ok\n"));
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+	/* PCM#1 Digital inputs, no outputs */
+	if ((err = snd_pcm_new(chip->card, "Digital PCM", 1, 0,
+			       num_digital_busses_in(chip), &pcm)) < 0)
+		return err;
+	pcm->private_data = chip;
+	chip->digital_pcm = pcm;
+	strcpy(pcm->name, chip->card->shortname);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
+	if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
+		return err;
+	DE_INIT(("Digital PCM ok\n"));
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+#else /* ECHOCARD_HAS_VMIXER */
+
+	/* The card can manage substreams formed by analog and digital channels
+	at the same time, but I prefer to keep analog and digital channels
+	separated, because that mixed thing is confusing and useless. So we
+	register two PCM devices: */
+
+	/* PCM#0 Analog i/o */
+	if ((err = snd_pcm_new(chip->card, "Analog PCM", 0,
+			       num_analog_busses_out(chip),
+			       num_analog_busses_in(chip), &pcm)) < 0)
+		return err;
+	pcm->private_data = chip;
+	chip->analog_pcm = pcm;
+	strcpy(pcm->name, chip->card->shortname);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
+	if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
+		return err;
+	DE_INIT(("Analog PCM ok\n"));
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+	/* PCM#1 Digital i/o */
+	if ((err = snd_pcm_new(chip->card, "Digital PCM", 1,
+			       num_digital_busses_out(chip),
+			       num_digital_busses_in(chip), &pcm)) < 0)
+		return err;
+	pcm->private_data = chip;
+	chip->digital_pcm = pcm;
+	strcpy(pcm->name, chip->card->shortname);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
+	if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
+		return err;
+	DE_INIT(("Digital PCM ok\n"));
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+#endif /* ECHOCARD_HAS_VMIXER */
+
+	return 0;
+}
+
+
+
+
+/******************************************************************************
+	Control interface
+******************************************************************************/
+
+/******************* PCM output volume *******************/
+static int snd_echo_output_gain_info(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_info *uinfo)
+{
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = num_busses_out(chip);
+	uinfo->value.integer.min = ECHOGAIN_MINOUT;
+	uinfo->value.integer.max = ECHOGAIN_MAXOUT;
+	return 0;
+}
+
+static int snd_echo_output_gain_get(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int c;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	for (c = 0; c < num_busses_out(chip); c++)
+		ucontrol->value.integer.value[c] = chip->output_gain[c];
+	return 0;
+}
+
+static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int c, changed, gain;
+
+	changed = 0;
+	chip = snd_kcontrol_chip(kcontrol);
+	spin_lock_irq(&chip->lock);
+	for (c = 0; c < num_busses_out(chip); c++) {
+		gain = ucontrol->value.integer.value[c];
+		/* Ignore out of range values */
+		if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
+			continue;
+		if (chip->output_gain[c] != gain) {
+			set_output_gain(chip, c, gain);
+			changed = 1;
+		}
+	}
+	if (changed)
+		update_output_line_level(chip);
+	spin_unlock_irq(&chip->lock);
+	return changed;
+}
+
+#ifdef ECHOCARD_HAS_VMIXER
+/* On Vmixer cards this one controls the line-out volume */
+static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = {
+	.name = "Line Playback Volume",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info = snd_echo_output_gain_info,
+	.get = snd_echo_output_gain_get,
+	.put = snd_echo_output_gain_put,
+};
+#else
+static struct snd_kcontrol_new snd_echo_pcm_output_gain __devinitdata = {
+	.name = "PCM Playback Volume",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info = snd_echo_output_gain_info,
+	.get = snd_echo_output_gain_get,
+	.put = snd_echo_output_gain_put,
+};
+#endif
+
+
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+
+/******************* Analog input volume *******************/
+static int snd_echo_input_gain_info(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_info *uinfo)
+{
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = num_analog_busses_in(chip);
+	uinfo->value.integer.min = ECHOGAIN_MININP;
+	uinfo->value.integer.max = ECHOGAIN_MAXINP;
+	return 0;
+}
+
+static int snd_echo_input_gain_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int c;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	for (c = 0; c < num_analog_busses_in(chip); c++)
+		ucontrol->value.integer.value[c] = chip->input_gain[c];
+	return 0;
+}
+
+static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int c, gain, changed;
+
+	changed = 0;
+	chip = snd_kcontrol_chip(kcontrol);
+	spin_lock_irq(&chip->lock);
+	for (c = 0; c < num_analog_busses_in(chip); c++) {
+		gain = ucontrol->value.integer.value[c];
+		/* Ignore out of range values */
+		if (gain < ECHOGAIN_MININP || gain > ECHOGAIN_MAXINP)
+			continue;
+		if (chip->input_gain[c] != gain) {
+			set_input_gain(chip, c, gain);
+			changed = 1;
+		}
+	}
+	if (changed)
+		update_input_line_level(chip);
+	spin_unlock_irq(&chip->lock);
+	return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_line_input_gain __devinitdata = {
+	.name = "Line Capture Volume",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info = snd_echo_input_gain_info,
+	.get = snd_echo_input_gain_get,
+	.put = snd_echo_input_gain_put,
+};
+
+#endif /* ECHOCARD_HAS_INPUT_GAIN */
+
+
+
+#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+
+/************ Analog output nominal level (+4dBu / -10dBV) ***************/
+static int snd_echo_output_nominal_info (struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_info *uinfo)
+{
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = num_analog_busses_out(chip);
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_echo_output_nominal_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int c;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	for (c = 0; c < num_analog_busses_out(chip); c++)
+		ucontrol->value.integer.value[c] = chip->nominal_level[c];
+	return 0;
+}
+
+static int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int c, changed;
+
+	changed = 0;
+	chip = snd_kcontrol_chip(kcontrol);
+	spin_lock_irq(&chip->lock);
+	for (c = 0; c < num_analog_busses_out(chip); c++) {
+		if (chip->nominal_level[c] != ucontrol->value.integer.value[c]) {
+			set_nominal_level(chip, c,
+					  ucontrol->value.integer.value[c]);
+			changed = 1;
+		}
+	}
+	if (changed)
+		update_output_line_level(chip);
+	spin_unlock_irq(&chip->lock);
+	return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_output_nominal_level __devinitdata = {
+	.name = "Line Playback Switch (-10dBV)",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info = snd_echo_output_nominal_info,
+	.get = snd_echo_output_nominal_get,
+	.put = snd_echo_output_nominal_put,
+};
+
+#endif /* ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL */
+
+
+
+#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+
+/*************** Analog input nominal level (+4dBu / -10dBV) ***************/
+static int snd_echo_input_nominal_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = num_analog_busses_in(chip);
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_echo_input_nominal_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int c;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	for (c = 0; c < num_analog_busses_in(chip); c++)
+		ucontrol->value.integer.value[c] =
+			chip->nominal_level[bx_analog_in(chip) + c];
+	return 0;
+}
+
+static int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int c, changed;
+
+	changed = 0;
+	chip = snd_kcontrol_chip(kcontrol);
+	spin_lock_irq(&chip->lock);
+	for (c = 0; c < num_analog_busses_in(chip); c++) {
+		if (chip->nominal_level[bx_analog_in(chip) + c] !=
+		    ucontrol->value.integer.value[c]) {
+			set_nominal_level(chip, bx_analog_in(chip) + c,
+					  ucontrol->value.integer.value[c]);
+			changed = 1;
+		}
+	}
+	if (changed)
+		update_output_line_level(chip);	/* "Output" is not a mistake
+						 * here.
+						 */
+	spin_unlock_irq(&chip->lock);
+	return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_intput_nominal_level __devinitdata = {
+	.name = "Line Capture Switch (-10dBV)",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info = snd_echo_input_nominal_info,
+	.get = snd_echo_input_nominal_get,
+	.put = snd_echo_input_nominal_put,
+};
+
+#endif /* ECHOCARD_HAS_INPUT_NOMINAL_LEVEL */
+
+
+
+#ifdef ECHOCARD_HAS_MONITOR
+
+/******************* Monitor mixer *******************/
+static int snd_echo_mixer_info(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *uinfo)
+{
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = ECHOGAIN_MINOUT;
+	uinfo->value.integer.max = ECHOGAIN_MAXOUT;
+	uinfo->dimen.d[0] = num_busses_out(chip);
+	uinfo->dimen.d[1] = num_busses_in(chip);
+	return 0;
+}
+
+static int snd_echo_mixer_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] =
+		chip->monitor_gain[ucontrol->id.index / num_busses_in(chip)]
+			[ucontrol->id.index % num_busses_in(chip)];
+	return 0;
+}
+
+static int snd_echo_mixer_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int changed,  gain;
+	short out, in;
+
+	changed = 0;
+	chip = snd_kcontrol_chip(kcontrol);
+	out = ucontrol->id.index / num_busses_in(chip);
+	in = ucontrol->id.index % num_busses_in(chip);
+	gain = ucontrol->value.integer.value[0];
+	if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
+		return -EINVAL;
+	if (chip->monitor_gain[out][in] != gain) {
+		spin_lock_irq(&chip->lock);
+		set_monitor_gain(chip, out, in, gain);
+		update_output_line_level(chip);
+		spin_unlock_irq(&chip->lock);
+		changed = 1;
+	}
+	return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_monitor_mixer __devinitdata = {
+	.name = "Monitor Mixer Volume",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info = snd_echo_mixer_info,
+	.get = snd_echo_mixer_get,
+	.put = snd_echo_mixer_put,
+};
+
+#endif /* ECHOCARD_HAS_MONITOR */
+
+
+
+#ifdef ECHOCARD_HAS_VMIXER
+
+/******************* Vmixer *******************/
+static int snd_echo_vmixer_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = ECHOGAIN_MINOUT;
+	uinfo->value.integer.max = ECHOGAIN_MAXOUT;
+	uinfo->dimen.d[0] = num_busses_out(chip);
+	uinfo->dimen.d[1] = num_pipes_out(chip);
+	return 0;
+}
+
+static int snd_echo_vmixer_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] =
+		chip->vmixer_gain[ucontrol->id.index / num_pipes_out(chip)]
+			[ucontrol->id.index % num_pipes_out(chip)];
+	return 0;
+}
+
+static int snd_echo_vmixer_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int gain, changed;
+	short vch, out;
+
+	changed = 0;
+	chip = snd_kcontrol_chip(kcontrol);
+	out = ucontrol->id.index / num_pipes_out(chip);
+	vch = ucontrol->id.index % num_pipes_out(chip);
+	gain = ucontrol->value.integer.value[0];
+	if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
+		return -EINVAL;
+	if (chip->vmixer_gain[out][vch] != ucontrol->value.integer.value[0]) {
+		spin_lock_irq(&chip->lock);
+		set_vmixer_gain(chip, out, vch, ucontrol->value.integer.value[0]);
+		update_vmixer_level(chip);
+		spin_unlock_irq(&chip->lock);
+		changed = 1;
+	}
+	return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_vmixer __devinitdata = {
+	.name = "VMixer Volume",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info = snd_echo_vmixer_info,
+	.get = snd_echo_vmixer_get,
+	.put = snd_echo_vmixer_put,
+};
+
+#endif /* ECHOCARD_HAS_VMIXER */
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+
+/******************* Digital mode switch *******************/
+static int snd_echo_digital_mode_info(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_info *uinfo)
+{
+	static char *names[4] = {
+		"S/PDIF Coaxial", "S/PDIF Optical", "ADAT Optical",
+		"S/PDIF Cdrom"
+	};
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->value.enumerated.items = chip->num_digital_modes;
+	uinfo->count = 1;
+	if (uinfo->value.enumerated.item >= chip->num_digital_modes)
+		uinfo->value.enumerated.item = chip->num_digital_modes - 1;
+	strcpy(uinfo->value.enumerated.name, names[
+			chip->digital_mode_list[uinfo->value.enumerated.item]]);
+	return 0;
+}
+
+static int snd_echo_digital_mode_get(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int i, mode;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	mode = chip->digital_mode;
+	for (i = chip->num_digital_modes - 1; i >= 0; i--)
+		if (mode == chip->digital_mode_list[i]) {
+			ucontrol->value.enumerated.item[0] = i;
+			break;
+		}
+	return 0;
+}
+
+static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int changed;
+	unsigned short emode, dmode;
+
+	changed = 0;
+	chip = snd_kcontrol_chip(kcontrol);
+
+	emode = ucontrol->value.enumerated.item[0];
+	if (emode >= chip->num_digital_modes)
+		return -EINVAL;
+	dmode = chip->digital_mode_list[emode];
+
+	if (dmode != chip->digital_mode) {
+		/* mode_mutex is required to make this operation atomic wrt
+		pcm_digital_*_open() and set_input_clock() functions. */
+		down(&chip->mode_mutex);
+
+		/* Do not allow the user to change the digital mode when a pcm
+		device is open because it also changes the number of channels
+		and the allowed sample rates */
+		if (atomic_read(&chip->opencount)) {
+			changed = -EAGAIN;
+		} else {
+			changed = set_digital_mode(chip, dmode);
+			/* If we had to change the clock source, report it */
+			if (changed > 0 && chip->clock_src_ctl) {
+				snd_ctl_notify(chip->card,
+					       SNDRV_CTL_EVENT_MASK_VALUE,
+					       &chip->clock_src_ctl->id);
+				DE_ACT(("SDM() =%d\n", changed));
+			}
+			if (changed >= 0)
+				changed = 1;	/* No errors */
+		}
+		up(&chip->mode_mutex);
+	}
+	return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_digital_mode_switch __devinitdata = {
+	.name = "Digital mode Switch",
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.info = snd_echo_digital_mode_info,
+	.get = snd_echo_digital_mode_get,
+	.put = snd_echo_digital_mode_put,
+};
+
+#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+
+/******************* S/PDIF mode switch *******************/
+static int snd_echo_spdif_mode_info(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_info *uinfo)
+{
+	static char *names[2] = {"Consumer", "Professional"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->value.enumerated.items = 2;
+	uinfo->count = 1;
+	if (uinfo->value.enumerated.item)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name,
+	       names[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_echo_spdif_mode_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.enumerated.item[0] = !!chip->professional_spdif;
+	return 0;
+}
+
+static int snd_echo_spdif_mode_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int mode;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	mode = !!ucontrol->value.enumerated.item[0];
+	if (mode != chip->professional_spdif) {
+		spin_lock_irq(&chip->lock);
+		set_professional_spdif(chip, mode);
+		spin_unlock_irq(&chip->lock);
+		return 1;
+	}
+	return 0;
+}
+
+static struct snd_kcontrol_new snd_echo_spdif_mode_switch __devinitdata = {
+	.name = "S/PDIF mode Switch",
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.info = snd_echo_spdif_mode_info,
+	.get = snd_echo_spdif_mode_get,
+	.put = snd_echo_spdif_mode_put,
+};
+
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+
+
+#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
+
+/******************* Select input clock source *******************/
+static int snd_echo_clock_source_info(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_info *uinfo)
+{
+	static char *names[8] = {
+		"Internal", "Word", "Super", "S/PDIF", "ADAT", "ESync",
+		"ESync96", "MTC"
+	};
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->value.enumerated.items = chip->num_clock_sources;
+	uinfo->count = 1;
+	if (uinfo->value.enumerated.item >= chip->num_clock_sources)
+		uinfo->value.enumerated.item = chip->num_clock_sources - 1;
+	strcpy(uinfo->value.enumerated.name, names[
+			chip->clock_source_list[uinfo->value.enumerated.item]]);
+	return 0;
+}
+
+static int snd_echo_clock_source_get(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int i, clock;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	clock = chip->input_clock;
+
+	for (i = 0; i < chip->num_clock_sources; i++)
+		if (clock == chip->clock_source_list[i])
+			ucontrol->value.enumerated.item[0] = i;
+
+	return 0;
+}
+
+static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int changed;
+	unsigned int eclock, dclock;
+
+	changed = 0;
+	chip = snd_kcontrol_chip(kcontrol);
+	eclock = ucontrol->value.enumerated.item[0];
+	if (eclock >= chip->input_clock_types)
+		return -EINVAL;
+	dclock = chip->clock_source_list[eclock];
+	if (chip->input_clock != dclock) {
+		down(&chip->mode_mutex);
+		spin_lock_irq(&chip->lock);
+		if ((changed = set_input_clock(chip, dclock)) == 0)
+			changed = 1;	/* no errors */
+		spin_unlock_irq(&chip->lock);
+		up(&chip->mode_mutex);
+	}
+
+	if (changed < 0)
+		DE_ACT(("seticlk val%d err 0x%x\n", dclock, changed));
+
+	return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_clock_source_switch __devinitdata = {
+	.name = "Sample Clock Source",
+	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+	.info = snd_echo_clock_source_info,
+	.get = snd_echo_clock_source_get,
+	.put = snd_echo_clock_source_put,
+};
+
+#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */
+
+
+
+#ifdef ECHOCARD_HAS_PHANTOM_POWER
+
+/******************* Phantom power switch *******************/
+static int snd_echo_phantom_power_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *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_echo_phantom_power_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = chip->phantom_power;
+	return 0;
+}
+
+static int snd_echo_phantom_power_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
+	int power, changed = 0;
+
+	power = !!ucontrol->value.integer.value[0];
+	if (chip->phantom_power != power) {
+		spin_lock_irq(&chip->lock);
+		changed = set_phantom_power(chip, power);
+		spin_unlock_irq(&chip->lock);
+		if (changed == 0)
+			changed = 1;	/* no errors */
+	}
+	return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_phantom_power_switch __devinitdata = {
+	.name = "Phantom power Switch",
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.info = snd_echo_phantom_power_info,
+	.get = snd_echo_phantom_power_get,
+	.put = snd_echo_phantom_power_put,
+};
+
+#endif /* ECHOCARD_HAS_PHANTOM_POWER */
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+
+/******************* Digital input automute switch *******************/
+static int snd_echo_automute_info(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_info *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_echo_automute_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = chip->digital_in_automute;
+	return 0;
+}
+
+static int snd_echo_automute_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
+	int automute, changed = 0;
+
+	automute = !!ucontrol->value.integer.value[0];
+	if (chip->digital_in_automute != automute) {
+		spin_lock_irq(&chip->lock);
+		changed = set_input_auto_mute(chip, automute);
+		spin_unlock_irq(&chip->lock);
+		if (changed == 0)
+			changed = 1;	/* no errors */
+	}
+	return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_automute_switch __devinitdata = {
+	.name = "Digital Capture Switch (automute)",
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.info = snd_echo_automute_info,
+	.get = snd_echo_automute_get,
+	.put = snd_echo_automute_put,
+};
+
+#endif /* ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE */
+
+
+
+/******************* VU-meters switch *******************/
+static int snd_echo_vumeters_switch_info(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_info *uinfo)
+{
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	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_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	spin_lock_irq(&chip->lock);
+	set_meters_on(chip, ucontrol->value.integer.value[0]);
+	spin_unlock_irq(&chip->lock);
+	return 1;
+}
+
+static struct snd_kcontrol_new snd_echo_vumeters_switch __devinitdata = {
+	.name = "VU-meters Switch",
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.access = SNDRV_CTL_ELEM_ACCESS_WRITE,
+	.info = snd_echo_vumeters_switch_info,
+	.put = snd_echo_vumeters_switch_put,
+};
+
+
+
+/***** Read VU-meters (input, output, analog and digital together) *****/
+static int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_info *uinfo)
+{
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 96;
+	uinfo->value.integer.min = ECHOGAIN_MINOUT;
+	uinfo->value.integer.max = 0;
+#ifdef ECHOCARD_HAS_VMIXER
+	uinfo->dimen.d[0] = 3;	/* Out, In, Virt */
+#else
+	uinfo->dimen.d[0] = 2;	/* Out, In */
+#endif
+	uinfo->dimen.d[1] = 16;	/* 16 channels */
+	uinfo->dimen.d[2] = 2;	/* 0=level, 1=peak */
+	return 0;
+}
+
+static int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	get_audio_meters(chip, ucontrol->value.integer.value);
+	return 0;
+}
+
+static struct snd_kcontrol_new snd_echo_vumeters __devinitdata = {
+	.name = "VU-meters",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info = snd_echo_vumeters_info,
+	.get = snd_echo_vumeters_get,
+};
+
+
+
+/*** Channels info - it exports informations about the number of channels ***/
+static int snd_echo_channels_info_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	struct echoaudio *chip;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 6;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1 << ECHO_CLOCK_NUMBER;
+	return 0;
+}
+
+static int snd_echo_channels_info_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct echoaudio *chip;
+	int detected, clocks, bit, src;
+
+	chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = num_busses_in(chip);
+	ucontrol->value.integer.value[1] = num_analog_busses_in(chip);
+	ucontrol->value.integer.value[2] = num_busses_out(chip);
+	ucontrol->value.integer.value[3] = num_analog_busses_out(chip);
+	ucontrol->value.integer.value[4] = num_pipes_out(chip);
+
+	/* Compute the bitmask of the currently valid input clocks */
+	detected = detect_input_clocks(chip);
+	clocks = 0;
+	src = chip->num_clock_sources - 1;
+	for (bit = ECHO_CLOCK_NUMBER - 1; bit >= 0; bit--)
+		if (detected & (1 << bit))
+			for (; src >= 0; src--)
+				if (bit == chip->clock_source_list[src]) {
+					clocks |= 1 << src;
+					break;
+				}
+	ucontrol->value.integer.value[5] = clocks;
+
+	return 0;
+}
+
+static struct snd_kcontrol_new snd_echo_channels_info __devinitdata = {
+	.name = "Channels info",
+	.iface = SNDRV_CTL_ELEM_IFACE_HWDEP,
+	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info = snd_echo_channels_info_info,
+	.get = snd_echo_channels_info_get,
+};
+
+
+
+
+/******************************************************************************
+	IRQ Handler
+******************************************************************************/
+
+static irqreturn_t snd_echo_interrupt(int irq, void *dev_id,
+				      struct pt_regs *regs)
+{
+	struct echoaudio *chip = dev_id;
+	struct snd_pcm_substream *substream;
+	int period, ss, st;
+
+	spin_lock(&chip->lock);
+	st = service_irq(chip);
+	if (st < 0) {
+		spin_unlock(&chip->lock);
+		return IRQ_NONE;
+	}
+	/* The hardware doesn't tell us which substream caused the irq,
+	thus we have to check all running substreams. */
+	for (ss = 0; ss < DSP_MAXPIPES; ss++) {
+		if ((substream = chip->substream[ss])) {
+			period = pcm_pointer(substream) /
+				substream->runtime->period_size;
+			if (period != chip->last_period[ss]) {
+				chip->last_period[ss] = period;
+				spin_unlock(&chip->lock);
+				snd_pcm_period_elapsed(substream);
+				spin_lock(&chip->lock);
+			}
+		}
+	}
+	spin_unlock(&chip->lock);
+
+#ifdef ECHOCARD_HAS_MIDI
+	if (st > 0 && chip->midi_in) {
+		snd_rawmidi_receive(chip->midi_in, chip->midi_buffer, st);
+		DE_MID(("rawmidi_iread=%d\n", st));
+	}
+#endif
+	return IRQ_HANDLED;
+}
+
+
+
+
+/******************************************************************************
+	Module construction / destruction
+******************************************************************************/
+
+static int snd_echo_free(struct echoaudio *chip)
+{
+	DE_INIT(("Stop DSP...\n"));
+	if (chip->comm_page) {
+		rest_in_peace(chip);
+		snd_dma_free_pages(&chip->commpage_dma_buf);
+	}
+	DE_INIT(("Stopped.\n"));
+
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+
+	if (chip->dsp_registers)
+		iounmap(chip->dsp_registers);
+
+	if (chip->iores)
+		release_and_free_resource(chip->iores);
+
+	DE_INIT(("MMIO freed.\n"));
+
+	pci_disable_device(chip->pci);
+
+	/* release chip data */
+	kfree(chip);
+	DE_INIT(("Chip freed.\n"));
+	return 0;
+}
+
+
+
+static int snd_echo_dev_free(struct snd_device *device)
+{
+	struct echoaudio *chip = device->device_data;
+
+	DE_INIT(("snd_echo_dev_free()...\n"));
+	return snd_echo_free(chip);
+}
+
+
+
+/* <--snd_echo_probe() */
+static __devinit int snd_echo_create(struct snd_card *card,
+				     struct pci_dev *pci,
+				     struct echoaudio **rchip)
+{
+	struct echoaudio *chip;
+	int err;
+	size_t sz;
+	static struct snd_device_ops ops = {
+		.dev_free = snd_echo_dev_free,
+	};
+
+	*rchip = NULL;
+
+	pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0xC0);
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	pci_set_master(pci);
+
+	/* allocate a chip-specific data */
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (!chip) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+	DE_INIT(("chip=%p\n", chip));
+
+	spin_lock_init(&chip->lock);
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+
+	/* PCI resource allocation */
+	chip->dsp_registers_phys = pci_resource_start(pci, 0);
+	sz = pci_resource_len(pci, 0);
+	if (sz > PAGE_SIZE)
+		sz = PAGE_SIZE;		/* We map only the required part */
+
+	if ((chip->iores = request_mem_region(chip->dsp_registers_phys, sz,
+					      ECHOCARD_NAME)) == NULL) {
+		snd_echo_free(chip);
+		snd_printk(KERN_ERR "cannot get memory region\n");
+		return -EBUSY;
+	}
+	chip->dsp_registers = (volatile u32 __iomem *)
+		ioremap_nocache(chip->dsp_registers_phys, sz);
+
+	if (request_irq(pci->irq, snd_echo_interrupt, SA_INTERRUPT | SA_SHIRQ,
+						ECHOCARD_NAME, (void *)chip)) {
+		snd_echo_free(chip);
+		snd_printk(KERN_ERR "cannot grab irq\n");
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+	DE_INIT(("pci=%p irq=%d subdev=%04x Init hardware...\n",
+		 chip->pci, chip->irq, chip->pci->subsystem_device));
+
+	/* Create the DSP comm page - this is the area of memory used for most
+	of the communication with the DSP, which accesses it via bus mastering */
+	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+				sizeof(struct comm_page),
+				&chip->commpage_dma_buf) < 0) {
+		snd_echo_free(chip);
+		snd_printk(KERN_ERR "cannot allocate the comm page\n");
+		return -ENOMEM;
+	}
+	chip->comm_page_phys = chip->commpage_dma_buf.addr;
+	chip->comm_page = (struct comm_page *)chip->commpage_dma_buf.area;
+
+	err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
+	if (err) {
+		DE_INIT(("init_hw err=%d\n", err));
+		snd_echo_free(chip);
+		return err;
+	}
+	DE_INIT(("Card init OK\n"));
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_echo_free(chip);
+		return err;
+	}
+	atomic_set(&chip->opencount, 0);
+	init_MUTEX(&chip->mode_mutex);
+	chip->can_set_rate = 1;
+	*rchip = chip;
+	/* Init done ! */
+	return 0;
+}
+
+
+
+/* constructor */
+static int __devinit snd_echo_probe(struct pci_dev *pci,
+				    const struct pci_device_id *pci_id)
+{
+	static int dev;
+	struct snd_card *card;
+	struct echoaudio *chip;
+	char *dsp;
+	int i, err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	DE_INIT(("Echoaudio driver starting...\n"));
+	i = 0;
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_echo_create(card, pci, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "Echo_" ECHOCARD_NAME);
+	strcpy(card->shortname, chip->card_name);
+
+	dsp = "56301";
+	if (pci_id->device == 0x3410)
+		dsp = "56361";
+
+	sprintf(card->longname, "%s rev.%d (DSP%s) at 0x%lx irq %i",
+		card->shortname, pci_id->subdevice & 0x000f, dsp,
+		chip->dsp_registers_phys, chip->irq);
+
+	if ((err = snd_echo_new_pcm(chip)) < 0) {
+		snd_printk(KERN_ERR "new pcm error %d\n", err);
+		snd_card_free(card);
+		return err;
+	}
+
+#ifdef ECHOCARD_HAS_MIDI
+	if (chip->has_midi) {	/* Some Mia's do not have midi */
+		if ((err = snd_echo_midi_create(card, chip)) < 0) {
+			snd_printk(KERN_ERR "new midi error %d\n", err);
+			snd_card_free(card);
+			return err;
+		}
+	}
+#endif
+
+#ifdef ECHOCARD_HAS_VMIXER
+	snd_echo_vmixer.count = num_pipes_out(chip) * num_busses_out(chip);
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_output_gain, chip))) < 0)
+		goto ctl_error;
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip))) < 0)
+		goto ctl_error;
+#else
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_pcm_output_gain, chip))) < 0)
+		goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip))) < 0)
+		goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+	if (!chip->hasnt_input_nominal_level)
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_intput_nominal_level, chip))) < 0)
+			goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_output_nominal_level, chip))) < 0)
+		goto ctl_error;
+#endif
+
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters_switch, chip))) < 0)
+		goto ctl_error;
+
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters, chip))) < 0)
+		goto ctl_error;
+
+#ifdef ECHOCARD_HAS_MONITOR
+	snd_echo_monitor_mixer.count = num_busses_in(chip) * num_busses_out(chip);
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_monitor_mixer, chip))) < 0)
+		goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_automute_switch, chip))) < 0)
+		goto ctl_error;
+#endif
+
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_channels_info, chip))) < 0)
+		goto ctl_error;
+
+#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+	/* Creates a list of available digital modes */
+	chip->num_digital_modes = 0;
+	for (i = 0; i < 6; i++)
+		if (chip->digital_modes & (1 << i))
+			chip->digital_mode_list[chip->num_digital_modes++] = i;
+
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_digital_mode_switch, chip))) < 0)
+		goto ctl_error;
+#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */
+
+#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
+	/* Creates a list of available clock sources */
+	chip->num_clock_sources = 0;
+	for (i = 0; i < 10; i++)
+		if (chip->input_clock_types & (1 << i))
+			chip->clock_source_list[chip->num_clock_sources++] = i;
+
+	if (chip->num_clock_sources > 1) {
+		chip->clock_src_ctl = snd_ctl_new1(&snd_echo_clock_source_switch, chip);
+		if ((err = snd_ctl_add(chip->card, chip->clock_src_ctl)) < 0)
+			goto ctl_error;
+	}
+#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_spdif_mode_switch, chip))) < 0)
+		goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_PHANTOM_POWER
+	if (chip->has_phantom_power)
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_phantom_power_switch, chip))) < 0)
+			goto ctl_error;
+#endif
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		goto ctl_error;
+	}
+	snd_printk(KERN_INFO "Card registered: %s\n", card->longname);
+
+	pci_set_drvdata(pci, chip);
+	dev++;
+	return 0;
+
+ctl_error:
+	snd_printk(KERN_ERR "new control error %d\n", err);
+	snd_card_free(card);
+	return err;
+}
+
+
+
+static void __devexit snd_echo_remove(struct pci_dev *pci)
+{
+	struct echoaudio *chip;
+
+	chip = pci_get_drvdata(pci);
+	if (chip)
+		snd_card_free(chip->card);
+	pci_set_drvdata(pci, NULL);
+}
+
+
+
+/******************************************************************************
+	Everything starts and ends here
+******************************************************************************/
+
+/* pci_driver definition */
+static struct pci_driver driver = {
+	.name = "Echoaudio " ECHOCARD_NAME,
+	.id_table = snd_echo_ids,
+	.probe = snd_echo_probe,
+	.remove = __devexit_p(snd_echo_remove),
+};
+
+
+
+/* initialization of the module */
+static int __init alsa_card_echo_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+
+
+/* clean up the module */
+static void __exit alsa_card_echo_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+
+module_init(alsa_card_echo_init)
+module_exit(alsa_card_echo_exit)
diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h
new file mode 100644
index 000000000000..7e88c968e22f
--- /dev/null
+++ b/sound/pci/echoaudio/echoaudio.h
@@ -0,0 +1,590 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+ ****************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+ ****************************************************************************
+
+
+   Here's a block diagram of how most of the cards work:
+
+                  +-----------+
+           record |           |<-------------------- Inputs
+          <-------|           |        |
+     PCI          | Transport |        |
+     bus          |  engine   |       \|/
+          ------->|           |    +-------+
+            play  |           |--->|monitor|-------> Outputs
+                  +-----------+    | mixer |
+                                   +-------+
+
+   The lines going to and from the PCI bus represent "pipes".  A pipe performs
+   audio transport - moving audio data to and from buffers on the host via
+   bus mastering.
+
+   The inputs and outputs on the right represent input and output "busses."
+   A bus is a physical, real connection to the outside world.  An example
+   of a bus would be the 1/4" analog connectors on the back of Layla or
+   an RCA S/PDIF connector.
+
+   For most cards, there is a one-to-one correspondence between outputs
+   and busses; that is, each individual pipe is hard-wired to a single bus.
+
+   Cards that work this way are Darla20, Gina20, Layla20, Darla24, Gina24,
+   Layla24, Mona, and Indigo.
+
+
+   Mia has a feature called "virtual outputs."
+
+
+                  +-----------+
+           record |           |<----------------------------- Inputs
+          <-------|           |                  |
+     PCI          | Transport |                  |
+     bus          |  engine   |                 \|/
+          ------->|           |   +------+   +-------+
+            play  |           |-->|vmixer|-->|monitor|-------> Outputs
+                  +-----------+   +------+   | mixer |
+                                             +-------+
+
+
+   Obviously, the difference here is the box labeled "vmixer."  Vmixer is
+   short for "virtual output mixer."  For Mia, pipes are *not* hard-wired
+   to a single bus; the vmixer lets you mix any pipe to any bus in any
+   combination.
+
+   Note, however, that the left-hand side of the diagram is unchanged.
+   Transport works exactly the same way - the difference is in the mixer stage.
+
+
+   Pipes and busses are numbered starting at zero.
+
+
+
+   Pipe index
+   ==========
+
+   A number of calls in CEchoGals refer to a "pipe index".  A pipe index is
+   a unique number for a pipe that unambiguously refers to a playback or record
+   pipe.  Pipe indices are numbered starting with analog outputs, followed by
+   digital outputs, then analog inputs, then digital inputs.
+
+   Take Gina24 as an example:
+
+   Pipe index
+
+   0-7            Analog outputs (0 .. FirstDigitalBusOut-1)
+   8-15           Digital outputs (FirstDigitalBusOut .. NumBussesOut-1)
+   16-17          Analog inputs
+   18-25          Digital inputs
+
+
+   You get the pipe index by calling CEchoGals::OpenAudio; the other transport
+   functions take the pipe index as a parameter.  If you need a pipe index for
+   some other reason, use the handy Makepipe_index method.
+
+
+   Some calls take a CChannelMask parameter; CChannelMask is a handy way to
+   group pipe indices.
+
+
+
+   Digital mode switch
+   ===================
+
+   Some cards (right now, Gina24, Layla24, and Mona) have a Digital Mode Switch
+   or DMS.  Cards with a DMS can be set to one of three mutually exclusive
+   digital modes: S/PDIF RCA, S/PDIF optical, or ADAT optical.
+
+   This may create some confusion since ADAT optical is 8 channels wide and
+   S/PDIF is only two channels wide.  Gina24, Layla24, and Mona handle this
+   by acting as if they always have 8 digital outs and ins.  If you are in
+   either S/PDIF mode, the last 6 channels don't do anything - data sent
+   out these channels is thrown away and you will always record zeros.
+
+   Note that with Gina24, Layla24, and Mona, sample rates above 50 kHz are
+   only available if you have the card configured for S/PDIF optical or S/PDIF
+   RCA.
+
+
+
+   Double speed mode
+   =================
+
+   Some of the cards support 88.2 kHz and 96 kHz sampling (Darla24, Gina24,
+   Layla24, Mona, Mia, and Indigo).  For these cards, the driver sometimes has
+   to worry about "double speed mode"; double speed mode applies whenever the
+   sampling rate is above 50 kHz.
+
+   For instance, Mona and Layla24 support word clock sync.  However, they
+   actually support two different word clock modes - single speed (below
+   50 kHz) and double speed (above 50 kHz).  The hardware detects if a single
+   or double speed word clock signal is present; the generic code uses that
+   information to determine which mode to use.
+
+   The generic code takes care of all this for you.
+*/
+
+
+#ifndef _ECHOAUDIO_H_
+#define _ECHOAUDIO_H_
+
+
+#define TRUE 1
+#define FALSE 0
+
+#include "echoaudio_dsp.h"
+
+
+
+/***********************************************************************
+
+	PCI configuration space
+
+***********************************************************************/
+
+/*
+ * PCI vendor ID and device IDs for the hardware
+ */
+#define VENDOR_ID		0x1057
+#define DEVICE_ID_56301		0x1801
+#define DEVICE_ID_56361		0x3410
+#define SUBVENDOR_ID		0xECC0
+
+
+/*
+ * Valid Echo PCI subsystem card IDs
+ */
+#define DARLA20			0x0010
+#define GINA20			0x0020
+#define LAYLA20			0x0030
+#define DARLA24			0x0040
+#define GINA24			0x0050
+#define LAYLA24			0x0060
+#define MONA			0x0070
+#define MIA			0x0080
+#define INDIGO			0x0090
+#define INDIGO_IO		0x00a0
+#define INDIGO_DJ		0x00b0
+#define ECHO3G			0x0100
+
+
+/************************************************************************
+
+	Array sizes and so forth
+
+***********************************************************************/
+
+/*
+ * Sizes
+ */
+#define ECHO_MAXAUDIOINPUTS	32	/* Max audio input channels */
+#define ECHO_MAXAUDIOOUTPUTS	32	/* Max audio output channels */
+#define ECHO_MAXAUDIOPIPES	32	/* Max number of input and output
+					 * pipes */
+#define E3G_MAX_OUTPUTS		16
+#define ECHO_MAXMIDIJACKS	1	/* Max MIDI ports */
+#define ECHO_MIDI_QUEUE_SZ 	512	/* Max MIDI input queue entries */
+#define ECHO_MTC_QUEUE_SZ	32	/* Max MIDI time code input queue
+					 * entries */
+
+/*
+ * MIDI activity indicator timeout
+ */
+#define MIDI_ACTIVITY_TIMEOUT_USEC	200000
+
+
+/****************************************************************************
+ 
+   Clocks
+
+*****************************************************************************/
+
+/*
+ * Clock numbers
+ */
+#define ECHO_CLOCK_INTERNAL		0
+#define ECHO_CLOCK_WORD			1
+#define ECHO_CLOCK_SUPER		2
+#define ECHO_CLOCK_SPDIF		3
+#define ECHO_CLOCK_ADAT			4
+#define ECHO_CLOCK_ESYNC		5
+#define ECHO_CLOCK_ESYNC96		6
+#define ECHO_CLOCK_MTC			7
+#define ECHO_CLOCK_NUMBER		8
+#define ECHO_CLOCKS			0xffff
+
+/*
+ * Clock bit numbers - used to report capabilities and whatever clocks
+ * are being detected dynamically.
+ */
+#define ECHO_CLOCK_BIT_INTERNAL		(1 << ECHO_CLOCK_INTERNAL)
+#define ECHO_CLOCK_BIT_WORD		(1 << ECHO_CLOCK_WORD)
+#define ECHO_CLOCK_BIT_SUPER		(1 << ECHO_CLOCK_SUPER)
+#define ECHO_CLOCK_BIT_SPDIF		(1 << ECHO_CLOCK_SPDIF)
+#define ECHO_CLOCK_BIT_ADAT		(1 << ECHO_CLOCK_ADAT)
+#define ECHO_CLOCK_BIT_ESYNC		(1 << ECHO_CLOCK_ESYNC)
+#define ECHO_CLOCK_BIT_ESYNC96		(1 << ECHO_CLOCK_ESYNC96)
+#define ECHO_CLOCK_BIT_MTC		(1<<ECHO_CLOCK_MTC)
+
+
+/***************************************************************************
+
+   Digital modes
+
+****************************************************************************/
+
+/*
+ * Digital modes for Mona, Layla24, and Gina24
+ */
+#define DIGITAL_MODE_NONE			0xFF
+#define DIGITAL_MODE_SPDIF_RCA			0
+#define DIGITAL_MODE_SPDIF_OPTICAL		1
+#define DIGITAL_MODE_ADAT			2
+#define DIGITAL_MODE_SPDIF_CDROM		3
+#define DIGITAL_MODES				4
+
+/*
+ * Digital mode capability masks
+ */
+#define ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA	(1 << DIGITAL_MODE_SPDIF_RCA)
+#define ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL	(1 << DIGITAL_MODE_SPDIF_OPTICAL)
+#define ECHOCAPS_HAS_DIGITAL_MODE_ADAT		(1 << DIGITAL_MODE_ADAT)
+#define ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_CDROM	(1 << DIGITAL_MODE_SPDIF_CDROM)
+
+
+#define EXT_3GBOX_NC			0x01	/* 3G box not connected */
+#define EXT_3GBOX_NOT_SET		0x02	/* 3G box not detected yet */
+
+
+#define ECHOGAIN_MUTED		(-128)	/* Minimum possible gain */
+#define ECHOGAIN_MINOUT		(-128)	/* Min output gain (dB) */
+#define ECHOGAIN_MAXOUT		(6)	/* Max output gain (dB) */
+#define ECHOGAIN_MININP		(-50)	/* Min input gain (0.5 dB) */
+#define ECHOGAIN_MAXINP		(50)	/* Max input gain (0.5 dB) */
+
+#define PIPE_STATE_STOPPED	0	/* Pipe has been reset */
+#define PIPE_STATE_PAUSED	1	/* Pipe has been stopped */
+#define PIPE_STATE_STARTED	2	/* Pipe has been started */
+#define PIPE_STATE_PENDING	3	/* Pipe has pending start */
+
+
+/* Debug initialization */
+#ifdef CONFIG_SND_DEBUG
+#define DE_INIT(x) snd_printk x
+#else
+#define DE_INIT(x)
+#endif
+
+/* Debug hw_params callbacks */
+#ifdef CONFIG_SND_DEBUG
+#define DE_HWP(x) snd_printk x
+#else
+#define DE_HWP(x)
+#endif
+
+/* Debug normal activity (open, start, stop...) */
+#ifdef CONFIG_SND_DEBUG
+#define DE_ACT(x) snd_printk x
+#else
+#define DE_ACT(x)
+#endif
+
+/* Debug midi activity */
+#ifdef CONFIG_SND_DEBUG
+#define DE_MID(x) snd_printk x
+#else
+#define DE_MID(x)
+#endif
+
+
+struct audiopipe {
+	volatile u32 *dma_counter;	/* Commpage register that contains
+					 * the current dma position
+					 * (lower 32 bits only)
+					 */
+	u32 last_counter;		/* The last position, which is used
+					 * to compute...
+					 */
+	u32 position;			/* ...the number of bytes tranferred
+					 * by the DMA engine, modulo the
+					 * buffer size
+					 */
+	short index;			/* Index of the first channel or <0
+					 * if hw is not configured yet
+					 */
+	short interleave;
+	struct snd_dma_buffer sgpage;	/* Room for the scatter-gather list */
+	struct snd_pcm_hardware hw;
+	struct snd_pcm_hw_constraint_list constr;
+	short sglist_head;
+	char state;			/* pipe state */
+};
+
+
+struct audioformat {
+	u8 interleave;			/* How the data is arranged in memory:
+					 * mono = 1, stereo = 2, ...
+					 */
+	u8 bits_per_sample;		/* 8, 16, 24, 32 (24 bits left aligned) */
+	char mono_to_stereo;		/* Only used if interleave is 1 and
+					 * if this is an output pipe.
+					 */
+	char data_are_bigendian;	/* 1 = big endian, 0 = little endian */
+};
+
+
+struct echoaudio {
+	spinlock_t lock;
+	struct snd_pcm_substream *substream[DSP_MAXPIPES];
+	int last_period[DSP_MAXPIPES];
+	struct semaphore mode_mutex;
+	u16 num_digital_modes, digital_mode_list[6];
+	u16 num_clock_sources, clock_source_list[10];
+	atomic_t opencount;
+	struct snd_kcontrol *clock_src_ctl;
+	struct snd_pcm *analog_pcm, *digital_pcm;
+	struct snd_card *card;
+	const char *card_name;
+	struct pci_dev *pci;
+	unsigned long dsp_registers_phys;
+	struct resource *iores;
+	struct snd_dma_buffer commpage_dma_buf;
+	int irq;
+#ifdef ECHOCARD_HAS_MIDI
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_substream *midi_in, *midi_out;
+#endif
+	struct timer_list timer;
+	char tinuse;				/* Timer in use */
+	char midi_full;				/* MIDI output buffer is full */
+	char can_set_rate;
+	char rate_set;
+
+	/* This stuff is used mainly by the lowlevel code */
+	struct comm_page *comm_page;	/* Virtual address of the memory
+					 * seen by DSP
+					 */
+	u32 pipe_alloc_mask;		/* Bitmask of allocated pipes */
+	u32 pipe_cyclic_mask;		/* Bitmask of pipes with cyclic
+					 * buffers
+					 */
+	u32 sample_rate;		/* Card sample rate in Hz */
+	u8 digital_mode;		/* Current digital mode
+					 * (see DIGITAL_MODE_*)
+					 */
+	u8 spdif_status;		/* Gina20, Darla20, Darla24 - only */
+	u8 clock_state;			/* Gina20, Darla20, Darla24 - only */
+	u8 input_clock;			/* Currently selected sample clock
+					 * source
+					 */
+	u8 output_clock;		/* Layla20 only */
+	char meters_enabled;		/* VU-meters status */
+	char asic_loaded;		/* Set TRUE when ASIC loaded */
+	char bad_board;			/* Set TRUE if DSP won't load */
+	char professional_spdif;	/* 0 = consumer; 1 = professional */
+	char non_audio_spdif;		/* 3G - only */
+	char digital_in_automute;	/* Gina24, Layla24, Mona - only */
+	char has_phantom_power;
+	char hasnt_input_nominal_level;	/* Gina3G */
+	char phantom_power;		/* Gina3G - only */
+	char has_midi;
+	char midi_input_enabled;
+
+#ifdef ECHOCARD_ECHO3G
+	/* External module -dependent pipe and bus indexes */
+	char px_digital_out, px_analog_in, px_digital_in, px_num;
+	char bx_digital_out, bx_analog_in, bx_digital_in, bx_num;
+#endif
+
+	char nominal_level[ECHO_MAXAUDIOPIPES];	/* True == -10dBV
+						 * False == +4dBu */
+	s8 input_gain[ECHO_MAXAUDIOINPUTS];	/* Input level -50..+50
+						 * unit is 0.5dB */
+	s8 output_gain[ECHO_MAXAUDIOOUTPUTS];	/* Output level -128..+6 dB
+						 * (-128=muted) */
+	s8 monitor_gain[ECHO_MAXAUDIOOUTPUTS][ECHO_MAXAUDIOINPUTS];
+		/* -128..+6 dB */
+	s8 vmixer_gain[ECHO_MAXAUDIOOUTPUTS][ECHO_MAXAUDIOOUTPUTS];
+		/* -128..+6 dB */
+
+	u16 digital_modes;		/* Bitmask of supported modes
+					 * (see ECHOCAPS_HAS_DIGITAL_MODE_*) */
+	u16 input_clock_types;		/* Suppoted input clock types */
+	u16 output_clock_types;		/* Suppoted output clock types -
+					 * Layla20 only */
+	u16 device_id, subdevice_id;
+	u16 *dsp_code;			/* Current DSP code loaded,
+					 * NULL if nothing loaded */
+	const struct firmware *dsp_code_to_load;/* DSP code to load */
+	const struct firmware *asic_code;	/* Current ASIC code */
+	u32 comm_page_phys;			/* Physical address of the
+						 * memory seen by DSP */
+	volatile u32 __iomem *dsp_registers;	/* DSP's register base */
+	u32 active_mask;			/* Chs. active mask or
+						 * punks out */
+
+#ifdef ECHOCARD_HAS_MIDI
+	u16 mtc_state;				/* State for MIDI input parsing state machine */
+	u8 midi_buffer[MIDI_IN_BUFFER_SIZE];
+#endif
+};
+
+
+static int init_dsp_comm_page(struct echoaudio *chip);
+static int init_line_levels(struct echoaudio *chip);
+static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe);
+static int load_firmware(struct echoaudio *chip);
+static int wait_handshake(struct echoaudio *chip);
+static int send_vector(struct echoaudio *chip, u32 command);
+static int get_firmware(const struct firmware **fw_entry,
+			const struct firmware *frm, struct echoaudio *chip);
+static void free_firmware(const struct firmware *fw_entry);
+
+#ifdef ECHOCARD_HAS_MIDI
+static int enable_midi_input(struct echoaudio *chip, char enable);
+static int midi_service_irq(struct echoaudio *chip);
+static int __devinit snd_echo_midi_create(struct snd_card *card,
+					  struct echoaudio *chip);
+#endif
+
+
+static inline void clear_handshake(struct echoaudio *chip)
+{
+	chip->comm_page->handshake = 0;
+}
+
+static inline u32 get_dsp_register(struct echoaudio *chip, u32 index)
+{
+	return readl(&chip->dsp_registers[index]);
+}
+
+static inline void set_dsp_register(struct echoaudio *chip, u32 index,
+				    u32 value)
+{
+	writel(value, &chip->dsp_registers[index]);
+}
+
+
+/* Pipe and bus indexes. PX_* and BX_* are defined as chip->px_* and chip->bx_*
+for 3G cards because they depend on the external box. They are integer
+constants for all other cards.
+Never use those defines directly, use the following functions instead. */
+
+static inline int px_digital_out(const struct echoaudio *chip)
+{
+	return PX_DIGITAL_OUT;
+}
+
+static inline int px_analog_in(const struct echoaudio *chip)
+{
+	return PX_ANALOG_IN;
+}
+
+static inline int px_digital_in(const struct echoaudio *chip)
+{
+	return PX_DIGITAL_IN;
+}
+
+static inline int px_num(const struct echoaudio *chip)
+{
+	return PX_NUM;
+}
+
+static inline int bx_digital_out(const struct echoaudio *chip)
+{
+	return BX_DIGITAL_OUT;
+}
+
+static inline int bx_analog_in(const struct echoaudio *chip)
+{
+	return BX_ANALOG_IN;
+}
+
+static inline int bx_digital_in(const struct echoaudio *chip)
+{
+	return BX_DIGITAL_IN;
+}
+
+static inline int bx_num(const struct echoaudio *chip)
+{
+	return BX_NUM;
+}
+
+static inline int num_pipes_out(const struct echoaudio *chip)
+{
+	return px_analog_in(chip);
+}
+
+static inline int num_pipes_in(const struct echoaudio *chip)
+{
+	return px_num(chip) - px_analog_in(chip);
+}
+
+static inline int num_busses_out(const struct echoaudio *chip)
+{
+	return bx_analog_in(chip);
+}
+
+static inline int num_busses_in(const struct echoaudio *chip)
+{
+	return bx_num(chip) - bx_analog_in(chip);
+}
+
+static inline int num_analog_busses_out(const struct echoaudio *chip)
+{
+	return bx_digital_out(chip);
+}
+
+static inline int num_analog_busses_in(const struct echoaudio *chip)
+{
+	return bx_digital_in(chip) - bx_analog_in(chip);
+}
+
+static inline int num_digital_busses_out(const struct echoaudio *chip)
+{
+	return num_busses_out(chip) - num_analog_busses_out(chip);
+}
+
+static inline int num_digital_busses_in(const struct echoaudio *chip)
+{
+	return num_busses_in(chip) - num_analog_busses_in(chip);
+}
+
+/* The monitor array is a one-dimensional array; compute the offset
+ * into the array */
+static inline int monitor_index(const struct echoaudio *chip, int out, int in)
+{
+	return out * num_busses_in(chip) + in;
+}
+
+
+#ifndef pci_device
+#define pci_device(chip) (&chip->pci->dev)
+#endif
+
+
+#endif /* _ECHOAUDIO_H_ */
diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c
new file mode 100644
index 000000000000..9f439ea459f4
--- /dev/null
+++ b/sound/pci/echoaudio/echoaudio_3g.c
@@ -0,0 +1,431 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+
+/* These functions are common for all "3G" cards */
+
+
+static int check_asic_status(struct echoaudio *chip)
+{
+	u32 box_status;
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	chip->comm_page->ext_box_status =
+		__constant_cpu_to_le32(E3G_ASIC_NOT_LOADED);
+	chip->asic_loaded = FALSE;
+	clear_handshake(chip);
+	send_vector(chip, DSP_VC_TEST_ASIC);
+
+	if (wait_handshake(chip)) {
+		chip->dsp_code = NULL;
+		return -EIO;
+	}
+
+	box_status = le32_to_cpu(chip->comm_page->ext_box_status);
+	DE_INIT(("box_status=%x\n", box_status));
+	if (box_status == E3G_ASIC_NOT_LOADED)
+		return -ENODEV;
+
+	chip->asic_loaded = TRUE;
+	return box_status & E3G_BOX_TYPE_MASK;
+}
+
+
+
+static inline u32 get_frq_reg(struct echoaudio *chip)
+{
+	return le32_to_cpu(chip->comm_page->e3g_frq_register);
+}
+
+
+
+/* Most configuration of 3G cards is accomplished by writing the control
+register. write_control_reg sends the new control register value to the DSP. */
+static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq,
+			     char force)
+{
+	if (wait_handshake(chip))
+		return -EIO;
+
+	DE_ACT(("WriteControlReg: Setting 0x%x, 0x%x\n", ctl, frq));
+
+	ctl = cpu_to_le32(ctl);
+	frq = cpu_to_le32(frq);
+
+	if (ctl != chip->comm_page->control_register ||
+	    frq != chip->comm_page->e3g_frq_register || force) {
+		chip->comm_page->e3g_frq_register = frq;
+		chip->comm_page->control_register = ctl;
+		clear_handshake(chip);
+		return send_vector(chip, DSP_VC_WRITE_CONTROL_REG);
+	}
+
+	DE_ACT(("WriteControlReg: not written, no change\n"));
+	return 0;
+}
+
+
+
+/* Set the digital mode - currently for Gina24, Layla24, Mona, 3G */
+static int set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+	u8 previous_mode;
+	int err, i, o;
+
+	/* All audio channels must be closed before changing the digital mode */
+	snd_assert(!chip->pipe_alloc_mask, return -EAGAIN);
+
+	snd_assert(chip->digital_modes & (1 << mode), return -EINVAL);
+
+	previous_mode = chip->digital_mode;
+	err = dsp_set_digital_mode(chip, mode);
+
+	/* If we successfully changed the digital mode from or to ADAT,
+	 * then make sure all output, input and monitor levels are
+	 * updated by the DSP comm object. */
+	if (err >= 0 && previous_mode != mode &&
+	    (previous_mode == DIGITAL_MODE_ADAT || mode == DIGITAL_MODE_ADAT)) {
+		spin_lock_irq(&chip->lock);
+		for (o = 0; o < num_busses_out(chip); o++)
+			for (i = 0; i < num_busses_in(chip); i++)
+				set_monitor_gain(chip, o, i,
+						 chip->monitor_gain[o][i]);
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+		for (i = 0; i < num_busses_in(chip); i++)
+			set_input_gain(chip, i, chip->input_gain[i]);
+		update_input_line_level(chip);
+#endif
+
+		for (o = 0; o < num_busses_out(chip); o++)
+			set_output_gain(chip, o, chip->output_gain[o]);
+		update_output_line_level(chip);
+		spin_unlock_irq(&chip->lock);
+	}
+
+	return err;
+}
+
+
+
+static u32 set_spdif_bits(struct echoaudio *chip, u32 control_reg, u32 rate)
+{
+	control_reg &= E3G_SPDIF_FORMAT_CLEAR_MASK;
+
+	switch (rate) {
+	case 32000 :
+		control_reg |= E3G_SPDIF_SAMPLE_RATE0 | E3G_SPDIF_SAMPLE_RATE1;
+		break;
+	case 44100 :
+		if (chip->professional_spdif)
+			control_reg |= E3G_SPDIF_SAMPLE_RATE0;
+		break;
+	case 48000 :
+		control_reg |= E3G_SPDIF_SAMPLE_RATE1;
+		break;
+	}
+
+	if (chip->professional_spdif)
+		control_reg |= E3G_SPDIF_PRO_MODE;
+
+	if (chip->non_audio_spdif)
+		control_reg |= E3G_SPDIF_NOT_AUDIO;
+
+	control_reg |= E3G_SPDIF_24_BIT | E3G_SPDIF_TWO_CHANNEL |
+		E3G_SPDIF_COPY_PERMIT;
+
+	return control_reg;
+}
+
+
+
+/* Set the S/PDIF output format */
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+	u32 control_reg;
+
+	control_reg = le32_to_cpu(chip->comm_page->control_register);
+	chip->professional_spdif = prof;
+	control_reg = set_spdif_bits(chip, control_reg, chip->sample_rate);
+	return write_control_reg(chip, control_reg, get_frq_reg(chip), 0);
+}
+
+
+
+/* detect_input_clocks() returns a bitmask consisting of all the input clocks
+currently connected to the hardware; this changes as the user connects and
+disconnects clock inputs. You should use this information to determine which
+clocks the user is allowed to select. */
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+	u32 clocks_from_dsp, clock_bits;
+
+	/* Map the DSP clock detect bits to the generic driver clock
+	 * detect bits */
+	clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+	clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+	if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD)
+		clock_bits |= ECHO_CLOCK_BIT_WORD;
+
+	switch(chip->digital_mode) {
+	case DIGITAL_MODE_SPDIF_RCA:
+	case DIGITAL_MODE_SPDIF_OPTICAL:
+		if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF)
+			clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+		break;
+	case DIGITAL_MODE_ADAT:
+		if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_ADAT)
+			clock_bits |= ECHO_CLOCK_BIT_ADAT;
+		break;
+	}
+
+	return clock_bits;
+}
+
+
+
+static int load_asic(struct echoaudio *chip)
+{
+	int box_type, err;
+
+	if (chip->asic_loaded)
+		return 0;
+
+	/* Give the DSP a few milliseconds to settle down */
+	mdelay(2);
+
+	err = load_asic_generic(chip, DSP_FNC_LOAD_3G_ASIC,
+				&card_fw[FW_3G_ASIC]);
+	if (err < 0)
+		return err;
+
+	chip->asic_code = &card_fw[FW_3G_ASIC];
+
+	/* Now give the new ASIC a little time to set up */
+	mdelay(2);
+	/* See if it worked */
+	box_type = check_asic_status(chip);
+
+	/* Set up the control register if the load succeeded -
+	 * 48 kHz, internal clock, S/PDIF RCA mode */
+	if (box_type >= 0) {
+		err = write_control_reg(chip, E3G_48KHZ,
+					E3G_FREQ_REG_DEFAULT, TRUE);
+		if (err < 0)
+			return err;
+	}
+
+	return box_type;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+	u32 control_reg, clock, base_rate, frq_reg;
+
+	/* Only set the clock for internal mode. */
+	if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+		DE_ACT(("set_sample_rate: Cannot set sample rate - "
+			"clock not set to CLK_CLOCKININTERNAL\n"));
+		/* Save the rate anyhow */
+		chip->comm_page->sample_rate = cpu_to_le32(rate);
+		chip->sample_rate = rate;
+		set_input_clock(chip, chip->input_clock);
+		return 0;
+	}
+
+	snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
+		   return -EINVAL);
+
+	clock = 0;
+	control_reg = le32_to_cpu(chip->comm_page->control_register);
+	control_reg &= E3G_CLOCK_CLEAR_MASK;
+
+	switch (rate) {
+	case 96000:
+		clock = E3G_96KHZ;
+		break;
+	case 88200:
+		clock = E3G_88KHZ;
+		break;
+	case 48000:
+		clock = E3G_48KHZ;
+		break;
+	case 44100:
+		clock = E3G_44KHZ;
+		break;
+	case 32000:
+		clock = E3G_32KHZ;
+		break;
+	default:
+		clock = E3G_CONTINUOUS_CLOCK;
+		if (rate > 50000)
+			clock |= E3G_DOUBLE_SPEED_MODE;
+		break;
+	}
+
+	control_reg |= clock;
+	control_reg = set_spdif_bits(chip, control_reg, rate);
+
+	base_rate = rate;
+	if (base_rate > 50000)
+		base_rate /= 2;
+	if (base_rate < 32000)
+		base_rate = 32000;
+
+	frq_reg = E3G_MAGIC_NUMBER / base_rate - 2;
+	if (frq_reg > E3G_FREQ_REG_MAX)
+		frq_reg = E3G_FREQ_REG_MAX;
+
+	chip->comm_page->sample_rate = cpu_to_le32(rate);	/* ignored by the DSP */
+	chip->sample_rate = rate;
+	DE_ACT(("SetSampleRate: %d clock %x\n", rate, control_reg));
+
+	/* Tell the DSP about it - DSP reads both control reg & freq reg */
+	return write_control_reg(chip, control_reg, frq_reg, 0);
+}
+
+
+
+/* Set the sample clock source to internal, S/PDIF, ADAT */
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+	u32 control_reg, clocks_from_dsp;
+
+	DE_ACT(("set_input_clock:\n"));
+
+	/* Mask off the clock select bits */
+	control_reg = le32_to_cpu(chip->comm_page->control_register) &
+		E3G_CLOCK_CLEAR_MASK;
+	clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+	switch (clock) {
+	case ECHO_CLOCK_INTERNAL:
+		DE_ACT(("Set Echo3G clock to INTERNAL\n"));
+		chip->input_clock = ECHO_CLOCK_INTERNAL;
+		return set_sample_rate(chip, chip->sample_rate);
+	case ECHO_CLOCK_SPDIF:
+		if (chip->digital_mode == DIGITAL_MODE_ADAT)
+			return -EAGAIN;
+		DE_ACT(("Set Echo3G clock to SPDIF\n"));
+		control_reg |= E3G_SPDIF_CLOCK;
+		if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF96)
+			control_reg |= E3G_DOUBLE_SPEED_MODE;
+		else
+			control_reg &= ~E3G_DOUBLE_SPEED_MODE;
+		break;
+	case ECHO_CLOCK_ADAT:
+		if (chip->digital_mode != DIGITAL_MODE_ADAT)
+			return -EAGAIN;
+		DE_ACT(("Set Echo3G clock to ADAT\n"));
+		control_reg |= E3G_ADAT_CLOCK;
+		control_reg &= ~E3G_DOUBLE_SPEED_MODE;
+		break;
+	case ECHO_CLOCK_WORD:
+		DE_ACT(("Set Echo3G clock to WORD\n"));
+		control_reg |= E3G_WORD_CLOCK;
+		if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD96)
+			control_reg |= E3G_DOUBLE_SPEED_MODE;
+		else
+			control_reg &= ~E3G_DOUBLE_SPEED_MODE;
+		break;
+	default:
+		DE_ACT(("Input clock 0x%x not supported for Echo3G\n", clock));
+		return -EINVAL;
+	}
+
+	chip->input_clock = clock;
+	return write_control_reg(chip, control_reg, get_frq_reg(chip), 1);
+}
+
+
+
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+	u32 control_reg;
+	int err, incompatible_clock;
+
+	/* Set clock to "internal" if it's not compatible with the new mode */
+	incompatible_clock = FALSE;
+	switch (mode) {
+	case DIGITAL_MODE_SPDIF_OPTICAL:
+	case DIGITAL_MODE_SPDIF_RCA:
+		if (chip->input_clock == ECHO_CLOCK_ADAT)
+			incompatible_clock = TRUE;
+		break;
+	case DIGITAL_MODE_ADAT:
+		if (chip->input_clock == ECHO_CLOCK_SPDIF)
+			incompatible_clock = TRUE;
+		break;
+	default:
+		DE_ACT(("Digital mode not supported: %d\n", mode));
+		return -EINVAL;
+	}
+
+	spin_lock_irq(&chip->lock);
+
+	if (incompatible_clock) {
+		chip->sample_rate = 48000;
+		set_input_clock(chip, ECHO_CLOCK_INTERNAL);
+	}
+
+	/* Clear the current digital mode */
+	control_reg = le32_to_cpu(chip->comm_page->control_register);
+	control_reg &= E3G_DIGITAL_MODE_CLEAR_MASK;
+
+	/* Tweak the control reg */
+	switch (mode) {
+	case DIGITAL_MODE_SPDIF_OPTICAL:
+		control_reg |= E3G_SPDIF_OPTICAL_MODE;
+		break;
+	case DIGITAL_MODE_SPDIF_RCA:
+		/* E3G_SPDIF_OPTICAL_MODE bit cleared */
+		break;
+	case DIGITAL_MODE_ADAT:
+		control_reg |= E3G_ADAT_MODE;
+		control_reg &= ~E3G_DOUBLE_SPEED_MODE;	/* @@ useless */
+		break;
+	}
+
+	err = write_control_reg(chip, control_reg, get_frq_reg(chip), 1);
+	spin_unlock_irq(&chip->lock);
+	if (err < 0)
+		return err;
+	chip->digital_mode = mode;
+
+	DE_ACT(("set_digital_mode(%d)\n", chip->digital_mode));
+	return incompatible_clock;
+}
diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c
new file mode 100644
index 000000000000..42afa837d9b4
--- /dev/null
+++ b/sound/pci/echoaudio/echoaudio_dsp.c
@@ -0,0 +1,1125 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+#if PAGE_SIZE < 4096
+#error PAGE_SIZE is < 4k
+#endif
+
+static int restore_dsp_rettings(struct echoaudio *chip);
+
+
+/* Some vector commands involve the DSP reading or writing data to and from the
+comm page; if you send one of these commands to the DSP, it will complete the
+command and then write a non-zero value to the Handshake field in the
+comm page.  This function waits for the handshake to show up. */
+static int wait_handshake(struct echoaudio *chip)
+{
+	int i;
+
+	/* Wait up to 10ms for the handshake from the DSP */
+	for (i = 0; i < HANDSHAKE_TIMEOUT; i++) {
+		/* Look for the handshake value */
+		if (chip->comm_page->handshake) {
+			/*if (i)  DE_ACT(("Handshake time: %d\n", i));*/
+			return 0;
+		}
+		udelay(1);
+	}
+
+	snd_printk(KERN_ERR "wait_handshake(): Timeout waiting for DSP\n");
+	return -EBUSY;
+}
+
+
+
+/* Much of the interaction between the DSP and the driver is done via vector
+commands; send_vector writes a vector command to the DSP.  Typically, this
+causes the DSP to read or write fields in the comm page.
+PCI posting is not required thanks to the handshake logic. */
+static int send_vector(struct echoaudio *chip, u32 command)
+{
+	int i;
+
+	wmb();	/* Flush all pending writes before sending the command */
+
+	/* Wait up to 100ms for the "vector busy" bit to be off */
+	for (i = 0; i < VECTOR_BUSY_TIMEOUT; i++) {
+		if (!(get_dsp_register(chip, CHI32_VECTOR_REG) &
+		      CHI32_VECTOR_BUSY)) {
+			set_dsp_register(chip, CHI32_VECTOR_REG, command);
+			/*if (i)  DE_ACT(("send_vector time: %d\n", i));*/
+			return 0;
+		}
+		udelay(1);
+	}
+
+	DE_ACT((KERN_ERR "timeout on send_vector\n"));
+	return -EBUSY;
+}
+
+
+
+/* write_dsp writes a 32-bit value to the DSP; this is used almost
+exclusively for loading the DSP. */
+static int write_dsp(struct echoaudio *chip, u32 data)
+{
+	u32 status, i;
+
+	for (i = 0; i < 10000000; i++) {	/* timeout = 10s */
+		status = get_dsp_register(chip, CHI32_STATUS_REG);
+		if ((status & CHI32_STATUS_HOST_WRITE_EMPTY) != 0) {
+			set_dsp_register(chip, CHI32_DATA_REG, data);
+			wmb();			/* write it immediately */
+			return 0;
+		}
+		udelay(1);
+		cond_resched();
+	}
+
+	chip->bad_board = TRUE;		/* Set TRUE until DSP re-loaded */
+	DE_ACT((KERN_ERR "write_dsp: Set bad_board to TRUE\n"));
+	return -EIO;
+}
+
+
+
+/* read_dsp reads a 32-bit value from the DSP; this is used almost
+exclusively for loading the DSP and checking the status of the ASIC. */
+static int read_dsp(struct echoaudio *chip, u32 *data)
+{
+	u32 status, i;
+
+	for (i = 0; i < READ_DSP_TIMEOUT; i++) {
+		status = get_dsp_register(chip, CHI32_STATUS_REG);
+		if ((status & CHI32_STATUS_HOST_READ_FULL) != 0) {
+			*data = get_dsp_register(chip, CHI32_DATA_REG);
+			return 0;
+		}
+		udelay(1);
+		cond_resched();
+	}
+
+	chip->bad_board = TRUE;		/* Set TRUE until DSP re-loaded */
+	DE_INIT((KERN_ERR "read_dsp: Set bad_board to TRUE\n"));
+	return -EIO;
+}
+
+
+
+/****************************************************************************
+	Firmware loading functions
+ ****************************************************************************/
+
+/* This function is used to read back the serial number from the DSP;
+this is triggered by the SET_COMMPAGE_ADDR command.
+Only some early Echogals products have serial numbers in the ROM;
+the serial number is not used, but you still need to do this as
+part of the DSP load process. */
+static int read_sn(struct echoaudio *chip)
+{
+	int i;
+	u32 sn[6];
+
+	for (i = 0; i < 5; i++) {
+		if (read_dsp(chip, &sn[i])) {
+			snd_printk(KERN_ERR "Failed to read serial number\n");
+			return -EIO;
+		}
+	}
+	DE_INIT(("Read serial number %08x %08x %08x %08x %08x\n",
+		 sn[0], sn[1], sn[2], sn[3], sn[4]));
+	return 0;
+}
+
+
+
+#ifndef ECHOCARD_HAS_ASIC
+/* This card has no ASIC, just return ok */
+static inline int check_asic_status(struct echoaudio *chip)
+{
+	chip->asic_loaded = TRUE;
+	return 0;
+}
+
+#endif /* !ECHOCARD_HAS_ASIC */
+
+
+
+#ifdef ECHOCARD_HAS_ASIC
+
+/* Load ASIC code - done after the DSP is loaded */
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+			     const struct firmware *asic)
+{
+	const struct firmware *fw;
+	int err;
+	u32 i, size;
+	u8 *code;
+
+	if ((err = get_firmware(&fw, asic, chip)) < 0) {
+		snd_printk(KERN_WARNING "Firmware not found !\n");
+		return err;
+	}
+
+	code = (u8 *)fw->data;
+	size = fw->size;
+
+	/* Send the "Here comes the ASIC" command */
+	if (write_dsp(chip, cmd) < 0)
+		goto la_error;
+
+	/* Write length of ASIC file in bytes */
+	if (write_dsp(chip, size) < 0)
+		goto la_error;
+
+	for (i = 0; i < size; i++) {
+		if (write_dsp(chip, code[i]) < 0)
+			goto la_error;
+	}
+
+	DE_INIT(("ASIC loaded\n"));
+	free_firmware(fw);
+	return 0;
+
+la_error:
+	DE_INIT(("failed on write_dsp\n"));
+	free_firmware(fw);
+	return -EIO;
+}
+
+#endif /* ECHOCARD_HAS_ASIC */
+
+
+
+#ifdef DSP_56361
+
+/* Install the resident loader for 56361 DSPs;  The resident loader is on
+the EPROM on the board for 56301 DSP. The resident loader is a tiny little
+program that is used to load the real DSP code. */
+static int install_resident_loader(struct echoaudio *chip)
+{
+	u32 address;
+	int index, words, i;
+	u16 *code;
+	u32 status;
+	const struct firmware *fw;
+
+	/* 56361 cards only!  This check is required by the old 56301-based
+	Mona and Gina24 */
+	if (chip->device_id != DEVICE_ID_56361)
+		return 0;
+
+	/* Look to see if the resident loader is present.  If the resident
+	loader is already installed, host flag 5 will be on. */
+	status = get_dsp_register(chip, CHI32_STATUS_REG);
+	if (status & CHI32_STATUS_REG_HF5) {
+		DE_INIT(("Resident loader already installed; status is 0x%x\n",
+			 status));
+		return 0;
+	}
+
+	if ((i = get_firmware(&fw, &card_fw[FW_361_LOADER], chip)) < 0) {
+		snd_printk(KERN_WARNING "Firmware not found !\n");
+		return i;
+	}
+
+	/* The DSP code is an array of 16 bit words.  The array is divided up
+	into sections.  The first word of each section is the size in words,
+	followed by the section type.
+	Since DSP addresses and data are 24 bits wide, they each take up two
+	16 bit words in the array.
+	This is a lot like the other loader loop, but it's not a loop, you
+	don't write the memory type, and you don't write a zero at the end. */
+
+	/* Set DSP format bits for 24 bit mode */
+	set_dsp_register(chip, CHI32_CONTROL_REG,
+			 get_dsp_register(chip, CHI32_CONTROL_REG) | 0x900);
+
+	code = (u16 *)fw->data;
+
+	/* Skip the header section; the first word in the array is the size
+	of the first section, so the first real section of code is pointed
+	to by Code[0]. */
+	index = code[0];
+
+	/* Skip the section size, LRS block type, and DSP memory type */
+	index += 3;
+
+	/* Get the number of DSP words to write */
+	words = code[index++];
+
+	/* Get the DSP address for this block; 24 bits, so build from two words */
+	address = ((u32)code[index] << 16) + code[index + 1];
+	index += 2;
+
+	/* Write the count to the DSP */
+	if (write_dsp(chip, words)) {
+		DE_INIT(("install_resident_loader: Failed to write word count!\n"));
+		goto irl_error;
+	}
+	/* Write the DSP address */
+	if (write_dsp(chip, address)) {
+		DE_INIT(("install_resident_loader: Failed to write DSP address!\n"));
+		goto irl_error;
+	}
+	/* Write out this block of code to the DSP */
+	for (i = 0; i < words; i++) {
+		u32 data;
+
+		data = ((u32)code[index] << 16) + code[index + 1];
+		if (write_dsp(chip, data)) {
+			DE_INIT(("install_resident_loader: Failed to write DSP code\n"));
+			goto irl_error;
+		}
+		index += 2;
+	}
+
+	/* Wait for flag 5 to come up */
+	for (i = 0; i < 200; i++) {	/* Timeout is 50us * 200 = 10ms */
+		udelay(50);
+		status = get_dsp_register(chip, CHI32_STATUS_REG);
+		if (status & CHI32_STATUS_REG_HF5)
+			break;
+	}
+
+	if (i == 200) {
+		DE_INIT(("Resident loader failed to set HF5\n"));
+		goto irl_error;
+	}
+
+	DE_INIT(("Resident loader successfully installed\n"));
+	free_firmware(fw);
+	return 0;
+
+irl_error:
+	free_firmware(fw);
+	return -EIO;
+}
+
+#endif /* DSP_56361 */
+
+
+static int load_dsp(struct echoaudio *chip, u16 *code)
+{
+	u32 address, data;
+	int index, words, i;
+
+	if (chip->dsp_code == code) {
+		DE_INIT(("DSP is already loaded!\n"));
+		return 0;
+	}
+	chip->bad_board = TRUE;		/* Set TRUE until DSP loaded */
+	chip->dsp_code = NULL;		/* Current DSP code not loaded */
+	chip->asic_loaded = FALSE;	/* Loading the DSP code will reset the ASIC */
+
+	DE_INIT(("load_dsp: Set bad_board to TRUE\n"));
+
+	/* If this board requires a resident loader, install it. */
+#ifdef DSP_56361
+	if ((i = install_resident_loader(chip)) < 0)
+		return i;
+#endif
+
+	/* Send software reset command */
+	if (send_vector(chip, DSP_VC_RESET) < 0) {
+		DE_INIT(("LoadDsp: send_vector DSP_VC_RESET failed, Critical Failure\n"));
+		return -EIO;
+	}
+	/* Delay 10us */
+	udelay(10);
+
+	/* Wait 10ms for HF3 to indicate that software reset is complete */
+	for (i = 0; i < 1000; i++) {	/* Timeout is 10us * 1000 = 10ms */
+		if (get_dsp_register(chip, CHI32_STATUS_REG) &
+		    CHI32_STATUS_REG_HF3)
+			break;
+		udelay(10);
+	}
+
+	if (i == 1000) {
+		DE_INIT(("load_dsp: Timeout waiting for CHI32_STATUS_REG_HF3\n"));
+		return -EIO;
+	}
+
+	/* Set DSP format bits for 24 bit mode now that soft reset is done */
+	set_dsp_register(chip, CHI32_CONTROL_REG,
+			 get_dsp_register(chip, CHI32_CONTROL_REG) | 0x900);
+
+	/* Main loader loop */
+
+	index = code[0];
+	for (;;) {
+		int block_type, mem_type;
+
+		/* Total Block Size */
+		index++;
+
+		/* Block Type */
+		block_type = code[index];
+		if (block_type == 4)	/* We're finished */
+			break;
+
+		index++;
+
+		/* Memory Type  P=0,X=1,Y=2 */
+		mem_type = code[index++];
+
+		/* Block Code Size */
+		words = code[index++];
+		if (words == 0)		/* We're finished */
+			break;
+
+		/* Start Address */
+		address = ((u32)code[index] << 16) + code[index + 1];
+		index += 2;
+
+		if (write_dsp(chip, words) < 0) {
+			DE_INIT(("load_dsp: failed to write number of DSP words\n"));
+			return -EIO;
+		}
+		if (write_dsp(chip, address) < 0) {
+			DE_INIT(("load_dsp: failed to write DSP address\n"));
+			return -EIO;
+		}
+		if (write_dsp(chip, mem_type) < 0) {
+			DE_INIT(("load_dsp: failed to write DSP memory type\n"));
+			return -EIO;
+		}
+		/* Code */
+		for (i = 0; i < words; i++, index+=2) {
+			data = ((u32)code[index] << 16) + code[index + 1];
+			if (write_dsp(chip, data) < 0) {
+				DE_INIT(("load_dsp: failed to write DSP data\n"));
+				return -EIO;
+			}
+		}
+	}
+
+	if (write_dsp(chip, 0) < 0) {	/* We're done!!! */
+		DE_INIT(("load_dsp: Failed to write final zero\n"));
+		return -EIO;
+	}
+	udelay(10);
+
+	for (i = 0; i < 5000; i++) {	/* Timeout is 100us * 5000 = 500ms */
+		/* Wait for flag 4 - indicates that the DSP loaded OK */
+		if (get_dsp_register(chip, CHI32_STATUS_REG) &
+		    CHI32_STATUS_REG_HF4) {
+			set_dsp_register(chip, CHI32_CONTROL_REG,
+					 get_dsp_register(chip, CHI32_CONTROL_REG) & ~0x1b00);
+
+			if (write_dsp(chip, DSP_FNC_SET_COMMPAGE_ADDR) < 0) {
+				DE_INIT(("load_dsp: Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n"));
+				return -EIO;
+			}
+
+			if (write_dsp(chip, chip->comm_page_phys) < 0) {
+				DE_INIT(("load_dsp: Failed to write comm page address\n"));
+				return -EIO;
+			}
+
+			/* Get the serial number via slave mode.
+			This is triggered by the SET_COMMPAGE_ADDR command.
+			We don't actually use the serial number but we have to
+			get it as part of the DSP init voodoo. */
+			if (read_sn(chip) < 0) {
+				DE_INIT(("load_dsp: Failed to read serial number\n"));
+				return -EIO;
+			}
+
+			chip->dsp_code = code;		/* Show which DSP code loaded */
+			chip->bad_board = FALSE;	/* DSP OK */
+			DE_INIT(("load_dsp: OK!\n"));
+			return 0;
+		}
+		udelay(100);
+	}
+
+	DE_INIT(("load_dsp: DSP load timed out waiting for HF4\n"));
+	return -EIO;
+}
+
+
+
+/* load_firmware takes care of loading the DSP and any ASIC code. */
+static int load_firmware(struct echoaudio *chip)
+{
+	const struct firmware *fw;
+	int box_type, err;
+
+	snd_assert(chip->dsp_code_to_load && chip->comm_page, return -EPERM);
+
+	/* See if the ASIC is present and working - only if the DSP is already loaded */
+	if (chip->dsp_code) {
+		if ((box_type = check_asic_status(chip)) >= 0)
+			return box_type;
+		/* ASIC check failed; force the DSP to reload */
+		chip->dsp_code = NULL;
+	}
+
+	if ((err = get_firmware(&fw, chip->dsp_code_to_load, chip)) < 0)
+		return err;
+	err = load_dsp(chip, (u16 *)fw->data);
+	free_firmware(fw);
+	if (err < 0)
+		return err;
+
+	if ((box_type = load_asic(chip)) < 0)
+		return box_type;	/* error */
+
+	if ((err = restore_dsp_rettings(chip)) < 0)
+		return err;
+
+	return box_type;
+}
+
+
+
+/****************************************************************************
+	Mixer functions
+ ****************************************************************************/
+
+#if defined(ECHOCARD_HAS_INPUT_NOMINAL_LEVEL) || \
+	defined(ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL)
+
+/* Set the nominal level for an input or output bus (true = -10dBV, false = +4dBu) */
+static int set_nominal_level(struct echoaudio *chip, u16 index, char consumer)
+{
+	snd_assert(index < num_busses_out(chip) + num_busses_in(chip),
+		   return -EINVAL);
+
+	/* Wait for the handshake (OK even if ASIC is not loaded) */
+	if (wait_handshake(chip))
+		return -EIO;
+
+	chip->nominal_level[index] = consumer;
+
+	if (consumer)
+		chip->comm_page->nominal_level_mask |= cpu_to_le32(1 << index);
+	else
+		chip->comm_page->nominal_level_mask &= ~cpu_to_le32(1 << index);
+
+	return 0;
+}
+
+#endif /* ECHOCARD_HAS_*_NOMINAL_LEVEL */
+
+
+
+/* Set the gain for a single physical output channel (dB). */
+static int set_output_gain(struct echoaudio *chip, u16 channel, s8 gain)
+{
+	snd_assert(channel < num_busses_out(chip), return -EINVAL);
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	/* Save the new value */
+	chip->output_gain[channel] = gain;
+	chip->comm_page->line_out_level[channel] = gain;
+	return 0;
+}
+
+
+
+#ifdef ECHOCARD_HAS_MONITOR
+/* Set the monitor level from an input bus to an output bus. */
+static int set_monitor_gain(struct echoaudio *chip, u16 output, u16 input,
+			    s8 gain)
+{
+	snd_assert(output < num_busses_out(chip) &&
+		   input < num_busses_in(chip), return -EINVAL);
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	chip->monitor_gain[output][input] = gain;
+	chip->comm_page->monitors[monitor_index(chip, output, input)] = gain;
+	return 0;
+}
+#endif /* ECHOCARD_HAS_MONITOR */
+
+
+/* Tell the DSP to read and update output, nominal & monitor levels in comm page. */
+static int update_output_line_level(struct echoaudio *chip)
+{
+	if (wait_handshake(chip))
+		return -EIO;
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_UPDATE_OUTVOL);
+}
+
+
+
+/* Tell the DSP to read and update input levels in comm page */
+static int update_input_line_level(struct echoaudio *chip)
+{
+	if (wait_handshake(chip))
+		return -EIO;
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_UPDATE_INGAIN);
+}
+
+
+
+/* set_meters_on turns the meters on or off.  If meters are turned on, the DSP
+will write the meter and clock detect values to the comm page at about 30Hz */
+static void set_meters_on(struct echoaudio *chip, char on)
+{
+	if (on && !chip->meters_enabled) {
+		send_vector(chip, DSP_VC_METERS_ON);
+		chip->meters_enabled = 1;
+	} else if (!on && chip->meters_enabled) {
+		send_vector(chip, DSP_VC_METERS_OFF);
+		chip->meters_enabled = 0;
+		memset((s8 *)chip->comm_page->vu_meter, ECHOGAIN_MUTED,
+		       DSP_MAXPIPES);
+		memset((s8 *)chip->comm_page->peak_meter, ECHOGAIN_MUTED,
+		       DSP_MAXPIPES);
+	}
+}
+
+
+
+/* Fill out an the given array using the current values in the comm page.
+Meters are written in the comm page by the DSP in this order:
+ Output busses
+ Input busses
+ Output pipes (vmixer cards only)
+
+This function assumes there are no more than 16 in/out busses or pipes
+Meters is an array [3][16][2] of long. */
+static void get_audio_meters(struct echoaudio *chip, long *meters)
+{
+	int i, m, n;
+
+	m = 0;
+	n = 0;
+	for (i = 0; i < num_busses_out(chip); i++, m++) {
+		meters[n++] = chip->comm_page->vu_meter[m];
+		meters[n++] = chip->comm_page->peak_meter[m];
+	}
+	for (; n < 32; n++)
+		meters[n] = 0;
+
+#ifdef ECHOCARD_ECHO3G
+	m = E3G_MAX_OUTPUTS;	/* Skip unused meters */
+#endif
+
+	for (i = 0; i < num_busses_in(chip); i++, m++) {
+		meters[n++] = chip->comm_page->vu_meter[m];
+		meters[n++] = chip->comm_page->peak_meter[m];
+	}
+	for (; n < 64; n++)
+		meters[n] = 0;
+
+#ifdef ECHOCARD_HAS_VMIXER
+	for (i = 0; i < num_pipes_out(chip); i++, m++) {
+		meters[n++] = chip->comm_page->vu_meter[m];
+		meters[n++] = chip->comm_page->peak_meter[m];
+	}
+#endif
+	for (; n < 96; n++)
+		meters[n] = 0;
+}
+
+
+
+static int restore_dsp_rettings(struct echoaudio *chip)
+{
+	int err;
+	DE_INIT(("restore_dsp_settings\n"));
+
+	if ((err = check_asic_status(chip)) < 0)
+		return err;
+
+	/* @ Gina20/Darla20 only. Should be harmless for other cards. */
+	chip->comm_page->gd_clock_state = GD_CLOCK_UNDEF;
+	chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_UNDEF;
+	chip->comm_page->handshake = 0xffffffff;
+
+	if ((err = set_sample_rate(chip, chip->sample_rate)) < 0)
+		return err;
+
+	if (chip->meters_enabled)
+		if (send_vector(chip, DSP_VC_METERS_ON) < 0)
+			return -EIO;
+
+#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
+	if (set_input_clock(chip, chip->input_clock) < 0)
+		return -EIO;
+#endif
+
+#ifdef ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH
+	if (set_output_clock(chip, chip->output_clock) < 0)
+		return -EIO;
+#endif
+
+	if (update_output_line_level(chip) < 0)
+		return -EIO;
+
+	if (update_input_line_level(chip) < 0)
+		return -EIO;
+
+#ifdef ECHOCARD_HAS_VMIXER
+	if (update_vmixer_level(chip) < 0)
+		return -EIO;
+#endif
+
+	if (wait_handshake(chip) < 0)
+		return -EIO;
+	clear_handshake(chip);
+
+	DE_INIT(("restore_dsp_rettings done\n"));
+	return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+/****************************************************************************
+	Transport functions
+ ****************************************************************************/
+
+/* set_audio_format() sets the format of the audio data in host memory for
+this pipe.  Note that _MS_ (mono-to-stereo) playback modes are not used by ALSA
+but they are here because they are just mono while capturing */
+static void set_audio_format(struct echoaudio *chip, u16 pipe_index,
+			     const struct audioformat *format)
+{
+	u16 dsp_format;
+
+	dsp_format = DSP_AUDIOFORM_SS_16LE;
+
+	/* Look for super-interleave (no big-endian and 8 bits) */
+	if (format->interleave > 2) {
+		switch (format->bits_per_sample) {
+		case 16:
+			dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_16LE;
+			break;
+		case 24:
+			dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_24LE;
+			break;
+		case 32:
+			dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_32LE;
+			break;
+		}
+		dsp_format |= format->interleave;
+	} else if (format->data_are_bigendian) {
+		/* For big-endian data, only 32 bit samples are supported */
+		switch (format->interleave) {
+		case 1:
+			dsp_format = DSP_AUDIOFORM_MM_32BE;
+			break;
+#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+		case 2:
+			dsp_format = DSP_AUDIOFORM_SS_32BE;
+			break;
+#endif
+		}
+	} else if (format->interleave == 1 &&
+		   format->bits_per_sample == 32 && !format->mono_to_stereo) {
+		/* 32 bit little-endian mono->mono case */
+		dsp_format = DSP_AUDIOFORM_MM_32LE;
+	} else {
+		/* Handle the other little-endian formats */
+		switch (format->bits_per_sample) {
+		case 8:
+			if (format->interleave == 2)
+				dsp_format = DSP_AUDIOFORM_SS_8;
+			else
+				dsp_format = DSP_AUDIOFORM_MS_8;
+			break;
+		default:
+		case 16:
+			if (format->interleave == 2)
+				dsp_format = DSP_AUDIOFORM_SS_16LE;
+			else
+				dsp_format = DSP_AUDIOFORM_MS_16LE;
+			break;
+		case 24:
+			if (format->interleave == 2)
+				dsp_format = DSP_AUDIOFORM_SS_24LE;
+			else
+				dsp_format = DSP_AUDIOFORM_MS_24LE;
+			break;
+		case 32:
+			if (format->interleave == 2)
+				dsp_format = DSP_AUDIOFORM_SS_32LE;
+			else
+				dsp_format = DSP_AUDIOFORM_MS_32LE;
+			break;
+		}
+	}
+	DE_ACT(("set_audio_format[%d] = %x\n", pipe_index, dsp_format));
+	chip->comm_page->audio_format[pipe_index] = cpu_to_le16(dsp_format);
+}
+
+
+
+/* start_transport starts transport for a set of pipes.
+The bits 1 in channel_mask specify what pipes to start. Only the bit of the
+first channel must be set, regardless its interleave.
+Same thing for pause_ and stop_ -trasport below. */
+static int start_transport(struct echoaudio *chip, u32 channel_mask,
+			   u32 cyclic_mask)
+{
+	DE_ACT(("start_transport %x\n", channel_mask));
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	chip->comm_page->cmd_start |= cpu_to_le32(channel_mask);
+
+	if (chip->comm_page->cmd_start) {
+		clear_handshake(chip);
+		send_vector(chip, DSP_VC_START_TRANSFER);
+		if (wait_handshake(chip))
+			return -EIO;
+		/* Keep track of which pipes are transporting */
+		chip->active_mask |= channel_mask;
+		chip->comm_page->cmd_start = 0;
+		return 0;
+	}
+
+	DE_ACT(("start_transport: No pipes to start!\n"));
+	return -EINVAL;
+}
+
+
+
+static int pause_transport(struct echoaudio *chip, u32 channel_mask)
+{
+	DE_ACT(("pause_transport %x\n", channel_mask));
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	chip->comm_page->cmd_stop |= cpu_to_le32(channel_mask);
+	chip->comm_page->cmd_reset = 0;
+	if (chip->comm_page->cmd_stop) {
+		clear_handshake(chip);
+		send_vector(chip, DSP_VC_STOP_TRANSFER);
+		if (wait_handshake(chip))
+			return -EIO;
+		/* Keep track of which pipes are transporting */
+		chip->active_mask &= ~channel_mask;
+		chip->comm_page->cmd_stop = 0;
+		chip->comm_page->cmd_reset = 0;
+		return 0;
+	}
+
+	DE_ACT(("pause_transport: No pipes to stop!\n"));
+	return 0;
+}
+
+
+
+static int stop_transport(struct echoaudio *chip, u32 channel_mask)
+{
+	DE_ACT(("stop_transport %x\n", channel_mask));
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	chip->comm_page->cmd_stop |= cpu_to_le32(channel_mask);
+	chip->comm_page->cmd_reset |= cpu_to_le32(channel_mask);
+	if (chip->comm_page->cmd_reset) {
+		clear_handshake(chip);
+		send_vector(chip, DSP_VC_STOP_TRANSFER);
+		if (wait_handshake(chip))
+			return -EIO;
+		/* Keep track of which pipes are transporting */
+		chip->active_mask &= ~channel_mask;
+		chip->comm_page->cmd_stop = 0;
+		chip->comm_page->cmd_reset = 0;
+		return 0;
+	}
+
+	DE_ACT(("stop_transport: No pipes to stop!\n"));
+	return 0;
+}
+
+
+
+static inline int is_pipe_allocated(struct echoaudio *chip, u16 pipe_index)
+{
+	return (chip->pipe_alloc_mask & (1 << pipe_index));
+}
+
+
+
+/* Stops everything and turns off the DSP. All pipes should be already
+stopped and unallocated. */
+static int rest_in_peace(struct echoaudio *chip)
+{
+	DE_ACT(("rest_in_peace() open=%x\n", chip->pipe_alloc_mask));
+
+	/* Stops all active pipes (just to be sure) */
+	stop_transport(chip, chip->active_mask);
+
+	set_meters_on(chip, FALSE);
+
+#ifdef ECHOCARD_HAS_MIDI
+	enable_midi_input(chip, FALSE);
+#endif
+
+	/* Go to sleep */
+	if (chip->dsp_code) {
+		/* Make load_firmware do a complete reload */
+		chip->dsp_code = NULL;
+		/* Put the DSP to sleep */
+		return send_vector(chip, DSP_VC_GO_COMATOSE);
+	}
+	return 0;
+}
+
+
+
+/* Fills the comm page with default values */
+static int init_dsp_comm_page(struct echoaudio *chip)
+{
+	/* Check if the compiler added extra padding inside the structure */
+	if (offsetof(struct comm_page, midi_output) != 0xbe0) {
+		DE_INIT(("init_dsp_comm_page() - Invalid struct comm_page structure\n"));
+		return -EPERM;
+	}
+
+	/* Init all the basic stuff */
+	chip->card_name = ECHOCARD_NAME;
+	chip->bad_board = TRUE;	/* Set TRUE until DSP loaded */
+	chip->dsp_code = NULL;	/* Current DSP code not loaded */
+	chip->digital_mode = DIGITAL_MODE_NONE;
+	chip->input_clock = ECHO_CLOCK_INTERNAL;
+	chip->output_clock = ECHO_CLOCK_WORD;
+	chip->asic_loaded = FALSE;
+	memset(chip->comm_page, 0, sizeof(struct comm_page));
+
+	/* Init the comm page */
+	chip->comm_page->comm_size =
+		__constant_cpu_to_le32(sizeof(struct comm_page));
+	chip->comm_page->handshake = 0xffffffff;
+	chip->comm_page->midi_out_free_count =
+		__constant_cpu_to_le32(DSP_MIDI_OUT_FIFO_SIZE);
+	chip->comm_page->sample_rate = __constant_cpu_to_le32(44100);
+	chip->sample_rate = 44100;
+
+	/* Set line levels so we don't blast any inputs on startup */
+	memset(chip->comm_page->monitors, ECHOGAIN_MUTED, MONITOR_ARRAY_SIZE);
+	memset(chip->comm_page->vmixer, ECHOGAIN_MUTED, VMIXER_ARRAY_SIZE);
+
+	return 0;
+}
+
+
+
+/* This function initializes the several volume controls for busses and pipes.
+This MUST be called after the DSP is up and running ! */
+static int init_line_levels(struct echoaudio *chip)
+{
+	int st, i, o;
+
+	DE_INIT(("init_line_levels\n"));
+
+	/* Mute output busses */
+	for (i = 0; i < num_busses_out(chip); i++)
+		if ((st = set_output_gain(chip, i, ECHOGAIN_MUTED)))
+			return st;
+	if ((st = update_output_line_level(chip)))
+		return st;
+
+#ifdef ECHOCARD_HAS_VMIXER
+	/* Mute the Vmixer */
+	for (i = 0; i < num_pipes_out(chip); i++)
+		for (o = 0; o < num_busses_out(chip); o++)
+			if ((st = set_vmixer_gain(chip, o, i, ECHOGAIN_MUTED)))
+				return st;
+	if ((st = update_vmixer_level(chip)))
+		return st;
+#endif /* ECHOCARD_HAS_VMIXER */
+
+#ifdef ECHOCARD_HAS_MONITOR
+	/* Mute the monitor mixer */
+	for (o = 0; o < num_busses_out(chip); o++)
+		for (i = 0; i < num_busses_in(chip); i++)
+			if ((st = set_monitor_gain(chip, o, i, ECHOGAIN_MUTED)))
+				return st;
+	if ((st = update_output_line_level(chip)))
+		return st;
+#endif /* ECHOCARD_HAS_MONITOR */
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+	for (i = 0; i < num_busses_in(chip); i++)
+		if ((st = set_input_gain(chip, i, ECHOGAIN_MUTED)))
+			return st;
+	if ((st = update_input_line_level(chip)))
+		return st;
+#endif /* ECHOCARD_HAS_INPUT_GAIN */
+
+	return 0;
+}
+
+
+
+/* This is low level part of the interrupt handler.
+It returns -1 if the IRQ is not ours, or N>=0 if it is, where N is the number
+of midi data in the input queue. */
+static int service_irq(struct echoaudio *chip)
+{
+	int st;
+
+	/* Read the DSP status register and see if this DSP generated this interrupt */
+	if (get_dsp_register(chip, CHI32_STATUS_REG) & CHI32_STATUS_IRQ) {
+		st = 0;
+#ifdef ECHOCARD_HAS_MIDI
+		/* Get and parse midi data if present */
+		if (chip->comm_page->midi_input[0])	/* The count is at index 0 */
+			st = midi_service_irq(chip);	/* Returns how many midi bytes we received */
+#endif
+		/* Clear the hardware interrupt */
+		chip->comm_page->midi_input[0] = 0;
+		send_vector(chip, DSP_VC_ACK_INT);
+		return st;
+	}
+	return -1;
+}
+
+
+
+
+/******************************************************************************
+	Functions for opening and closing pipes
+ ******************************************************************************/
+
+/* allocate_pipes is used to reserve audio pipes for your exclusive use.
+The call will fail if some pipes are already allocated. */
+static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
+			  int pipe_index, int interleave)
+{
+	int i;
+	u32 channel_mask;
+	char is_cyclic;
+
+	DE_ACT(("allocate_pipes: ch=%d int=%d\n", pipe_index, interleave));
+
+	if (chip->bad_board)
+		return -EIO;
+
+	is_cyclic = 1;	/* This driver uses cyclic buffers only */
+
+	for (channel_mask = i = 0; i < interleave; i++)
+		channel_mask |= 1 << (pipe_index + i);
+	if (chip->pipe_alloc_mask & channel_mask) {
+		DE_ACT(("allocate_pipes: channel already open\n"));
+		return -EAGAIN;
+	}
+
+	chip->comm_page->position[pipe_index] = 0;
+	chip->pipe_alloc_mask |= channel_mask;
+	if (is_cyclic)
+		chip->pipe_cyclic_mask |= channel_mask;
+	pipe->index = pipe_index;
+	pipe->interleave = interleave;
+	pipe->state = PIPE_STATE_STOPPED;
+
+	/* The counter register is where the DSP writes the 32 bit DMA
+	position for a pipe.  The DSP is constantly updating this value as
+	it moves data. The DMA counter is in units of bytes, not samples. */
+	pipe->dma_counter = &chip->comm_page->position[pipe_index];
+	*pipe->dma_counter = 0;
+	DE_ACT(("allocate_pipes: ok\n"));
+	return pipe_index;
+}
+
+
+
+static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe)
+{
+	u32 channel_mask;
+	int i;
+
+	DE_ACT(("free_pipes: Pipe %d\n", pipe->index));
+	snd_assert(is_pipe_allocated(chip, pipe->index), return -EINVAL);
+	snd_assert(pipe->state == PIPE_STATE_STOPPED, return -EINVAL);
+
+	for (channel_mask = i = 0; i < pipe->interleave; i++)
+		channel_mask |= 1 << (pipe->index + i);
+
+	chip->pipe_alloc_mask &= ~channel_mask;
+	chip->pipe_cyclic_mask &= ~channel_mask;
+	return 0;
+}
+
+
+
+/******************************************************************************
+	Functions for managing the scatter-gather list
+******************************************************************************/
+
+static int sglist_init(struct echoaudio *chip, struct audiopipe *pipe)
+{
+	pipe->sglist_head = 0;
+	memset(pipe->sgpage.area, 0, PAGE_SIZE);
+	chip->comm_page->sglist_addr[pipe->index].addr =
+		cpu_to_le32(pipe->sgpage.addr);
+	return 0;
+}
+
+
+
+static int sglist_add_mapping(struct echoaudio *chip, struct audiopipe *pipe,
+				dma_addr_t address, size_t length)
+{
+	int head = pipe->sglist_head;
+	struct sg_entry *list = (struct sg_entry *)pipe->sgpage.area;
+
+	if (head < MAX_SGLIST_ENTRIES - 1) {
+		list[head].addr = cpu_to_le32(address);
+		list[head].size = cpu_to_le32(length);
+		pipe->sglist_head++;
+	} else {
+		DE_ACT(("SGlist: too many fragments\n"));
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+
+
+static inline int sglist_add_irq(struct echoaudio *chip, struct audiopipe *pipe)
+{
+	return sglist_add_mapping(chip, pipe, 0, 0);
+}
+
+
+
+static inline int sglist_wrap(struct echoaudio *chip, struct audiopipe *pipe)
+{
+	return sglist_add_mapping(chip, pipe, pipe->sgpage.addr, 0);
+}
diff --git a/sound/pci/echoaudio/echoaudio_dsp.h b/sound/pci/echoaudio/echoaudio_dsp.h
new file mode 100644
index 000000000000..e55ee00991ac
--- /dev/null
+++ b/sound/pci/echoaudio/echoaudio_dsp.h
@@ -0,0 +1,694 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+#ifndef _ECHO_DSP_
+#define _ECHO_DSP_
+
+
+/**** Echogals: Darla20, Gina20, Layla20, and Darla24 ****/
+#if defined(ECHOGALS_FAMILY)
+
+#define NUM_ASIC_TESTS		5
+#define READ_DSP_TIMEOUT	1000000L	/* one second */
+
+/**** Echo24: Gina24, Layla24, Mona, Mia, Mia-midi ****/
+#elif defined(ECHO24_FAMILY)
+
+#define DSP_56361			/* Some Echo24 cards use the 56361 DSP */
+#define READ_DSP_TIMEOUT	100000L		/* .1 second */
+
+/**** 3G: Gina3G, Layla3G ****/
+#elif defined(ECHO3G_FAMILY)
+
+#define DSP_56361
+#define READ_DSP_TIMEOUT 	100000L		/* .1 second */
+#define MIN_MTC_1X_RATE		32000
+
+/**** Indigo: Indigo, Indigo IO, Indigo DJ ****/
+#elif defined(INDIGO_FAMILY)
+
+#define DSP_56361
+#define READ_DSP_TIMEOUT	100000L		/* .1 second */
+
+#else
+
+#error No family is defined
+
+#endif
+
+
+
+/*
+ *
+ *  Max inputs and outputs
+ *
+ */
+
+#define DSP_MAXAUDIOINPUTS		16	/* Max audio input channels */
+#define DSP_MAXAUDIOOUTPUTS		16	/* Max audio output channels */
+#define DSP_MAXPIPES			32	/* Max total pipes (input + output) */
+
+
+/*
+ *
+ * These are the offsets for the memory-mapped DSP registers; the DSP base
+ * address is treated as the start of a u32 array.
+ */
+
+#define CHI32_CONTROL_REG		4
+#define CHI32_STATUS_REG		5
+#define CHI32_VECTOR_REG		6
+#define CHI32_DATA_REG			7
+
+
+/*
+ *
+ * Interesting bits within the DSP registers
+ *
+ */
+
+#define CHI32_VECTOR_BUSY		0x00000001
+#define CHI32_STATUS_REG_HF3		0x00000008
+#define CHI32_STATUS_REG_HF4		0x00000010
+#define CHI32_STATUS_REG_HF5		0x00000020
+#define CHI32_STATUS_HOST_READ_FULL	0x00000004
+#define CHI32_STATUS_HOST_WRITE_EMPTY	0x00000002
+#define CHI32_STATUS_IRQ		0x00000040
+
+
+/* 
+ *
+ * DSP commands sent via slave mode; these are sent to the DSP by write_dsp()
+ *
+ */
+
+#define DSP_FNC_SET_COMMPAGE_ADDR		0x02
+#define DSP_FNC_LOAD_LAYLA_ASIC			0xa0
+#define DSP_FNC_LOAD_GINA24_ASIC		0xa0
+#define DSP_FNC_LOAD_MONA_PCI_CARD_ASIC		0xa0
+#define DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC	0xa0
+#define DSP_FNC_LOAD_MONA_EXTERNAL_ASIC		0xa1
+#define DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC	0xa1
+#define DSP_FNC_LOAD_3G_ASIC			0xa0
+
+
+/*
+ *
+ * Defines to handle the MIDI input state engine; these are used to properly
+ * extract MIDI time code bytes and their timestamps from the MIDI input stream.
+ *
+ */
+
+#define MIDI_IN_STATE_NORMAL	0
+#define MIDI_IN_STATE_TS_HIGH	1
+#define MIDI_IN_STATE_TS_LOW	2
+#define MIDI_IN_STATE_F1_DATA 	3
+#define MIDI_IN_SKIP_DATA	(-1)
+
+
+/*----------------------------------------------------------------------------
+
+Setting the sample rates on Layla24 is somewhat schizophrenic.
+
+For standard rates, it works exactly like Mona and Gina24.  That is, for
+8, 11.025, 16, 22.05, 32, 44.1, 48, 88.2, and 96 kHz, you just set the
+appropriate bits in the control register and write the control register.
+
+In order to support MIDI time code sync (and possibly SMPTE LTC sync in
+the future), Layla24 also has "continuous sample rate mode".  In this mode,
+Layla24 can generate any sample rate between 25 and 50 kHz inclusive, or
+50 to 100 kHz inclusive for double speed mode.
+
+To use continuous mode:
+
+-Set the clock select bits in the control register to 0xe (see the #define
+ below)
+
+-Set double-speed mode if you want to use sample rates above 50 kHz
+
+-Write the control register as you would normally
+
+-Now, you need to set the frequency register. First, you need to determine the
+ value for the frequency register.  This is given by the following formula:
+
+frequency_reg = (LAYLA24_MAGIC_NUMBER / sample_rate) - 2
+
+Note the #define below for the magic number
+
+-Wait for the DSP handshake
+-Write the frequency_reg value to the .SampleRate field of the comm page
+-Send the vector command SET_LAYLA24_FREQUENCY_REG (see vmonkey.h)
+
+Once you have set the control register up for continuous mode, you can just
+write the frequency register to change the sample rate.  This could be
+used for MIDI time code sync. For MTC sync, the control register is set for
+continuous mode.  The driver then just keeps writing the
+SET_LAYLA24_FREQUENCY_REG command.
+
+-----------------------------------------------------------------------------*/
+
+#define LAYLA24_MAGIC_NUMBER			677376000
+#define LAYLA24_CONTINUOUS_CLOCK		0x000e
+
+
+/*
+ *
+ * DSP vector commands
+ *
+ */
+
+#define DSP_VC_RESET				0x80ff
+
+#ifndef DSP_56361
+
+#define DSP_VC_ACK_INT				0x8073
+#define DSP_VC_SET_VMIXER_GAIN			0x0000	/* Not used, only for compile */
+#define DSP_VC_START_TRANSFER			0x0075	/* Handshke rqd. */
+#define DSP_VC_METERS_ON			0x0079
+#define DSP_VC_METERS_OFF			0x007b
+#define DSP_VC_UPDATE_OUTVOL			0x007d	/* Handshke rqd. */
+#define DSP_VC_UPDATE_INGAIN			0x007f	/* Handshke rqd. */
+#define DSP_VC_ADD_AUDIO_BUFFER			0x0081	/* Handshke rqd. */
+#define DSP_VC_TEST_ASIC			0x00eb
+#define DSP_VC_UPDATE_CLOCKS			0x00ef	/* Handshke rqd. */
+#define DSP_VC_SET_LAYLA_SAMPLE_RATE		0x00f1	/* Handshke rqd. */
+#define DSP_VC_SET_GD_AUDIO_STATE		0x00f1	/* Handshke rqd. */
+#define DSP_VC_WRITE_CONTROL_REG		0x00f1	/* Handshke rqd. */
+#define DSP_VC_MIDI_WRITE			0x00f5	/* Handshke rqd. */
+#define DSP_VC_STOP_TRANSFER			0x00f7	/* Handshke rqd. */
+#define DSP_VC_UPDATE_FLAGS			0x00fd	/* Handshke rqd. */
+#define DSP_VC_GO_COMATOSE			0x00f9
+
+#else /* !DSP_56361 */
+
+/* Vector commands for families that use either the 56301 or 56361 */
+#define DSP_VC_ACK_INT				0x80F5
+#define DSP_VC_SET_VMIXER_GAIN			0x00DB	/* Handshke rqd. */
+#define DSP_VC_START_TRANSFER			0x00DD	/* Handshke rqd. */
+#define DSP_VC_METERS_ON			0x00EF
+#define DSP_VC_METERS_OFF			0x00F1
+#define DSP_VC_UPDATE_OUTVOL			0x00E3	/* Handshke rqd. */
+#define DSP_VC_UPDATE_INGAIN			0x00E5	/* Handshke rqd. */
+#define DSP_VC_ADD_AUDIO_BUFFER			0x00E1	/* Handshke rqd. */
+#define DSP_VC_TEST_ASIC			0x00ED
+#define DSP_VC_UPDATE_CLOCKS			0x00E9	/* Handshke rqd. */
+#define DSP_VC_SET_LAYLA24_FREQUENCY_REG	0x00E9	/* Handshke rqd. */
+#define DSP_VC_SET_LAYLA_SAMPLE_RATE		0x00EB	/* Handshke rqd. */
+#define DSP_VC_SET_GD_AUDIO_STATE		0x00EB	/* Handshke rqd. */
+#define DSP_VC_WRITE_CONTROL_REG		0x00EB	/* Handshke rqd. */
+#define DSP_VC_MIDI_WRITE			0x00E7	/* Handshke rqd. */
+#define DSP_VC_STOP_TRANSFER			0x00DF	/* Handshke rqd. */
+#define DSP_VC_UPDATE_FLAGS			0x00FB	/* Handshke rqd. */
+#define DSP_VC_GO_COMATOSE			0x00d9
+
+#endif /* !DSP_56361 */
+
+
+/*
+ *
+ * Timeouts
+ *
+ */
+
+#define HANDSHAKE_TIMEOUT		20000	/* send_vector command timeout (20ms) */
+#define VECTOR_BUSY_TIMEOUT		100000	/* 100ms */
+#define MIDI_OUT_DELAY_USEC		2000	/* How long to wait after MIDI fills up */
+
+
+/*
+ *
+ * Flags for .Flags field in the comm page
+ *
+ */
+
+#define DSP_FLAG_MIDI_INPUT		0x0001	/* Enable MIDI input */
+#define DSP_FLAG_SPDIF_NONAUDIO		0x0002	/* Sets the "non-audio" bit
+						 * in the S/PDIF out status
+						 * bits.  Clear this flag for
+						 * audio data;
+						 * set it for AC3 or WMA or
+						 * some such */
+#define DSP_FLAG_PROFESSIONAL_SPDIF	0x0008	/* 1 Professional, 0 Consumer */
+
+
+/*
+ *
+ * Clock detect bits reported by the DSP for Gina20, Layla20, Darla24, and Mia
+ *
+ */
+
+#define GLDM_CLOCK_DETECT_BIT_WORD	0x0002
+#define GLDM_CLOCK_DETECT_BIT_SUPER	0x0004
+#define GLDM_CLOCK_DETECT_BIT_SPDIF	0x0008
+#define GLDM_CLOCK_DETECT_BIT_ESYNC	0x0010
+
+
+/*
+ *
+ * Clock detect bits reported by the DSP for Gina24, Mona, and Layla24
+ *
+ */
+
+#define GML_CLOCK_DETECT_BIT_WORD96	0x0002
+#define GML_CLOCK_DETECT_BIT_WORD48	0x0004
+#define GML_CLOCK_DETECT_BIT_SPDIF48	0x0008
+#define GML_CLOCK_DETECT_BIT_SPDIF96	0x0010
+#define GML_CLOCK_DETECT_BIT_WORD	(GML_CLOCK_DETECT_BIT_WORD96 | GML_CLOCK_DETECT_BIT_WORD48)
+#define GML_CLOCK_DETECT_BIT_SPDIF	(GML_CLOCK_DETECT_BIT_SPDIF48 | GML_CLOCK_DETECT_BIT_SPDIF96)
+#define GML_CLOCK_DETECT_BIT_ESYNC	0x0020
+#define GML_CLOCK_DETECT_BIT_ADAT	0x0040
+
+
+/*
+ *
+ * Layla clock numbers to send to DSP
+ *
+ */
+
+#define LAYLA20_CLOCK_INTERNAL		0
+#define LAYLA20_CLOCK_SPDIF		1
+#define LAYLA20_CLOCK_WORD		2
+#define LAYLA20_CLOCK_SUPER		3
+
+
+/*
+ *
+ * Gina/Darla clock states
+ *
+ */
+
+#define GD_CLOCK_NOCHANGE		0
+#define GD_CLOCK_44			1
+#define GD_CLOCK_48			2
+#define GD_CLOCK_SPDIFIN		3
+#define GD_CLOCK_UNDEF			0xff
+
+
+/*
+ *
+ * Gina/Darla S/PDIF status bits
+ *
+ */
+
+#define GD_SPDIF_STATUS_NOCHANGE	0
+#define GD_SPDIF_STATUS_44		1
+#define GD_SPDIF_STATUS_48		2
+#define GD_SPDIF_STATUS_UNDEF		0xff
+
+
+/*
+ *
+ * Layla20 output clocks
+ *
+ */
+
+#define LAYLA20_OUTPUT_CLOCK_SUPER	0
+#define LAYLA20_OUTPUT_CLOCK_WORD	1
+
+
+/****************************************************************************
+
+   Magic constants for the Darla24 hardware
+
+ ****************************************************************************/
+
+#define GD24_96000	0x0
+#define GD24_48000	0x1
+#define GD24_44100	0x2
+#define GD24_32000	0x3
+#define GD24_22050	0x4
+#define GD24_16000	0x5
+#define GD24_11025	0x6
+#define GD24_8000	0x7
+#define GD24_88200	0x8
+#define GD24_EXT_SYNC	0x9
+
+
+/*
+ *
+ * Return values from the DSP when ASIC is loaded
+ *
+ */
+
+#define ASIC_ALREADY_LOADED	0x1
+#define ASIC_NOT_LOADED		0x0
+
+
+/*
+ *
+ * DSP Audio formats
+ *
+ * These are the audio formats that the DSP can transfer
+ * via input and output pipes.  LE means little-endian,
+ * BE means big-endian.
+ *
+ * DSP_AUDIOFORM_MS_8   
+ *
+ *    8-bit mono unsigned samples.  For playback,
+ *    mono data is duplicated out the left and right channels
+ *    of the output bus.  The "MS" part of the name
+ *    means mono->stereo.
+ *
+ * DSP_AUDIOFORM_MS_16LE
+ *
+ *    16-bit signed little-endian mono samples.  Playback works
+ *    like the previous code.
+ *
+ * DSP_AUDIOFORM_MS_24LE
+ *
+ *    24-bit signed little-endian mono samples.  Data is packed
+ *    three bytes per sample; if you had two samples 0x112233 and 0x445566
+ *    they would be stored in memory like this: 33 22 11 66 55 44.
+ *
+ * DSP_AUDIOFORM_MS_32LE
+ * 
+ *    24-bit signed little-endian mono samples in a 32-bit 
+ *    container.  In other words, each sample is a 32-bit signed 
+ *    integer, where the actual audio data is left-justified 
+ *    in the 32 bits and only the 24 most significant bits are valid.
+ *
+ * DSP_AUDIOFORM_SS_8
+ * DSP_AUDIOFORM_SS_16LE
+ * DSP_AUDIOFORM_SS_24LE
+ * DSP_AUDIOFORM_SS_32LE
+ *
+ *    Like the previous ones, except now with stereo interleaved
+ *    data.  "SS" means stereo->stereo.
+ *
+ * DSP_AUDIOFORM_MM_32LE
+ *
+ *    Similar to DSP_AUDIOFORM_MS_32LE, except that the mono
+ *    data is not duplicated out both the left and right outputs.
+ *    This mode is used by the ASIO driver.  Here, "MM" means
+ *    mono->mono.
+ *
+ * DSP_AUDIOFORM_MM_32BE
+ *
+ *    Just like DSP_AUDIOFORM_MM_32LE, but now the data is
+ *    in big-endian format.
+ *
+ */
+
+#define DSP_AUDIOFORM_MS_8	0	/* 8 bit mono */
+#define DSP_AUDIOFORM_MS_16LE	1	/* 16 bit mono */
+#define DSP_AUDIOFORM_MS_24LE	2	/* 24 bit mono */
+#define DSP_AUDIOFORM_MS_32LE	3	/* 32 bit mono */
+#define DSP_AUDIOFORM_SS_8	4	/* 8 bit stereo */
+#define DSP_AUDIOFORM_SS_16LE	5	/* 16 bit stereo */
+#define DSP_AUDIOFORM_SS_24LE	6	/* 24 bit stereo */
+#define DSP_AUDIOFORM_SS_32LE	7	/* 32 bit stereo */
+#define DSP_AUDIOFORM_MM_32LE	8	/* 32 bit mono->mono little-endian */
+#define DSP_AUDIOFORM_MM_32BE	9	/* 32 bit mono->mono big-endian */
+#define DSP_AUDIOFORM_SS_32BE	10	/* 32 bit stereo big endian */
+#define DSP_AUDIOFORM_INVALID	0xFF	/* Invalid audio format */
+
+
+/*
+ *
+ * Super-interleave is defined as interleaving by 4 or more.  Darla20 and Gina20
+ * do not support super interleave.
+ *
+ * 16 bit, 24 bit, and 32 bit little endian samples are supported for super 
+ * interleave.  The interleave factor must be even.  16 - way interleave is the 
+ * current maximum, so you can interleave by 4, 6, 8, 10, 12, 14, and 16.
+ *
+ * The actual format code is derived by taking the define below and or-ing with
+ * the interleave factor.  So, 32 bit interleave by 6 is 0x86 and
+ * 16 bit interleave by 16 is (0x40 | 0x10) = 0x50.
+ *
+ */
+
+#define DSP_AUDIOFORM_SUPER_INTERLEAVE_16LE	0x40
+#define DSP_AUDIOFORM_SUPER_INTERLEAVE_24LE	0xc0
+#define DSP_AUDIOFORM_SUPER_INTERLEAVE_32LE	0x80
+
+
+/*
+ *
+ * Gina24, Mona, and Layla24 control register defines
+ *
+ */
+
+#define GML_CONVERTER_ENABLE	0x0010
+#define GML_SPDIF_PRO_MODE	0x0020	/* Professional S/PDIF == 1,
+					   consumer == 0 */
+#define GML_SPDIF_SAMPLE_RATE0	0x0040
+#define GML_SPDIF_SAMPLE_RATE1	0x0080
+#define GML_SPDIF_TWO_CHANNEL	0x0100	/* 1 == two channels,
+					   0 == one channel */
+#define GML_SPDIF_NOT_AUDIO	0x0200
+#define GML_SPDIF_COPY_PERMIT	0x0400
+#define GML_SPDIF_24_BIT	0x0800	/* 1 == 24 bit, 0 == 20 bit */
+#define GML_ADAT_MODE		0x1000	/* 1 == ADAT mode, 0 == S/PDIF mode */
+#define GML_SPDIF_OPTICAL_MODE	0x2000	/* 1 == optical mode, 0 == RCA mode */
+#define GML_SPDIF_CDROM_MODE	0x3000	/* 1 == CDROM mode,
+					 * 0 == RCA or optical mode */
+#define GML_DOUBLE_SPEED_MODE	0x4000	/* 1 == double speed,
+					   0 == single speed */
+
+#define GML_DIGITAL_IN_AUTO_MUTE 0x800000
+
+#define GML_96KHZ		(0x0 | GML_DOUBLE_SPEED_MODE)
+#define GML_88KHZ		(0x1 | GML_DOUBLE_SPEED_MODE)
+#define GML_48KHZ		0x2
+#define GML_44KHZ		0x3
+#define GML_32KHZ		0x4
+#define GML_22KHZ		0x5
+#define GML_16KHZ		0x6
+#define GML_11KHZ		0x7
+#define GML_8KHZ		0x8
+#define GML_SPDIF_CLOCK		0x9
+#define GML_ADAT_CLOCK		0xA
+#define GML_WORD_CLOCK		0xB
+#define GML_ESYNC_CLOCK		0xC
+#define GML_ESYNCx2_CLOCK	0xD
+
+#define GML_CLOCK_CLEAR_MASK		0xffffbff0
+#define GML_SPDIF_RATE_CLEAR_MASK	(~(GML_SPDIF_SAMPLE_RATE0|GML_SPDIF_SAMPLE_RATE1))
+#define GML_DIGITAL_MODE_CLEAR_MASK	0xffffcfff
+#define GML_SPDIF_FORMAT_CLEAR_MASK	0xfffff01f
+
+
+/*
+ *
+ * Mia sample rate and clock setting constants
+ *
+ */
+
+#define MIA_32000	0x0040
+#define MIA_44100	0x0042
+#define MIA_48000	0x0041
+#define MIA_88200	0x0142
+#define MIA_96000	0x0141
+
+#define MIA_SPDIF	0x00000044
+#define MIA_SPDIF96	0x00000144
+
+#define MIA_MIDI_REV	1	/* Must be Mia rev 1 for MIDI support */
+
+
+/*
+ *
+ * 3G register bits
+ *
+ */
+
+#define E3G_CONVERTER_ENABLE	0x0010
+#define E3G_SPDIF_PRO_MODE	0x0020	/* Professional S/PDIF == 1,
+					   consumer == 0 */
+#define E3G_SPDIF_SAMPLE_RATE0	0x0040
+#define E3G_SPDIF_SAMPLE_RATE1	0x0080
+#define E3G_SPDIF_TWO_CHANNEL	0x0100	/* 1 == two channels,
+					   0 == one channel */
+#define E3G_SPDIF_NOT_AUDIO	0x0200
+#define E3G_SPDIF_COPY_PERMIT	0x0400
+#define E3G_SPDIF_24_BIT	0x0800	/* 1 == 24 bit, 0 == 20 bit */
+#define E3G_DOUBLE_SPEED_MODE	0x4000	/* 1 == double speed,
+					   0 == single speed */
+#define E3G_PHANTOM_POWER	0x8000	/* 1 == phantom power on,
+					   0 == phantom power off */
+
+#define E3G_96KHZ		(0x0 | E3G_DOUBLE_SPEED_MODE)
+#define E3G_88KHZ		(0x1 | E3G_DOUBLE_SPEED_MODE)
+#define E3G_48KHZ		0x2
+#define E3G_44KHZ		0x3
+#define E3G_32KHZ		0x4
+#define E3G_22KHZ		0x5
+#define E3G_16KHZ		0x6
+#define E3G_11KHZ		0x7
+#define E3G_8KHZ		0x8
+#define E3G_SPDIF_CLOCK		0x9
+#define E3G_ADAT_CLOCK		0xA
+#define E3G_WORD_CLOCK		0xB
+#define E3G_CONTINUOUS_CLOCK	0xE
+
+#define E3G_ADAT_MODE		0x1000
+#define E3G_SPDIF_OPTICAL_MODE	0x2000
+
+#define E3G_CLOCK_CLEAR_MASK		0xbfffbff0
+#define E3G_DIGITAL_MODE_CLEAR_MASK	0xffffcfff
+#define E3G_SPDIF_FORMAT_CLEAR_MASK	0xfffff01f
+
+/* Clock detect bits reported by the DSP */
+#define E3G_CLOCK_DETECT_BIT_WORD96	0x0001
+#define E3G_CLOCK_DETECT_BIT_WORD48	0x0002
+#define E3G_CLOCK_DETECT_BIT_SPDIF48	0x0004
+#define E3G_CLOCK_DETECT_BIT_ADAT	0x0004
+#define E3G_CLOCK_DETECT_BIT_SPDIF96	0x0008
+#define E3G_CLOCK_DETECT_BIT_WORD	(E3G_CLOCK_DETECT_BIT_WORD96|E3G_CLOCK_DETECT_BIT_WORD48)
+#define E3G_CLOCK_DETECT_BIT_SPDIF	(E3G_CLOCK_DETECT_BIT_SPDIF48|E3G_CLOCK_DETECT_BIT_SPDIF96)
+
+/* Frequency control register */
+#define E3G_MAGIC_NUMBER		677376000
+#define E3G_FREQ_REG_DEFAULT		(E3G_MAGIC_NUMBER / 48000 - 2)
+#define E3G_FREQ_REG_MAX		0xffff
+
+/* 3G external box types */
+#define E3G_GINA3G_BOX_TYPE		0x00
+#define E3G_LAYLA3G_BOX_TYPE		0x10
+#define E3G_ASIC_NOT_LOADED		0xffff
+#define E3G_BOX_TYPE_MASK		0xf0
+
+#define EXT_3GBOX_NC			0x01
+#define EXT_3GBOX_NOT_SET		0x02
+
+
+/*
+ *
+ * Gina20 & Layla20 have input gain controls for the analog inputs;
+ * this is the magic number for the hardware that gives you 0 dB at -10.
+ *
+ */
+
+#define GL20_INPUT_GAIN_MAGIC_NUMBER	0xC8
+
+
+/*
+ *
+ * Defines how much time must pass between DSP load attempts
+ *
+ */
+
+#define DSP_LOAD_ATTEMPT_PERIOD		1000000L	/* One second */
+
+
+/*
+ *
+ * Size of arrays for the comm page.  MAX_PLAY_TAPS and MAX_REC_TAPS are
+ * no longer used, but the sizes must still be right for the DSP to see
+ * the comm page correctly.
+ *
+ */
+
+#define MONITOR_ARRAY_SIZE	0x180
+#define VMIXER_ARRAY_SIZE	0x40
+#define MIDI_OUT_BUFFER_SIZE	32
+#define MIDI_IN_BUFFER_SIZE	256
+#define MAX_PLAY_TAPS		168
+#define MAX_REC_TAPS		192
+#define DSP_MIDI_OUT_FIFO_SIZE	64
+
+
+/* sg_entry is a single entry for the scatter-gather list.  The array of struct
+sg_entry struct is read by the DSP, so all values must be little-endian. */
+
+#define MAX_SGLIST_ENTRIES 512
+
+struct sg_entry {
+	u32 addr;
+	u32 size;
+};
+
+
+/****************************************************************************
+
+  The comm page.  This structure is read and written by the DSP; the
+  DSP code is a firm believer in the byte offsets written in the comments
+  at the end of each line.  This structure should not be changed.
+
+  Any reads from or writes to this structure should be in little-endian format.
+
+ ****************************************************************************/
+
+struct comm_page {		/*				Base	Length*/
+	u32 comm_size;		/* size of this object		0x000	4 */
+	u32 flags;		/* See Appendix A below		0x004	4 */
+	u32 unused;		/* Unused entry			0x008	4 */
+	u32 sample_rate;	/* Card sample rate in Hz	0x00c	4 */
+	volatile u32 handshake;	/* DSP command handshake	0x010	4 */
+	u32 cmd_start;		/* Chs. to start mask		0x014	4 */
+	u32 cmd_stop;		/* Chs. to stop mask		0x018	4 */
+	u32 cmd_reset;		/* Chs. to reset mask		0x01c	4 */
+	u16 audio_format[DSP_MAXPIPES];	/* Chs. audio format	0x020	32*2 */
+	struct sg_entry sglist_addr[DSP_MAXPIPES];
+				/* Chs. Physical sglist addrs	0x060	32*8 */
+	volatile u32 position[DSP_MAXPIPES];
+				/* Positions for ea. ch.	0x160	32*4 */
+	volatile s8 vu_meter[DSP_MAXPIPES];
+				/* VU meters			0x1e0	32*1 */
+	volatile s8 peak_meter[DSP_MAXPIPES];
+				/* Peak meters			0x200	32*1 */
+	s8 line_out_level[DSP_MAXAUDIOOUTPUTS];
+				/* Output gain			0x220	16*1 */
+	s8 line_in_level[DSP_MAXAUDIOINPUTS];
+				/* Input gain			0x230	16*1 */
+	s8 monitors[MONITOR_ARRAY_SIZE];
+				/* Monitor map			0x240	0x180 */
+	u32 play_coeff[MAX_PLAY_TAPS];
+			/* Gina/Darla play filters - obsolete	0x3c0	168*4 */
+	u32 rec_coeff[MAX_REC_TAPS];
+			/* Gina/Darla record filters - obsolete	0x660	192*4 */
+	volatile u16 midi_input[MIDI_IN_BUFFER_SIZE];
+			/* MIDI input data transfer buffer	0x960	256*2 */
+	u8 gd_clock_state;	/* Chg Gina/Darla clock state	0xb60	1 */
+	u8 gd_spdif_status;	/* Chg. Gina/Darla S/PDIF state	0xb61	1 */
+	u8 gd_resampler_state;	/* Should always be 3		0xb62	1 */
+	u8 filler2;		/*				0xb63	1 */
+	u32 nominal_level_mask;	/* -10 level enable mask	0xb64	4 */
+	u16 input_clock;	/* Chg. Input clock state	0xb68	2 */
+	u16 output_clock;	/* Chg. Output clock state	0xb6a	2 */
+	volatile u32 status_clocks;
+				/* Current Input clock state	0xb6c	4 */
+	u32 ext_box_status;	/* External box status		0xb70	4 */
+	u32 cmd_add_buffer;	/* Pipes to add (obsolete)	0xb74	4 */
+	volatile u32 midi_out_free_count;
+			/* # of bytes free in MIDI output FIFO	0xb78	4 */
+	u32 unused2;		/* Cyclic pipes			0xb7c	4 */
+	u32 control_register;
+			/* Mona, Gina24, Layla24, 3G ctrl reg	0xb80	4 */
+	u32 e3g_frq_register;	/* 3G frequency register	0xb84	4 */
+	u8 filler[24];		/* filler			0xb88	24*1 */
+	s8 vmixer[VMIXER_ARRAY_SIZE];
+				/* Vmixer levels		0xba0	64*1 */
+	u8 midi_output[MIDI_OUT_BUFFER_SIZE];
+				/* MIDI output data		0xbe0	32*1 */
+};
+
+#endif /* _ECHO_DSP_ */
diff --git a/sound/pci/echoaudio/echoaudio_gml.c b/sound/pci/echoaudio/echoaudio_gml.c
new file mode 100644
index 000000000000..3aa37e76ebab
--- /dev/null
+++ b/sound/pci/echoaudio/echoaudio_gml.c
@@ -0,0 +1,198 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+/* These functions are common for Gina24, Layla24 and Mona cards */
+
+
+/* ASIC status check - some cards have one or two ASICs that need to be
+loaded.  Once that load is complete, this function is called to see if
+the load was successful.
+If this load fails, it does not necessarily mean that the hardware is
+defective - the external box may be disconnected or turned off. */
+static int check_asic_status(struct echoaudio *chip)
+{
+	u32 asic_status;
+
+	send_vector(chip, DSP_VC_TEST_ASIC);
+
+	/* The DSP will return a value to indicate whether or not the
+	   ASIC is currently loaded */
+	if (read_dsp(chip, &asic_status) < 0) {
+		DE_INIT(("check_asic_status: failed on read_dsp\n"));
+		chip->asic_loaded = FALSE;
+		return -EIO;
+	}
+
+	chip->asic_loaded = (asic_status == ASIC_ALREADY_LOADED);
+	return chip->asic_loaded ? 0 : -EIO;
+}
+
+
+
+/* Most configuration of Gina24, Layla24, or Mona is accomplished by writing
+the control register.  write_control_reg sends the new control register
+value to the DSP. */
+static int write_control_reg(struct echoaudio *chip, u32 value, char force)
+{
+	/* Handle the digital input auto-mute */
+	if (chip->digital_in_automute)
+		value |= GML_DIGITAL_IN_AUTO_MUTE;
+	else
+		value &= ~GML_DIGITAL_IN_AUTO_MUTE;
+
+	DE_ACT(("write_control_reg: 0x%x\n", value));
+
+	/* Write the control register */
+	value = cpu_to_le32(value);
+	if (value != chip->comm_page->control_register || force) {
+		if (wait_handshake(chip))
+			return -EIO;
+		chip->comm_page->control_register = value;
+		clear_handshake(chip);
+		return send_vector(chip, DSP_VC_WRITE_CONTROL_REG);
+	}
+	return 0;
+}
+
+
+
+/* Gina24, Layla24, and Mona support digital input auto-mute.  If the digital
+input auto-mute is enabled, the DSP will only enable the digital inputs if
+the card is syncing to a valid clock on the ADAT or S/PDIF inputs.
+If the auto-mute is disabled, the digital inputs are enabled regardless of
+what the input clock is set or what is connected. */
+static int set_input_auto_mute(struct echoaudio *chip, int automute)
+{
+	DE_ACT(("set_input_auto_mute %d\n", automute));
+
+	chip->digital_in_automute = automute;
+
+	/* Re-set the input clock to the current value - indirectly causes
+	the auto-mute flag to be sent to the DSP */
+	return set_input_clock(chip, chip->input_clock);
+}
+
+
+
+/* S/PDIF coax / S/PDIF optical / ADAT - switch */
+static int set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+	u8 previous_mode;
+	int err, i, o;
+
+	if (chip->bad_board)
+		return -EIO;
+
+	/* All audio channels must be closed before changing the digital mode */
+	snd_assert(!chip->pipe_alloc_mask, return -EAGAIN);
+
+	snd_assert(chip->digital_modes & (1 << mode), return -EINVAL);
+
+	previous_mode = chip->digital_mode;
+	err = dsp_set_digital_mode(chip, mode);
+
+	/* If we successfully changed the digital mode from or to ADAT,
+	   then make sure all output, input and monitor levels are
+	   updated by the DSP comm object. */
+	if (err >= 0 && previous_mode != mode &&
+	    (previous_mode == DIGITAL_MODE_ADAT || mode == DIGITAL_MODE_ADAT)) {
+		spin_lock_irq(&chip->lock);
+		for (o = 0; o < num_busses_out(chip); o++)
+			for (i = 0; i < num_busses_in(chip); i++)
+				set_monitor_gain(chip, o, i,
+						 chip->monitor_gain[o][i]);
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+		for (i = 0; i < num_busses_in(chip); i++)
+			set_input_gain(chip, i, chip->input_gain[i]);
+		update_input_line_level(chip);
+#endif
+
+		for (o = 0; o < num_busses_out(chip); o++)
+			set_output_gain(chip, o, chip->output_gain[o]);
+		update_output_line_level(chip);
+		spin_unlock_irq(&chip->lock);
+	}
+
+	return err;
+}
+
+
+
+/* Set the S/PDIF output format */
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+	u32 control_reg;
+	int err;
+
+	/* Clear the current S/PDIF flags */
+	control_reg = le32_to_cpu(chip->comm_page->control_register);
+	control_reg &= GML_SPDIF_FORMAT_CLEAR_MASK;
+
+	/* Set the new S/PDIF flags depending on the mode */
+	control_reg |= GML_SPDIF_TWO_CHANNEL | GML_SPDIF_24_BIT |
+		GML_SPDIF_COPY_PERMIT;
+	if (prof) {
+		/* Professional mode */
+		control_reg |= GML_SPDIF_PRO_MODE;
+
+		switch (chip->sample_rate) {
+		case 32000:
+			control_reg |= GML_SPDIF_SAMPLE_RATE0 |
+				GML_SPDIF_SAMPLE_RATE1;
+			break;
+		case 44100:
+			control_reg |= GML_SPDIF_SAMPLE_RATE0;
+			break;
+		case 48000:
+			control_reg |= GML_SPDIF_SAMPLE_RATE1;
+			break;
+		}
+	} else {
+		/* Consumer mode */
+		switch (chip->sample_rate) {
+		case 32000:
+			control_reg |= GML_SPDIF_SAMPLE_RATE0 |
+				GML_SPDIF_SAMPLE_RATE1;
+			break;
+		case 48000:
+			control_reg |= GML_SPDIF_SAMPLE_RATE1;
+			break;
+		}
+	}
+
+	if ((err = write_control_reg(chip, control_reg, FALSE)))
+		return err;
+	chip->professional_spdif = prof;
+	DE_ACT(("set_professional_spdif to %s\n",
+		prof ? "Professional" : "Consumer"));
+	return 0;
+}
diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c
new file mode 100644
index 000000000000..29d6d12f80ca
--- /dev/null
+++ b/sound/pci/echoaudio/gina20.c
@@ -0,0 +1,103 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHOGALS_FAMILY
+#define ECHOCARD_GINA20
+#define ECHOCARD_NAME "Gina20"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_INPUT_GAIN
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT	FALSE
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT	0	/* 8 */
+#define PX_DIGITAL_OUT	8	/* 2 */
+#define PX_ANALOG_IN	10	/* 2 */
+#define PX_DIGITAL_IN	12	/* 2 */
+#define PX_NUM		14
+
+/* Bus indexes */
+#define BX_ANALOG_OUT	0	/* 8 */
+#define BX_DIGITAL_OUT	8	/* 2 */
+#define BX_ANALOG_IN	10	/* 2 */
+#define BX_DIGITAL_IN	12	/* 2 */
+#define BX_NUM		14
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_GINA20_DSP	0
+
+static const struct firmware card_fw[] = {
+	{0, "gina20_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+	{0x1057, 0x1801, 0xECC0, 0x0020, 0, 0, 0},	/* DSP 56301 Gina20 rev.0 */
+	{0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_SYNC_START,
+	.formats =	SNDRV_PCM_FMTBIT_U8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_3LE |
+			SNDRV_PCM_FMTBIT_S32_LE |
+			SNDRV_PCM_FMTBIT_S32_BE,
+	.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+	.rate_min = 44100,
+	.rate_max = 48000,
+	.channels_min = 1,
+	.channels_max = 2,
+	.buffer_bytes_max = 262144,
+	.period_bytes_min = 32,
+	.period_bytes_max = 131072,
+	.periods_min = 2,
+	.periods_max = 220,
+	/* One page (4k) contains 512 instructions. I don't know if the hw
+	supports lists longer than this. In this case periods_max=220 is a
+	safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "gina20_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c
new file mode 100644
index 000000000000..2757c8960843
--- /dev/null
+++ b/sound/pci/echoaudio/gina20_dsp.c
@@ -0,0 +1,215 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int update_flags(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+	int err;
+
+	DE_INIT(("init_hw() - Gina20\n"));
+	snd_assert((subdevice_id & 0xfff0) == GINA20, return -ENODEV);
+
+	if ((err = init_dsp_comm_page(chip))) {
+		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+		return err;
+	}
+
+	chip->device_id = device_id;
+	chip->subdevice_id = subdevice_id;
+	chip->bad_board = TRUE;
+	chip->dsp_code_to_load = &card_fw[FW_GINA20_DSP];
+	chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
+	chip->clock_state = GD_CLOCK_UNDEF;
+	/* Since this card has no ASIC, mark it as loaded so everything
+	   works OK */
+	chip->asic_loaded = TRUE;
+	chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
+		ECHO_CLOCK_BIT_SPDIF;
+
+	if ((err = load_firmware(chip)) < 0)
+		return err;
+	chip->bad_board = FALSE;
+
+	if ((err = init_line_levels(chip)) < 0)
+		return err;
+
+	err = set_professional_spdif(chip, TRUE);
+
+	DE_INIT(("init_hw done\n"));
+	return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+	u32 clocks_from_dsp, clock_bits;
+
+	/* Map the DSP clock detect bits to the generic driver clock
+	   detect bits */
+	clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+	clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+	if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF)
+		clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+	return clock_bits;
+}
+
+
+
+/* The Gina20 has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+	return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+	u8 clock_state, spdif_status;
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	switch (rate) {
+	case 44100:
+		clock_state = GD_CLOCK_44;
+		spdif_status = GD_SPDIF_STATUS_44;
+		break;
+	case 48000:
+		clock_state = GD_CLOCK_48;
+		spdif_status = GD_SPDIF_STATUS_48;
+		break;
+	default:
+		clock_state = GD_CLOCK_NOCHANGE;
+		spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+		break;
+	}
+
+	if (chip->clock_state == clock_state)
+		clock_state = GD_CLOCK_NOCHANGE;
+	if (spdif_status == chip->spdif_status)
+		spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+
+	chip->comm_page->sample_rate = cpu_to_le32(rate);
+	chip->comm_page->gd_clock_state = clock_state;
+	chip->comm_page->gd_spdif_status = spdif_status;
+	chip->comm_page->gd_resampler_state = 3;	/* magic number - should always be 3 */
+
+	/* Save the new audio state if it changed */
+	if (clock_state != GD_CLOCK_NOCHANGE)
+		chip->clock_state = clock_state;
+	if (spdif_status != GD_SPDIF_STATUS_NOCHANGE)
+		chip->spdif_status = spdif_status;
+	chip->sample_rate = rate;
+
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+	DE_ACT(("set_input_clock:\n"));
+
+	switch (clock) {
+	case ECHO_CLOCK_INTERNAL:
+		/* Reset the audio state to unknown (just in case) */
+		chip->clock_state = GD_CLOCK_UNDEF;
+		chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
+		set_sample_rate(chip, chip->sample_rate);
+		chip->input_clock = clock;
+		DE_ACT(("Set Gina clock to INTERNAL\n"));
+		break;
+	case ECHO_CLOCK_SPDIF:
+		chip->comm_page->gd_clock_state = GD_CLOCK_SPDIFIN;
+		chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+		clear_handshake(chip);
+		send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE);
+		chip->clock_state = GD_CLOCK_SPDIFIN;
+		DE_ACT(("Set Gina20 clock to SPDIF\n"));
+		chip->input_clock = clock;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+
+/* Set input bus gain (one unit is 0.5dB !) */
+static int set_input_gain(struct echoaudio *chip, u16 input, int gain)
+{
+	snd_assert(input < num_busses_in(chip), return -EINVAL);
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	chip->input_gain[input] = gain;
+	gain += GL20_INPUT_GAIN_MAGIC_NUMBER;
+	chip->comm_page->line_in_level[input] = gain;
+	return 0;
+}
+
+
+
+/* Tell the DSP to reread the flags from the comm page */
+static int update_flags(struct echoaudio *chip)
+{
+	if (wait_handshake(chip))
+		return -EIO;
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+	DE_ACT(("set_professional_spdif %d\n", prof));
+	if (prof)
+		chip->comm_page->flags |=
+			__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+	else
+		chip->comm_page->flags &=
+			~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+	chip->professional_spdif = prof;
+	return update_flags(chip);
+}
diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c
new file mode 100644
index 000000000000..e464d720d0bd
--- /dev/null
+++ b/sound/pci/echoaudio/gina24.c
@@ -0,0 +1,123 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHO24_FAMILY
+#define ECHOCARD_GINA24
+#define ECHOCARD_NAME "Gina24"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT	6
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT	0	/* 8 */
+#define PX_DIGITAL_OUT	8	/* 8 */
+#define PX_ANALOG_IN	16	/* 2 */
+#define PX_DIGITAL_IN	18	/* 8 */
+#define PX_NUM		26
+
+/* Bus indexes */
+#define BX_ANALOG_OUT	0	/* 8 */
+#define BX_DIGITAL_OUT	8	/* 8 */
+#define BX_ANALOG_IN	16	/* 2 */
+#define BX_DIGITAL_IN	18	/* 8 */
+#define BX_NUM		26
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER		0
+#define FW_GINA24_301_DSP	1
+#define FW_GINA24_361_DSP	2
+#define FW_GINA24_301_ASIC	3
+#define FW_GINA24_361_ASIC	4
+
+static const struct firmware card_fw[] = {
+	{0, "loader_dsp.fw"},
+	{0, "gina24_301_dsp.fw"},
+	{0, "gina24_361_dsp.fw"},
+	{0, "gina24_301_asic.fw"},
+	{0, "gina24_361_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+	{0x1057, 0x1801, 0xECC0, 0x0050, 0, 0, 0},	/* DSP 56301 Gina24 rev.0 */
+	{0x1057, 0x1801, 0xECC0, 0x0051, 0, 0, 0},	/* DSP 56301 Gina24 rev.1 */
+	{0x1057, 0x3410, 0xECC0, 0x0050, 0, 0, 0},	/* DSP 56361 Gina24 rev.0 */
+	{0x1057, 0x3410, 0xECC0, 0x0051, 0, 0, 0},	/* DSP 56361 Gina24 rev.1 */
+	{0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_SYNC_START,
+	.formats =	SNDRV_PCM_FMTBIT_U8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_3LE |
+			SNDRV_PCM_FMTBIT_S32_LE |
+			SNDRV_PCM_FMTBIT_S32_BE,
+	.rates = 	SNDRV_PCM_RATE_8000_48000 |
+			SNDRV_PCM_RATE_88200 |
+			SNDRV_PCM_RATE_96000,
+	.rate_min = 8000,
+	.rate_max = 96000,
+	.channels_min = 1,
+	.channels_max = 8,
+	.buffer_bytes_max = 262144,
+	.period_bytes_min = 32,
+	.period_bytes_max = 131072,
+	.periods_min = 2,
+	.periods_max = 220,
+	/* One page (4k) contains 512 instructions. I don't know if the hw
+	supports lists longer than this. In this case periods_max=220 is a
+	safe limit to make sure the list never exceeds 512 instructions.
+	220 ~= (512 - 1 - (BUFFER_BYTES_MAX / PAGE_SIZE)) / 2 */
+};
+
+#include "gina24_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio_gml.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c
new file mode 100644
index 000000000000..144fc567becf
--- /dev/null
+++ b/sound/pci/echoaudio/gina24_dsp.c
@@ -0,0 +1,346 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int write_control_reg(struct echoaudio *chip, u32 value, char force);
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int set_digital_mode(struct echoaudio *chip, u8 mode);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+			     const struct firmware *asic);
+static int check_asic_status(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+	int err;
+
+	DE_INIT(("init_hw() - Gina24\n"));
+	snd_assert((subdevice_id & 0xfff0) == GINA24, return -ENODEV);
+
+	if ((err = init_dsp_comm_page(chip))) {
+		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+		return err;
+	}
+
+	chip->device_id = device_id;
+	chip->subdevice_id = subdevice_id;
+	chip->bad_board = TRUE;
+	chip->input_clock_types =
+		ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
+		ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96 |
+		ECHO_CLOCK_BIT_ADAT;
+	chip->professional_spdif = FALSE;
+	chip->digital_in_automute = TRUE;
+	chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
+
+	/* Gina24 comes in both '301 and '361 flavors */
+	if (chip->device_id == DEVICE_ID_56361) {
+		chip->dsp_code_to_load = &card_fw[FW_GINA24_361_DSP];
+		chip->digital_modes =
+			ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+			ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+			ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
+	} else {
+		chip->dsp_code_to_load = &card_fw[FW_GINA24_301_DSP];
+		chip->digital_modes =
+			ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+			ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+			ECHOCAPS_HAS_DIGITAL_MODE_ADAT |
+			ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_CDROM;
+	}
+
+	if ((err = load_firmware(chip)) < 0)
+		return err;
+	chip->bad_board = FALSE;
+
+	if ((err = init_line_levels(chip)) < 0)
+		return err;
+	err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
+	snd_assert(err >= 0, return err);
+	err = set_professional_spdif(chip, TRUE);
+
+	DE_INIT(("init_hw done\n"));
+	return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+	u32 clocks_from_dsp, clock_bits;
+
+	/* Map the DSP clock detect bits to the generic driver clock
+	   detect bits */
+	clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+	clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+	if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF)
+		clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+	if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT)
+		clock_bits |= ECHO_CLOCK_BIT_ADAT;
+
+	if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ESYNC)
+		clock_bits |= ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96;
+
+	return clock_bits;
+}
+
+
+
+/* Gina24 has an ASIC on the PCI card which must be loaded for anything
+interesting to happen. */
+static int load_asic(struct echoaudio *chip)
+{
+	u32 control_reg;
+	int err;
+	const struct firmware *fw;
+
+	if (chip->asic_loaded)
+		return 1;
+
+	/* Give the DSP a few milliseconds to settle down */
+	mdelay(10);
+
+	/* Pick the correct ASIC for '301 or '361 Gina24 */
+	if (chip->device_id == DEVICE_ID_56361)
+		fw = &card_fw[FW_GINA24_361_ASIC];
+	else
+		fw = &card_fw[FW_GINA24_301_ASIC];
+
+	if ((err = load_asic_generic(chip, DSP_FNC_LOAD_GINA24_ASIC, fw)) < 0)
+		return err;
+
+	chip->asic_code = fw;
+
+	/* Now give the new ASIC a little time to set up */
+	mdelay(10);
+	/* See if it worked */
+	err = check_asic_status(chip);
+
+	/* Set up the control register if the load succeeded -
+	   48 kHz, internal clock, S/PDIF RCA mode */
+	if (!err) {
+		control_reg = GML_CONVERTER_ENABLE | GML_48KHZ;
+		err = write_control_reg(chip, control_reg, TRUE);
+	}
+	DE_INIT(("load_asic() done\n"));
+	return err;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+	u32 control_reg, clock;
+
+	snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
+		   return -EINVAL);
+
+	/* Only set the clock for internal mode. */
+	if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+		DE_ACT(("set_sample_rate: Cannot set sample rate - "
+			"clock not set to CLK_CLOCKININTERNAL\n"));
+		/* Save the rate anyhow */
+		chip->comm_page->sample_rate = cpu_to_le32(rate);
+		chip->sample_rate = rate;
+		return 0;
+	}
+
+	clock = 0;
+
+	control_reg = le32_to_cpu(chip->comm_page->control_register);
+	control_reg &= GML_CLOCK_CLEAR_MASK & GML_SPDIF_RATE_CLEAR_MASK;
+
+	switch (rate) {
+	case 96000:
+		clock = GML_96KHZ;
+		break;
+	case 88200:
+		clock = GML_88KHZ;
+		break;
+	case 48000:
+		clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1;
+		break;
+	case 44100:
+		clock = GML_44KHZ;
+		/* Professional mode ? */
+		if (control_reg & GML_SPDIF_PRO_MODE)
+			clock |= GML_SPDIF_SAMPLE_RATE0;
+		break;
+	case 32000:
+		clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 |
+			GML_SPDIF_SAMPLE_RATE1;
+		break;
+	case 22050:
+		clock = GML_22KHZ;
+		break;
+	case 16000:
+		clock = GML_16KHZ;
+		break;
+	case 11025:
+		clock = GML_11KHZ;
+		break;
+	case 8000:
+		clock = GML_8KHZ;
+		break;
+	default:
+		DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+		return -EINVAL;
+	}
+
+	control_reg |= clock;
+
+	chip->comm_page->sample_rate = cpu_to_le32(rate);	/* ignored by the DSP */
+	chip->sample_rate = rate;
+	DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+
+	return write_control_reg(chip, control_reg, FALSE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+	u32 control_reg, clocks_from_dsp;
+
+	DE_ACT(("set_input_clock:\n"));
+
+	/* Mask off the clock select bits */
+	control_reg = le32_to_cpu(chip->comm_page->control_register) &
+		GML_CLOCK_CLEAR_MASK;
+	clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+	switch (clock) {
+	case ECHO_CLOCK_INTERNAL:
+		DE_ACT(("Set Gina24 clock to INTERNAL\n"));
+		chip->input_clock = ECHO_CLOCK_INTERNAL;
+		return set_sample_rate(chip, chip->sample_rate);
+	case ECHO_CLOCK_SPDIF:
+		if (chip->digital_mode == DIGITAL_MODE_ADAT)
+			return -EAGAIN;
+		DE_ACT(("Set Gina24 clock to SPDIF\n"));
+		control_reg |= GML_SPDIF_CLOCK;
+		if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96)
+			control_reg |= GML_DOUBLE_SPEED_MODE;
+		else
+			control_reg &= ~GML_DOUBLE_SPEED_MODE;
+		break;
+	case ECHO_CLOCK_ADAT:
+		if (chip->digital_mode != DIGITAL_MODE_ADAT)
+			return -EAGAIN;
+		DE_ACT(("Set Gina24 clock to ADAT\n"));
+		control_reg |= GML_ADAT_CLOCK;
+		control_reg &= ~GML_DOUBLE_SPEED_MODE;
+		break;
+	case ECHO_CLOCK_ESYNC:
+		DE_ACT(("Set Gina24 clock to ESYNC\n"));
+		control_reg |= GML_ESYNC_CLOCK;
+		control_reg &= ~GML_DOUBLE_SPEED_MODE;
+		break;
+	case ECHO_CLOCK_ESYNC96:
+		DE_ACT(("Set Gina24 clock to ESYNC96\n"));
+		control_reg |= GML_ESYNC_CLOCK | GML_DOUBLE_SPEED_MODE;
+		break;
+	default:
+		DE_ACT(("Input clock 0x%x not supported for Gina24\n", clock));
+		return -EINVAL;
+	}
+
+	chip->input_clock = clock;
+	return write_control_reg(chip, control_reg, TRUE);
+}
+
+
+
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+	u32 control_reg;
+	int err, incompatible_clock;
+
+	/* Set clock to "internal" if it's not compatible with the new mode */
+	incompatible_clock = FALSE;
+	switch (mode) {
+	case DIGITAL_MODE_SPDIF_OPTICAL:
+	case DIGITAL_MODE_SPDIF_CDROM:
+	case DIGITAL_MODE_SPDIF_RCA:
+		if (chip->input_clock == ECHO_CLOCK_ADAT)
+			incompatible_clock = TRUE;
+		break;
+	case DIGITAL_MODE_ADAT:
+		if (chip->input_clock == ECHO_CLOCK_SPDIF)
+			incompatible_clock = TRUE;
+		break;
+	default:
+		DE_ACT(("Digital mode not supported: %d\n", mode));
+		return -EINVAL;
+	}
+
+	spin_lock_irq(&chip->lock);
+
+	if (incompatible_clock) {	/* Switch to 48KHz, internal */
+		chip->sample_rate = 48000;
+		set_input_clock(chip, ECHO_CLOCK_INTERNAL);
+	}
+
+	/* Clear the current digital mode */
+	control_reg = le32_to_cpu(chip->comm_page->control_register);
+	control_reg &= GML_DIGITAL_MODE_CLEAR_MASK;
+
+	/* Tweak the control reg */
+	switch (mode) {
+	case DIGITAL_MODE_SPDIF_OPTICAL:
+		control_reg |= GML_SPDIF_OPTICAL_MODE;
+		break;
+	case DIGITAL_MODE_SPDIF_CDROM:
+		/* '361 Gina24 cards do not have the S/PDIF CD-ROM mode */
+		if (chip->device_id == DEVICE_ID_56301)
+			control_reg |= GML_SPDIF_CDROM_MODE;
+		break;
+	case DIGITAL_MODE_SPDIF_RCA:
+		/* GML_SPDIF_OPTICAL_MODE bit cleared */
+		break;
+	case DIGITAL_MODE_ADAT:
+		control_reg |= GML_ADAT_MODE;
+		control_reg &= ~GML_DOUBLE_SPEED_MODE;
+		break;
+	}
+
+	err = write_control_reg(chip, control_reg, TRUE);
+	spin_unlock_irq(&chip->lock);
+	if (err < 0)
+		return err;
+	chip->digital_mode = mode;
+
+	DE_ACT(("set_digital_mode to %d\n", chip->digital_mode));
+	return incompatible_clock;
+}
diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c
new file mode 100644
index 000000000000..bfd2467099ac
--- /dev/null
+++ b/sound/pci/echoaudio/indigo.c
@@ -0,0 +1,104 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define INDIGO_FAMILY
+#define ECHOCARD_INDIGO
+#define ECHOCARD_NAME "Indigo"
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_VMIXER
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT	0	/* 8 */
+#define PX_DIGITAL_OUT	8	/* 0 */
+#define PX_ANALOG_IN	8	/* 0 */
+#define PX_DIGITAL_IN	8	/* 0 */
+#define PX_NUM		8
+
+/* Bus indexes */
+#define BX_ANALOG_OUT	0	/* 2 */
+#define BX_DIGITAL_OUT	2	/* 0 */
+#define BX_ANALOG_IN	2	/* 0 */
+#define BX_DIGITAL_IN	2	/* 0 */
+#define BX_NUM		2
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER	0
+#define FW_INDIGO_DSP	1
+
+static const struct firmware card_fw[] = {
+	{0, "loader_dsp.fw"},
+	{0, "indigo_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+	{0x1057, 0x3410, 0xECC0, 0x0090, 0, 0, 0},	/* Indigo */
+	{0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_SYNC_START,
+	.formats =	SNDRV_PCM_FMTBIT_U8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_3LE |
+			SNDRV_PCM_FMTBIT_S32_LE |
+			SNDRV_PCM_FMTBIT_S32_BE,
+	.rates = 	SNDRV_PCM_RATE_32000 |
+			SNDRV_PCM_RATE_44100 |
+			SNDRV_PCM_RATE_48000 |
+			SNDRV_PCM_RATE_88200 |
+			SNDRV_PCM_RATE_96000,
+	.rate_min = 32000,
+	.rate_max = 96000,
+	.channels_min = 1,
+	.channels_max = 8,
+	.buffer_bytes_max = 262144,
+	.period_bytes_min = 32,
+	.period_bytes_max = 131072,
+	.periods_min = 2,
+	.periods_max = 220,
+};
+
+#include "indigo_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+
diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c
new file mode 100644
index 000000000000..d6ac7734609e
--- /dev/null
+++ b/sound/pci/echoaudio/indigo_dsp.c
@@ -0,0 +1,170 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+			   int gain);
+static int update_vmixer_level(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+	int err;
+
+	DE_INIT(("init_hw() - Indigo\n"));
+	snd_assert((subdevice_id & 0xfff0) == INDIGO, return -ENODEV);
+
+	if ((err = init_dsp_comm_page(chip))) {
+		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+		return err;
+	}
+
+	chip->device_id = device_id;
+	chip->subdevice_id = subdevice_id;
+	chip->bad_board = TRUE;
+	chip->dsp_code_to_load = &card_fw[FW_INDIGO_DSP];
+	/* Since this card has no ASIC, mark it as loaded so everything
+	   works OK */
+	chip->asic_loaded = TRUE;
+	chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
+
+	if ((err = load_firmware(chip)) < 0)
+		return err;
+	chip->bad_board = FALSE;
+
+	if ((err = init_line_levels(chip)) < 0)
+		return err;
+
+	/* Default routing of the virtual channels: all vchannels are routed
+	to the stereo output */
+	set_vmixer_gain(chip, 0, 0, 0);
+	set_vmixer_gain(chip, 1, 1, 0);
+	set_vmixer_gain(chip, 0, 2, 0);
+	set_vmixer_gain(chip, 1, 3, 0);
+	set_vmixer_gain(chip, 0, 4, 0);
+	set_vmixer_gain(chip, 1, 5, 0);
+	set_vmixer_gain(chip, 0, 6, 0);
+	set_vmixer_gain(chip, 1, 7, 0);
+	err = update_vmixer_level(chip);
+
+	DE_INIT(("init_hw done\n"));
+	return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+	return ECHO_CLOCK_BIT_INTERNAL;
+}
+
+
+
+/* The Indigo has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+	return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+	u32 control_reg;
+
+	switch (rate) {
+	case 96000:
+		control_reg = MIA_96000;
+		break;
+	case 88200:
+		control_reg = MIA_88200;
+		break;
+	case 48000:
+		control_reg = MIA_48000;
+		break;
+	case 44100:
+		control_reg = MIA_44100;
+		break;
+	case 32000:
+		control_reg = MIA_32000;
+		break;
+	default:
+		DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+		return -EINVAL;
+	}
+
+	/* Set the control register if it has changed */
+	if (control_reg != le32_to_cpu(chip->comm_page->control_register)) {
+		if (wait_handshake(chip))
+			return -EIO;
+
+		chip->comm_page->sample_rate = cpu_to_le32(rate);	/* ignored by the DSP */
+		chip->comm_page->control_register = cpu_to_le32(control_reg);
+		chip->sample_rate = rate;
+
+		clear_handshake(chip);
+		return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+	}
+	return 0;
+}
+
+
+
+/* This function routes the sound from a virtual channel to a real output */
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+			   int gain)
+{
+	int index;
+
+	snd_assert(pipe < num_pipes_out(chip) &&
+		   output < num_busses_out(chip), return -EINVAL);
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	chip->vmixer_gain[output][pipe] = gain;
+	index = output * num_pipes_out(chip) + pipe;
+	chip->comm_page->vmixer[index] = gain;
+
+	DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+	return 0;
+}
+
+
+
+/* Tell the DSP to read and update virtual mixer levels in comm page. */
+static int update_vmixer_level(struct echoaudio *chip)
+{
+	if (wait_handshake(chip))
+		return -EIO;
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_SET_VMIXER_GAIN);
+}
+
diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c
new file mode 100644
index 000000000000..8ed7ff1fd875
--- /dev/null
+++ b/sound/pci/echoaudio/indigodj.c
@@ -0,0 +1,104 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define INDIGO_FAMILY
+#define ECHOCARD_INDIGO_DJ
+#define ECHOCARD_NAME "Indigo DJ"
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_VMIXER
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT	0	/* 8 */
+#define PX_DIGITAL_OUT	8	/* 0 */
+#define PX_ANALOG_IN	8	/* 0 */
+#define PX_DIGITAL_IN	8	/* 0 */
+#define PX_NUM		8
+
+/* Bus indexes */
+#define BX_ANALOG_OUT	0	/* 4 */
+#define BX_DIGITAL_OUT	4	/* 0 */
+#define BX_ANALOG_IN	4	/* 0 */
+#define BX_DIGITAL_IN	4	/* 0 */
+#define BX_NUM		4
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER		0
+#define FW_INDIGO_DJ_DSP	1
+
+static const struct firmware card_fw[] = {
+	{0, "loader_dsp.fw"},
+	{0, "indigo_dj_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+	{0x1057, 0x3410, 0xECC0, 0x00B0, 0, 0, 0},	/* Indigo DJ*/
+	{0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_SYNC_START,
+	.formats =	SNDRV_PCM_FMTBIT_U8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_3LE |
+			SNDRV_PCM_FMTBIT_S32_LE |
+			SNDRV_PCM_FMTBIT_S32_BE,
+	.rates = 	SNDRV_PCM_RATE_32000 |
+			SNDRV_PCM_RATE_44100 |
+			SNDRV_PCM_RATE_48000 |
+			SNDRV_PCM_RATE_88200 |
+			SNDRV_PCM_RATE_96000,
+	.rate_min = 32000,
+	.rate_max = 96000,
+	.channels_min = 1,
+	.channels_max = 4,
+	.buffer_bytes_max = 262144,
+	.period_bytes_min = 32,
+	.period_bytes_max = 131072,
+	.periods_min = 2,
+	.periods_max = 220,
+};
+
+#include "indigodj_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+
diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c
new file mode 100644
index 000000000000..500e150b49fc
--- /dev/null
+++ b/sound/pci/echoaudio/indigodj_dsp.c
@@ -0,0 +1,170 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+			   int gain);
+static int update_vmixer_level(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+	int err;
+
+	DE_INIT(("init_hw() - Indigo DJ\n"));
+	snd_assert((subdevice_id & 0xfff0) == INDIGO_DJ, return -ENODEV);
+
+	if ((err = init_dsp_comm_page(chip))) {
+		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+		return err;
+	}
+
+	chip->device_id = device_id;
+	chip->subdevice_id = subdevice_id;
+	chip->bad_board = TRUE;
+	chip->dsp_code_to_load = &card_fw[FW_INDIGO_DJ_DSP];
+	/* Since this card has no ASIC, mark it as loaded so everything
+	   works OK */
+	chip->asic_loaded = TRUE;
+	chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
+
+	if ((err = load_firmware(chip)) < 0)
+		return err;
+	chip->bad_board = FALSE;
+
+	if ((err = init_line_levels(chip)) < 0)
+		return err;
+
+	/* Default routing of the virtual channels: vchannels 0-3 and
+	vchannels 4-7 are routed to real channels 0-4 */
+	set_vmixer_gain(chip, 0, 0, 0);
+	set_vmixer_gain(chip, 1, 1, 0);
+	set_vmixer_gain(chip, 2, 2, 0);
+	set_vmixer_gain(chip, 3, 3, 0);
+	set_vmixer_gain(chip, 0, 4, 0);
+	set_vmixer_gain(chip, 1, 5, 0);
+	set_vmixer_gain(chip, 2, 6, 0);
+	set_vmixer_gain(chip, 3, 7, 0);
+	err = update_vmixer_level(chip);
+
+	DE_INIT(("init_hw done\n"));
+	return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+	return ECHO_CLOCK_BIT_INTERNAL;
+}
+
+
+
+/* The IndigoDJ has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+	return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+	u32 control_reg;
+
+	switch (rate) {
+	case 96000:
+		control_reg = MIA_96000;
+		break;
+	case 88200:
+		control_reg = MIA_88200;
+		break;
+	case 48000:
+		control_reg = MIA_48000;
+		break;
+	case 44100:
+		control_reg = MIA_44100;
+		break;
+	case 32000:
+		control_reg = MIA_32000;
+		break;
+	default:
+		DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+		return -EINVAL;
+	}
+
+	/* Set the control register if it has changed */
+	if (control_reg != le32_to_cpu(chip->comm_page->control_register)) {
+		if (wait_handshake(chip))
+			return -EIO;
+
+		chip->comm_page->sample_rate = cpu_to_le32(rate);	/* ignored by the DSP */
+		chip->comm_page->control_register = cpu_to_le32(control_reg);
+		chip->sample_rate = rate;
+
+		clear_handshake(chip);
+		return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+	}
+	return 0;
+}
+
+
+
+/* This function routes the sound from a virtual channel to a real output */
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+			   int gain)
+{
+	int index;
+
+	snd_assert(pipe < num_pipes_out(chip) &&
+		   output < num_busses_out(chip), return -EINVAL);
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	chip->vmixer_gain[output][pipe] = gain;
+	index = output * num_pipes_out(chip) + pipe;
+	chip->comm_page->vmixer[index] = gain;
+
+	DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+	return 0;
+}
+
+
+
+/* Tell the DSP to read and update virtual mixer levels in comm page. */
+static int update_vmixer_level(struct echoaudio *chip)
+{
+	if (wait_handshake(chip))
+		return -EIO;
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_SET_VMIXER_GAIN);
+}
+
diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c
new file mode 100644
index 000000000000..a8788e959171
--- /dev/null
+++ b/sound/pci/echoaudio/indigoio.c
@@ -0,0 +1,105 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define INDIGO_FAMILY
+#define ECHOCARD_INDIGO_IO
+#define ECHOCARD_NAME "Indigo IO"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_VMIXER
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT	0	/* 8 */
+#define PX_DIGITAL_OUT	8	/* 0 */
+#define PX_ANALOG_IN	8	/* 2 */
+#define PX_DIGITAL_IN	10	/* 0 */
+#define PX_NUM		10
+
+/* Bus indexes */
+#define BX_ANALOG_OUT	0	/* 2 */
+#define BX_DIGITAL_OUT	2	/* 0 */
+#define BX_ANALOG_IN	2	/* 2 */
+#define BX_DIGITAL_IN	4	/* 0 */
+#define BX_NUM		4
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER		0
+#define FW_INDIGO_IO_DSP	1
+
+static const struct firmware card_fw[] = {
+	{0, "loader_dsp.fw"},
+	{0, "indigo_io_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+	{0x1057, 0x3410, 0xECC0, 0x00A0, 0, 0, 0},	/* Indigo IO*/
+	{0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_SYNC_START,
+	.formats =	SNDRV_PCM_FMTBIT_U8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_3LE |
+			SNDRV_PCM_FMTBIT_S32_LE |
+			SNDRV_PCM_FMTBIT_S32_BE,
+	.rates = 	SNDRV_PCM_RATE_32000 |
+			SNDRV_PCM_RATE_44100 |
+			SNDRV_PCM_RATE_48000 |
+			SNDRV_PCM_RATE_88200 |
+			SNDRV_PCM_RATE_96000,
+	.rate_min = 32000,
+	.rate_max = 96000,
+	.channels_min = 1,
+	.channels_max = 8,
+	.buffer_bytes_max = 262144,
+	.period_bytes_min = 32,
+	.period_bytes_max = 131072,
+	.periods_min = 2,
+	.periods_max = 220,
+};
+
+#include "indigoio_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+
diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c
new file mode 100644
index 000000000000..f3ad13d06be0
--- /dev/null
+++ b/sound/pci/echoaudio/indigoio_dsp.c
@@ -0,0 +1,141 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+			   int gain);
+static int update_vmixer_level(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+	int err;
+
+	DE_INIT(("init_hw() - Indigo IO\n"));
+	snd_assert((subdevice_id & 0xfff0) == INDIGO_IO, return -ENODEV);
+
+	if ((err = init_dsp_comm_page(chip))) {
+		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+		return err;
+	}
+
+	chip->device_id = device_id;
+	chip->subdevice_id = subdevice_id;
+	chip->bad_board = TRUE;
+	chip->dsp_code_to_load = &card_fw[FW_INDIGO_IO_DSP];
+	/* Since this card has no ASIC, mark it as loaded so everything
+	   works OK */
+	chip->asic_loaded = TRUE;
+	chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
+
+	if ((err = load_firmware(chip)) < 0)
+		return err;
+	chip->bad_board = FALSE;
+
+	if ((err = init_line_levels(chip)) < 0)
+		return err;
+
+	/* Default routing of the virtual channels: all vchannels are routed
+	to the stereo output */
+	set_vmixer_gain(chip, 0, 0, 0);
+	set_vmixer_gain(chip, 1, 1, 0);
+	set_vmixer_gain(chip, 0, 2, 0);
+	set_vmixer_gain(chip, 1, 3, 0);
+	set_vmixer_gain(chip, 0, 4, 0);
+	set_vmixer_gain(chip, 1, 5, 0);
+	set_vmixer_gain(chip, 0, 6, 0);
+	set_vmixer_gain(chip, 1, 7, 0);
+	err = update_vmixer_level(chip);
+
+	DE_INIT(("init_hw done\n"));
+	return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+	return ECHO_CLOCK_BIT_INTERNAL;
+}
+
+
+
+/* The IndigoIO has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+	return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+	if (wait_handshake(chip))
+		return -EIO;
+
+	chip->sample_rate = rate;
+	chip->comm_page->sample_rate = cpu_to_le32(rate);
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+}
+
+
+
+/* This function routes the sound from a virtual channel to a real output */
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+			   int gain)
+{
+	int index;
+
+	snd_assert(pipe < num_pipes_out(chip) &&
+		   output < num_busses_out(chip), return -EINVAL);
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	chip->vmixer_gain[output][pipe] = gain;
+	index = output * num_pipes_out(chip) + pipe;
+	chip->comm_page->vmixer[index] = gain;
+
+	DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+	return 0;
+}
+
+
+
+/* Tell the DSP to read and update virtual mixer levels in comm page. */
+static int update_vmixer_level(struct echoaudio *chip)
+{
+	if (wait_handshake(chip))
+		return -EIO;
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_SET_VMIXER_GAIN);
+}
+
diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c
new file mode 100644
index 000000000000..e503d74b3ba9
--- /dev/null
+++ b/sound/pci/echoaudio/layla20.c
@@ -0,0 +1,112 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHOGALS_FAMILY
+#define ECHOCARD_LAYLA20
+#define ECHOCARD_NAME "Layla20"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_INPUT_GAIN
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT	FALSE
+#define ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH
+#define ECHOCARD_HAS_MIDI
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT	0	/* 10 */
+#define PX_DIGITAL_OUT	10	/*  2 */
+#define PX_ANALOG_IN	12	/*  8 */
+#define PX_DIGITAL_IN	20	/*  2 */
+#define PX_NUM		22
+
+/* Bus indexes */
+#define BX_ANALOG_OUT	0	/* 10 */
+#define BX_DIGITAL_OUT	10	/*  2 */
+#define BX_ANALOG_IN	12	/*  8 */
+#define BX_DIGITAL_IN	20	/*  2 */
+#define BX_NUM		22
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_LAYLA20_DSP	0
+#define FW_LAYLA20_ASIC	1
+
+static const struct firmware card_fw[] = {
+	{0, "layla20_dsp.fw"},
+	{0, "layla20_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+	{0x1057, 0x1801, 0xECC0, 0x0030, 0, 0, 0},	/* DSP 56301 Layla20 rev.0 */
+	{0x1057, 0x1801, 0xECC0, 0x0031, 0, 0, 0},	/* DSP 56301 Layla20 rev.1 */
+	{0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_SYNC_START,
+	.formats =	SNDRV_PCM_FMTBIT_U8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_3LE |
+			SNDRV_PCM_FMTBIT_S32_LE |
+			SNDRV_PCM_FMTBIT_S32_BE,
+	.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS,
+	.rate_min = 8000,
+	.rate_max = 50000,
+	.channels_min = 1,
+	.channels_max = 10,
+	.buffer_bytes_max = 262144,
+	.period_bytes_min = 32,
+	.period_bytes_max = 131072,
+	.periods_min = 2,
+	.periods_max = 220,
+	/* One page (4k) contains 512 instructions. I don't know if the hw
+	supports lists longer than this. In this case periods_max=220 is a
+	safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+#include "layla20_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+#include "midi.c"
diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c
new file mode 100644
index 000000000000..990c9a60a0a8
--- /dev/null
+++ b/sound/pci/echoaudio/layla20_dsp.c
@@ -0,0 +1,290 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int read_dsp(struct echoaudio *chip, u32 *data);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+			     const struct firmware *asic);
+static int check_asic_status(struct echoaudio *chip);
+static int update_flags(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+	int err;
+
+	DE_INIT(("init_hw() - Layla20\n"));
+	snd_assert((subdevice_id & 0xfff0) == LAYLA20, return -ENODEV);
+
+	if ((err = init_dsp_comm_page(chip))) {
+		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+		return err;
+	}
+
+	chip->device_id = device_id;
+	chip->subdevice_id = subdevice_id;
+	chip->bad_board = TRUE;
+	chip->has_midi = TRUE;
+	chip->dsp_code_to_load = &card_fw[FW_LAYLA20_DSP];
+	chip->input_clock_types =
+		ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
+		ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER;
+	chip->output_clock_types =
+		ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER;
+
+	if ((err = load_firmware(chip)) < 0)
+		return err;
+	chip->bad_board = FALSE;
+
+	if ((err = init_line_levels(chip)) < 0)
+		return err;
+
+	err = set_professional_spdif(chip, TRUE);
+
+	DE_INIT(("init_hw done\n"));
+	return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+	u32 clocks_from_dsp, clock_bits;
+
+	/* Map the DSP clock detect bits to the generic driver clock detect bits */
+	clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+	clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+	if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF)
+		clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+	if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_WORD) {
+		if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SUPER)
+			clock_bits |= ECHO_CLOCK_BIT_SUPER;
+		else
+			clock_bits |= ECHO_CLOCK_BIT_WORD;
+	}
+
+	return clock_bits;
+}
+
+
+
+/* ASIC status check - some cards have one or two ASICs that need to be
+loaded.  Once that load is complete, this function is called to see if
+the load was successful.
+If this load fails, it does not necessarily mean that the hardware is
+defective - the external box may be disconnected or turned off.
+This routine sometimes fails for Layla20; for Layla20, the loop runs
+5 times and succeeds if it wins on three of the loops. */
+static int check_asic_status(struct echoaudio *chip)
+{
+	u32 asic_status;
+	int goodcnt, i;
+
+	chip->asic_loaded = FALSE;
+	for (i = goodcnt = 0; i < 5; i++) {
+		send_vector(chip, DSP_VC_TEST_ASIC);
+
+		/* The DSP will return a value to indicate whether or not
+		   the ASIC is currently loaded */
+		if (read_dsp(chip, &asic_status) < 0) {
+			DE_ACT(("check_asic_status: failed on read_dsp\n"));
+			return -EIO;
+		}
+
+		if (asic_status == ASIC_ALREADY_LOADED) {
+			if (++goodcnt == 3) {
+				chip->asic_loaded = TRUE;
+				return 0;
+			}
+		}
+	}
+	return -EIO;
+}
+
+
+
+/* Layla20 has an ASIC in the external box */
+static int load_asic(struct echoaudio *chip)
+{
+	int err;
+
+	if (chip->asic_loaded)
+		return 0;
+
+	err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA_ASIC,
+				&card_fw[FW_LAYLA20_ASIC]);
+	if (err < 0)
+		return err;
+
+	/* Check if ASIC is alive and well. */
+	return check_asic_status(chip);
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+	snd_assert(rate >= 8000 && rate <= 50000, return -EINVAL);
+
+	/* Only set the clock for internal mode. Do not return failure,
+	   simply treat it as a non-event. */
+	if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+		DE_ACT(("set_sample_rate: Cannot set sample rate - "
+			"clock not set to CLK_CLOCKININTERNAL\n"));
+		chip->comm_page->sample_rate = cpu_to_le32(rate);
+		chip->sample_rate = rate;
+		return 0;
+	}
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	DE_ACT(("set_sample_rate(%d)\n", rate));
+	chip->sample_rate = rate;
+	chip->comm_page->sample_rate = cpu_to_le32(rate);
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_SET_LAYLA_SAMPLE_RATE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock_source)
+{
+	u16 clock;
+	u32 rate;
+
+	DE_ACT(("set_input_clock:\n"));
+	rate = 0;
+	switch (clock_source) {
+	case ECHO_CLOCK_INTERNAL:
+		DE_ACT(("Set Layla20 clock to INTERNAL\n"));
+		rate = chip->sample_rate;
+		clock = LAYLA20_CLOCK_INTERNAL;
+		break;
+	case ECHO_CLOCK_SPDIF:
+		DE_ACT(("Set Layla20 clock to SPDIF\n"));
+		clock = LAYLA20_CLOCK_SPDIF;
+		break;
+	case ECHO_CLOCK_WORD:
+		DE_ACT(("Set Layla20 clock to WORD\n"));
+		clock = LAYLA20_CLOCK_WORD;
+		break;
+	case ECHO_CLOCK_SUPER:
+		DE_ACT(("Set Layla20 clock to SUPER\n"));
+		clock = LAYLA20_CLOCK_SUPER;
+		break;
+	default:
+		DE_ACT(("Input clock 0x%x not supported for Layla24\n",
+			clock_source));
+		return -EINVAL;
+	}
+	chip->input_clock = clock_source;
+
+	chip->comm_page->input_clock = cpu_to_le16(clock);
+	clear_handshake(chip);
+	send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+
+	if (rate)
+		set_sample_rate(chip, rate);
+
+	return 0;
+}
+
+
+
+static int set_output_clock(struct echoaudio *chip, u16 clock)
+{
+	DE_ACT(("set_output_clock: %d\n", clock));
+	switch (clock) {
+	case ECHO_CLOCK_SUPER:
+		clock = LAYLA20_OUTPUT_CLOCK_SUPER;
+		break;
+	case ECHO_CLOCK_WORD:
+		clock = LAYLA20_OUTPUT_CLOCK_WORD;
+		break;
+	default:
+		DE_ACT(("set_output_clock wrong clock\n"));
+		return -EINVAL;
+	}
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	chip->comm_page->output_clock = cpu_to_le16(clock);
+	chip->output_clock = clock;
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+}
+
+
+
+/* Set input bus gain (one unit is 0.5dB !) */
+static int set_input_gain(struct echoaudio *chip, u16 input, int gain)
+{
+	snd_assert(input < num_busses_in(chip), return -EINVAL);
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	chip->input_gain[input] = gain;
+	gain += GL20_INPUT_GAIN_MAGIC_NUMBER;
+	chip->comm_page->line_in_level[input] = gain;
+	return 0;
+}
+
+
+
+/* Tell the DSP to reread the flags from the comm page */
+static int update_flags(struct echoaudio *chip)
+{
+	if (wait_handshake(chip))
+		return -EIO;
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+	DE_ACT(("set_professional_spdif %d\n", prof));
+	if (prof)
+		chip->comm_page->flags |=
+			__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+	else
+		chip->comm_page->flags &=
+			~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+	chip->professional_spdif = prof;
+	return update_flags(chip);
+}
diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c
new file mode 100644
index 000000000000..d4581fdc841c
--- /dev/null
+++ b/sound/pci/echoaudio/layla24.c
@@ -0,0 +1,121 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHO24_FAMILY
+#define ECHOCARD_LAYLA24
+#define ECHOCARD_NAME "Layla24"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT	6
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+#define ECHOCARD_HAS_MIDI
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT	0	/* 8 */
+#define PX_DIGITAL_OUT	8	/* 8 */
+#define PX_ANALOG_IN	16	/* 8 */
+#define PX_DIGITAL_IN	24	/* 8 */
+#define PX_NUM		32
+
+/* Bus indexes */
+#define BX_ANALOG_OUT	0	/* 8 */
+#define BX_DIGITAL_OUT	8	/* 8 */
+#define BX_ANALOG_IN	16	/* 8 */
+#define BX_DIGITAL_IN	24	/* 8 */
+#define BX_NUM		32
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER		0
+#define FW_LAYLA24_DSP		1
+#define FW_LAYLA24_1_ASIC	2
+#define FW_LAYLA24_2A_ASIC	3
+#define FW_LAYLA24_2S_ASIC	4
+
+static const struct firmware card_fw[] = {
+	{0, "loader_dsp.fw"},
+	{0, "layla24_dsp.fw"},
+	{0, "layla24_1_asic.fw"},
+	{0, "layla24_2A_asic.fw"},
+	{0, "layla24_2S_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+	{0x1057, 0x3410, 0xECC0, 0x0060, 0, 0, 0},	/* DSP 56361 Layla24 rev.0 */
+	{0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_SYNC_START,
+	.formats =	SNDRV_PCM_FMTBIT_U8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_3LE |
+			SNDRV_PCM_FMTBIT_S32_LE |
+			SNDRV_PCM_FMTBIT_S32_BE,
+	.rates =	SNDRV_PCM_RATE_8000_96000,
+	.rate_min = 8000,
+	.rate_max = 100000,
+	.channels_min = 1,
+	.channels_max = 8,
+	.buffer_bytes_max = 262144,
+	.period_bytes_min = 32,
+	.period_bytes_max = 131072,
+	.periods_min = 2,
+	.periods_max = 220,
+	/* One page (4k) contains 512 instructions. I don't know if the hw
+	supports lists longer than this. In this case periods_max=220 is a
+	safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "layla24_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio_gml.c"
+#include "echoaudio.c"
+#include "midi.c"
diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c
new file mode 100644
index 000000000000..7ec5b63d0dce
--- /dev/null
+++ b/sound/pci/echoaudio/layla24_dsp.c
@@ -0,0 +1,394 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int write_control_reg(struct echoaudio *chip, u32 value, char force);
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int set_digital_mode(struct echoaudio *chip, u8 mode);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+			     const struct firmware *asic);
+static int check_asic_status(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+	int err;
+
+	DE_INIT(("init_hw() - Layla24\n"));
+	snd_assert((subdevice_id & 0xfff0) == LAYLA24, return -ENODEV);
+
+	if ((err = init_dsp_comm_page(chip))) {
+		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+		return err;
+	}
+
+	chip->device_id = device_id;
+	chip->subdevice_id = subdevice_id;
+	chip->bad_board = TRUE;
+	chip->has_midi = TRUE;
+	chip->dsp_code_to_load = &card_fw[FW_LAYLA24_DSP];
+	chip->input_clock_types =
+		ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
+		ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT;
+	chip->digital_modes =
+		ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+		ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+		ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
+	chip->digital_mode =		DIGITAL_MODE_SPDIF_RCA;
+	chip->professional_spdif = FALSE;
+	chip->digital_in_automute = TRUE;
+
+	if ((err = load_firmware(chip)) < 0)
+		return err;
+	chip->bad_board = FALSE;
+
+	if ((err = init_line_levels(chip)) < 0)
+		return err;
+
+	err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
+	snd_assert(err >= 0, return err);
+	err = set_professional_spdif(chip, TRUE);
+
+	DE_INIT(("init_hw done\n"));
+	return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+	u32 clocks_from_dsp, clock_bits;
+
+	/* Map the DSP clock detect bits to the generic driver clock detect bits */
+	clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+	clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+	if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF)
+		clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+	if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT)
+		clock_bits |= ECHO_CLOCK_BIT_ADAT;
+
+	if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD)
+		clock_bits |= ECHO_CLOCK_BIT_WORD;
+
+	return clock_bits;
+}
+
+
+
+/* Layla24 has an ASIC on the PCI card and another ASIC in the external box;
+both need to be loaded. */
+static int load_asic(struct echoaudio *chip)
+{
+	int err;
+
+	if (chip->asic_loaded)
+		return 1;
+
+	DE_INIT(("load_asic\n"));
+
+	/* Give the DSP a few milliseconds to settle down */
+	mdelay(10);
+
+	/* Load the ASIC for the PCI card */
+	err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC,
+				&card_fw[FW_LAYLA24_1_ASIC]);
+	if (err < 0)
+		return err;
+
+	chip->asic_code = &card_fw[FW_LAYLA24_2S_ASIC];
+
+	/* Now give the new ASIC a little time to set up */
+	mdelay(10);
+
+	/* Do the external one */
+	err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
+				&card_fw[FW_LAYLA24_2S_ASIC]);
+	if (err < 0)
+		return FALSE;
+
+	/* Now give the external ASIC a little time to set up */
+	mdelay(10);
+
+	/* See if it worked */
+	err = check_asic_status(chip);
+
+	/* Set up the control register if the load succeeded -
+	   48 kHz, internal clock, S/PDIF RCA mode */
+	if (!err)
+		err = write_control_reg(chip, GML_CONVERTER_ENABLE | GML_48KHZ,
+					TRUE);
+	
+	DE_INIT(("load_asic() done\n"));
+	return err;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+	u32 control_reg, clock, base_rate;
+
+	snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
+		   return -EINVAL);
+
+	/* Only set the clock for internal mode. */
+	if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+		DE_ACT(("set_sample_rate: Cannot set sample rate - "
+			"clock not set to CLK_CLOCKININTERNAL\n"));
+		/* Save the rate anyhow */
+		chip->comm_page->sample_rate = cpu_to_le32(rate);
+		chip->sample_rate = rate;
+		return 0;
+	}
+
+	/* Get the control register & clear the appropriate bits */
+	control_reg = le32_to_cpu(chip->comm_page->control_register);
+	control_reg &= GML_CLOCK_CLEAR_MASK & GML_SPDIF_RATE_CLEAR_MASK;
+
+	clock = 0;
+
+	switch (rate) {
+	case 96000:
+		clock = GML_96KHZ;
+		break;
+	case 88200:
+		clock = GML_88KHZ;
+		break;
+	case 48000:
+		clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1;
+		break;
+	case 44100:
+		clock = GML_44KHZ;
+		/* Professional mode */
+		if (control_reg & GML_SPDIF_PRO_MODE)
+			clock |= GML_SPDIF_SAMPLE_RATE0;
+		break;
+	case 32000:
+		clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 |
+			GML_SPDIF_SAMPLE_RATE1;
+		break;
+	case 22050:
+		clock = GML_22KHZ;
+		break;
+	case 16000:
+		clock = GML_16KHZ;
+		break;
+	case 11025:
+		clock = GML_11KHZ;
+		break;
+	case 8000:
+		clock = GML_8KHZ;
+		break;
+	default:
+		/* If this is a non-standard rate, then the driver needs to
+		use Layla24's special "continuous frequency" mode */
+		clock = LAYLA24_CONTINUOUS_CLOCK;
+		if (rate > 50000) {
+			base_rate = rate >> 1;
+			control_reg |= GML_DOUBLE_SPEED_MODE;
+		} else {
+			base_rate = rate;
+		}
+
+		if (base_rate < 25000)
+			base_rate = 25000;
+
+		if (wait_handshake(chip))
+			return -EIO;
+
+		chip->comm_page->sample_rate =
+			cpu_to_le32(LAYLA24_MAGIC_NUMBER / base_rate - 2);
+
+		clear_handshake(chip);
+		send_vector(chip, DSP_VC_SET_LAYLA24_FREQUENCY_REG);
+	}
+
+	control_reg |= clock;
+
+	chip->comm_page->sample_rate = cpu_to_le32(rate);	/* ignored by the DSP ? */
+	chip->sample_rate = rate;
+	DE_ACT(("set_sample_rate: %d clock %d\n", rate, control_reg));
+
+	return write_control_reg(chip, control_reg, FALSE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+	u32 control_reg, clocks_from_dsp;
+
+	/* Mask off the clock select bits */
+	control_reg = le32_to_cpu(chip->comm_page->control_register) &
+		GML_CLOCK_CLEAR_MASK;
+	clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+	/* Pick the new clock */
+	switch (clock) {
+	case ECHO_CLOCK_INTERNAL:
+		DE_ACT(("Set Layla24 clock to INTERNAL\n"));
+		chip->input_clock = ECHO_CLOCK_INTERNAL;
+		return set_sample_rate(chip, chip->sample_rate);
+	case ECHO_CLOCK_SPDIF:
+		if (chip->digital_mode == DIGITAL_MODE_ADAT)
+			return -EAGAIN;
+		control_reg |= GML_SPDIF_CLOCK;
+		/* Layla24 doesn't support 96KHz S/PDIF */
+		control_reg &= ~GML_DOUBLE_SPEED_MODE;
+		DE_ACT(("Set Layla24 clock to SPDIF\n"));
+		break;
+	case ECHO_CLOCK_WORD:
+		control_reg |= GML_WORD_CLOCK;
+		if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD96)
+			control_reg |= GML_DOUBLE_SPEED_MODE;
+		else
+			control_reg &= ~GML_DOUBLE_SPEED_MODE;
+		DE_ACT(("Set Layla24 clock to WORD\n"));
+		break;
+	case ECHO_CLOCK_ADAT:
+		if (chip->digital_mode != DIGITAL_MODE_ADAT)
+			return -EAGAIN;
+		control_reg |= GML_ADAT_CLOCK;
+		control_reg &= ~GML_DOUBLE_SPEED_MODE;
+		DE_ACT(("Set Layla24 clock to ADAT\n"));
+		break;
+	default:
+		DE_ACT(("Input clock 0x%x not supported for Layla24\n", clock));
+		return -EINVAL;
+	}
+
+	chip->input_clock = clock;
+	return write_control_reg(chip, control_reg, TRUE);
+}
+
+
+
+/* Depending on what digital mode you want, Layla24 needs different ASICs
+loaded.  This function checks the ASIC needed for the new mode and sees
+if it matches the one already loaded. */
+static int switch_asic(struct echoaudio *chip, const struct firmware *asic)
+{
+	s8 *monitors;
+
+	/*  Check to see if this is already loaded */
+	if (asic != chip->asic_code) {
+		monitors = kmalloc(MONITOR_ARRAY_SIZE, GFP_KERNEL);
+		if (! monitors)
+			return -ENOMEM;
+
+		memcpy(monitors, chip->comm_page->monitors, MONITOR_ARRAY_SIZE);
+		memset(chip->comm_page->monitors, ECHOGAIN_MUTED,
+		       MONITOR_ARRAY_SIZE);
+
+		/* Load the desired ASIC */
+		if (load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
+				      asic) < 0) {
+			memcpy(chip->comm_page->monitors, monitors,
+			       MONITOR_ARRAY_SIZE);
+			kfree(monitors);
+			return -EIO;
+		}
+		chip->asic_code = asic;
+		memcpy(chip->comm_page->monitors, monitors, MONITOR_ARRAY_SIZE);
+		kfree(monitors);
+	}
+
+	return 0;
+}
+
+
+
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+	u32 control_reg;
+	int err, incompatible_clock;
+	const struct firmware *asic;
+
+	/* Set clock to "internal" if it's not compatible with the new mode */
+	incompatible_clock = FALSE;
+	switch (mode) {
+	case DIGITAL_MODE_SPDIF_OPTICAL:
+	case DIGITAL_MODE_SPDIF_RCA:
+		if (chip->input_clock == ECHO_CLOCK_ADAT)
+			incompatible_clock = TRUE;
+		asic = &card_fw[FW_LAYLA24_2S_ASIC];
+		break;
+	case DIGITAL_MODE_ADAT:
+		if (chip->input_clock == ECHO_CLOCK_SPDIF)
+			incompatible_clock = TRUE;
+		asic = &card_fw[FW_LAYLA24_2A_ASIC];
+		break;
+	default:
+		DE_ACT(("Digital mode not supported: %d\n", mode));
+		return -EINVAL;
+	}
+
+	if (incompatible_clock) {	/* Switch to 48KHz, internal */
+		chip->sample_rate = 48000;
+		spin_lock_irq(&chip->lock);
+		set_input_clock(chip, ECHO_CLOCK_INTERNAL);
+		spin_unlock_irq(&chip->lock);
+	}
+
+	/* switch_asic() can sleep */
+	if (switch_asic(chip, asic) < 0)
+		return -EIO;
+
+	spin_lock_irq(&chip->lock);
+
+	/* Tweak the control register */
+	control_reg = le32_to_cpu(chip->comm_page->control_register);
+	control_reg &= GML_DIGITAL_MODE_CLEAR_MASK;
+
+	switch (mode) {
+	case DIGITAL_MODE_SPDIF_OPTICAL:
+		control_reg |= GML_SPDIF_OPTICAL_MODE;
+		break;
+	case DIGITAL_MODE_SPDIF_RCA:
+		/* GML_SPDIF_OPTICAL_MODE bit cleared */
+		break;
+	case DIGITAL_MODE_ADAT:
+		control_reg |= GML_ADAT_MODE;
+		control_reg &= ~GML_DOUBLE_SPEED_MODE;
+		break;
+	}
+
+	err = write_control_reg(chip, control_reg, TRUE);
+	spin_unlock_irq(&chip->lock);
+	if (err < 0)
+		return err;
+	chip->digital_mode = mode;
+
+	DE_ACT(("set_digital_mode to %d\n", mode));
+	return incompatible_clock;
+}
diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c
new file mode 100644
index 000000000000..be40c64263d2
--- /dev/null
+++ b/sound/pci/echoaudio/mia.c
@@ -0,0 +1,117 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHO24_FAMILY
+#define ECHOCARD_MIA
+#define ECHOCARD_NAME "Mia"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_VMIXER
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT	FALSE
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+#define ECHOCARD_HAS_MIDI
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT	0	/* 8 */
+#define PX_DIGITAL_OUT	8	/* 0 */
+#define PX_ANALOG_IN	8	/* 2 */
+#define PX_DIGITAL_IN	10	/* 2 */
+#define PX_NUM		12
+
+/* Bus indexes */
+#define BX_ANALOG_OUT	0	/* 2 */
+#define BX_DIGITAL_OUT	2	/* 2 */
+#define BX_ANALOG_IN	4	/* 2 */
+#define BX_DIGITAL_IN	6	/* 2 */
+#define BX_NUM		8
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER	0
+#define FW_MIA_DSP	1
+
+static const struct firmware card_fw[] = {
+	{0, "loader_dsp.fw"},
+	{0, "mia_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+	{0x1057, 0x3410, 0xECC0, 0x0080, 0, 0, 0},	/* DSP 56361 Mia rev.0 */
+	{0x1057, 0x3410, 0xECC0, 0x0081, 0, 0, 0},	/* DSP 56361 Mia rev.1 */
+	{0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_SYNC_START,
+	.formats =	SNDRV_PCM_FMTBIT_U8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_3LE |
+			SNDRV_PCM_FMTBIT_S32_LE |
+			SNDRV_PCM_FMTBIT_S32_BE,
+	.rates = 	SNDRV_PCM_RATE_32000 |
+			SNDRV_PCM_RATE_44100 |
+			SNDRV_PCM_RATE_48000 |
+			SNDRV_PCM_RATE_88200 |
+			SNDRV_PCM_RATE_96000,
+	.rate_min = 8000,
+	.rate_max = 96000,
+	.channels_min = 1,
+	.channels_max = 8,
+	.buffer_bytes_max = 262144,
+	.period_bytes_min = 32,
+	.period_bytes_max = 131072,
+	.periods_min = 2,
+	.periods_max = 220,
+	/* One page (4k) contains 512 instructions. I don't know if the hw
+	supports lists longer than this. In this case periods_max=220 is a
+	safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "mia_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+#include "midi.c"
diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c
new file mode 100644
index 000000000000..891c70519096
--- /dev/null
+++ b/sound/pci/echoaudio/mia_dsp.c
@@ -0,0 +1,229 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int update_flags(struct echoaudio *chip);
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+			   int gain);
+static int update_vmixer_level(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+	int err;
+
+	DE_INIT(("init_hw() - Mia\n"));
+	snd_assert((subdevice_id & 0xfff0) == MIA, return -ENODEV);
+
+	if ((err = init_dsp_comm_page(chip))) {
+		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+		return err;
+	}
+
+	chip->device_id = device_id;
+	chip->subdevice_id = subdevice_id;
+	chip->bad_board = TRUE;
+	chip->dsp_code_to_load = &card_fw[FW_MIA_DSP];
+	/* Since this card has no ASIC, mark it as loaded so everything
+	   works OK */
+	chip->asic_loaded = TRUE;
+	if ((subdevice_id & 0x0000f) == MIA_MIDI_REV)
+		chip->has_midi = TRUE;
+	chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
+		ECHO_CLOCK_BIT_SPDIF;
+
+	if ((err = load_firmware(chip)) < 0)
+		return err;
+	chip->bad_board = FALSE;
+
+	if ((err = init_line_levels(chip)))
+		return err;
+
+	/* Default routing of the virtual channels: vchannels 0-3 go to analog
+	outputs and vchannels 4-7 go to S/PDIF outputs */
+	set_vmixer_gain(chip, 0, 0, 0);
+	set_vmixer_gain(chip, 1, 1, 0);
+	set_vmixer_gain(chip, 0, 2, 0);
+	set_vmixer_gain(chip, 1, 3, 0);
+	set_vmixer_gain(chip, 2, 4, 0);
+	set_vmixer_gain(chip, 3, 5, 0);
+	set_vmixer_gain(chip, 2, 6, 0);
+	set_vmixer_gain(chip, 3, 7, 0);
+	err = update_vmixer_level(chip);
+
+	DE_INIT(("init_hw done\n"));
+	return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+	u32 clocks_from_dsp, clock_bits;
+
+	/* Map the DSP clock detect bits to the generic driver clock
+	   detect bits */
+	clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+	clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+	if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF)
+		clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+	return clock_bits;
+}
+
+
+
+/* The Mia has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+	return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+	u32 control_reg;
+
+	switch (rate) {
+	case 96000:
+		control_reg = MIA_96000;
+		break;
+	case 88200:
+		control_reg = MIA_88200;
+		break;
+	case 48000:
+		control_reg = MIA_48000;
+		break;
+	case 44100:
+		control_reg = MIA_44100;
+		break;
+	case 32000:
+		control_reg = MIA_32000;
+		break;
+	default:
+		DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+		return -EINVAL;
+	}
+
+	/* Override the clock setting if this Mia is set to S/PDIF clock */
+	if (chip->input_clock == ECHO_CLOCK_SPDIF)
+		control_reg |= MIA_SPDIF;
+
+	/* Set the control register if it has changed */
+	if (control_reg != le32_to_cpu(chip->comm_page->control_register)) {
+		if (wait_handshake(chip))
+			return -EIO;
+
+		chip->comm_page->sample_rate = cpu_to_le32(rate);	/* ignored by the DSP */
+		chip->comm_page->control_register = cpu_to_le32(control_reg);
+		chip->sample_rate = rate;
+
+		clear_handshake(chip);
+		return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+	}
+	return 0;
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+	DE_ACT(("set_input_clock(%d)\n", clock));
+	snd_assert(clock == ECHO_CLOCK_INTERNAL || clock == ECHO_CLOCK_SPDIF,
+		   return -EINVAL);
+
+	chip->input_clock = clock;
+	return set_sample_rate(chip, chip->sample_rate);
+}
+
+
+
+/* This function routes the sound from a virtual channel to a real output */
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+			   int gain)
+{
+	int index;
+
+	snd_assert(pipe < num_pipes_out(chip) &&
+		   output < num_busses_out(chip), return -EINVAL);
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	chip->vmixer_gain[output][pipe] = gain;
+	index = output * num_pipes_out(chip) + pipe;
+	chip->comm_page->vmixer[index] = gain;
+
+	DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+	return 0;
+}
+
+
+
+/* Tell the DSP to read and update virtual mixer levels in comm page. */
+static int update_vmixer_level(struct echoaudio *chip)
+{
+	if (wait_handshake(chip))
+		return -EIO;
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_SET_VMIXER_GAIN);
+}
+
+
+
+/* Tell the DSP to reread the flags from the comm page */
+static int update_flags(struct echoaudio *chip)
+{
+	if (wait_handshake(chip))
+		return -EIO;
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+	DE_ACT(("set_professional_spdif %d\n", prof));
+	if (prof)
+		chip->comm_page->flags |=
+			__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+	else
+		chip->comm_page->flags &=
+			~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+	chip->professional_spdif = prof;
+	return update_flags(chip);
+}
+
diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c
new file mode 100644
index 000000000000..e31f0f11e3a8
--- /dev/null
+++ b/sound/pci/echoaudio/midi.c
@@ -0,0 +1,327 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+/******************************************************************************
+	MIDI lowlevel code
+******************************************************************************/
+
+/* Start and stop Midi input */
+static int enable_midi_input(struct echoaudio *chip, char enable)
+{
+	DE_MID(("enable_midi_input(%d)\n", enable));
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	if (enable) {
+		chip->mtc_state = MIDI_IN_STATE_NORMAL;
+		chip->comm_page->flags |=
+			__constant_cpu_to_le32(DSP_FLAG_MIDI_INPUT);
+	} else
+		chip->comm_page->flags &=
+			~__constant_cpu_to_le32(DSP_FLAG_MIDI_INPUT);
+
+	clear_handshake(chip);
+	return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+/* Send a buffer full of MIDI data to the DSP
+Returns how many actually written or < 0 on error */
+static int write_midi(struct echoaudio *chip, u8 *data, int bytes)
+{
+	snd_assert(bytes > 0 && bytes < MIDI_OUT_BUFFER_SIZE, return -EINVAL);
+
+	if (wait_handshake(chip))
+		return -EIO;
+
+	/* HF4 indicates that it is safe to write MIDI output data */
+	if (! (get_dsp_register(chip, CHI32_STATUS_REG) & CHI32_STATUS_REG_HF4))
+		return 0;
+
+	chip->comm_page->midi_output[0] = bytes;
+	memcpy(&chip->comm_page->midi_output[1], data, bytes);
+	chip->comm_page->midi_out_free_count = 0;
+	clear_handshake(chip);
+	send_vector(chip, DSP_VC_MIDI_WRITE);
+	DE_MID(("write_midi: %d\n", bytes));
+	return bytes;
+}
+
+
+
+/* Run the state machine for MIDI input data
+MIDI time code sync isn't supported by this code right now, but you still need
+this state machine to parse the incoming MIDI data stream.  Every time the DSP
+sees a 0xF1 byte come in, it adds the DSP sample position to the MIDI data
+stream. The DSP sample position is represented as a 32 bit unsigned value,
+with the high 16 bits first, followed by the low 16 bits. Since these aren't
+real MIDI bytes, the following logic is needed to skip them. */
+static inline int mtc_process_data(struct echoaudio *chip, short midi_byte)
+{
+	switch (chip->mtc_state) {
+	case MIDI_IN_STATE_NORMAL:
+		if (midi_byte == 0xF1)
+			chip->mtc_state = MIDI_IN_STATE_TS_HIGH;
+		break;
+	case MIDI_IN_STATE_TS_HIGH:
+		chip->mtc_state = MIDI_IN_STATE_TS_LOW;
+		return MIDI_IN_SKIP_DATA;
+		break;
+	case MIDI_IN_STATE_TS_LOW:
+		chip->mtc_state = MIDI_IN_STATE_F1_DATA;
+		return MIDI_IN_SKIP_DATA;
+		break;
+	case MIDI_IN_STATE_F1_DATA:
+		chip->mtc_state = MIDI_IN_STATE_NORMAL;
+		break;
+	}
+	return 0;
+}
+
+
+
+/* This function is called from the IRQ handler and it reads the midi data
+from the DSP's buffer.  It returns the number of bytes received. */
+static int midi_service_irq(struct echoaudio *chip)
+{
+	short int count, midi_byte, i, received;
+
+	/* The count is at index 0, followed by actual data */
+	count = le16_to_cpu(chip->comm_page->midi_input[0]);
+
+	snd_assert(count < MIDI_IN_BUFFER_SIZE, return 0);
+
+	/* Get the MIDI data from the comm page */
+	i = 1;
+	received = 0;
+	for (i = 1; i <= count; i++) {
+		/* Get the MIDI byte */
+		midi_byte = le16_to_cpu(chip->comm_page->midi_input[i]);
+
+		/* Parse the incoming MIDI stream. The incoming MIDI data
+		consists of MIDI bytes and timestamps for the MIDI time code
+		0xF1 bytes. mtc_process_data() is a little state machine that
+		parses the stream. If you get MIDI_IN_SKIP_DATA back, then
+		this is a timestamp byte, not a MIDI byte, so don't store it
+		in the MIDI input buffer. */
+		if (mtc_process_data(chip, midi_byte) == MIDI_IN_SKIP_DATA)
+			continue;
+
+		chip->midi_buffer[received++] = (u8)midi_byte;
+	}
+
+	return received;
+}
+
+
+
+
+/******************************************************************************
+	MIDI interface
+******************************************************************************/
+
+static int snd_echo_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+	struct echoaudio *chip = substream->rmidi->private_data;
+
+	chip->midi_in = substream;
+	DE_MID(("rawmidi_iopen\n"));
+	return 0;
+}
+
+
+
+static void snd_echo_midi_input_trigger(struct snd_rawmidi_substream *substream,
+					int up)
+{
+	struct echoaudio *chip = substream->rmidi->private_data;
+
+	if (up != chip->midi_input_enabled) {
+		spin_lock_irq(&chip->lock);
+		enable_midi_input(chip, up);
+		spin_unlock_irq(&chip->lock);
+		chip->midi_input_enabled = up;
+	}
+}
+
+
+
+static int snd_echo_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+	struct echoaudio *chip = substream->rmidi->private_data;
+
+	chip->midi_in = NULL;
+	DE_MID(("rawmidi_iclose\n"));
+	return 0;
+}
+
+
+
+static int snd_echo_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+	struct echoaudio *chip = substream->rmidi->private_data;
+
+	chip->tinuse = 0;
+	chip->midi_full = 0;
+	chip->midi_out = substream;
+	DE_MID(("rawmidi_oopen\n"));
+	return 0;
+}
+
+
+
+static void snd_echo_midi_output_write(unsigned long data)
+{
+	struct echoaudio *chip = (struct echoaudio *)data;
+	unsigned long flags;
+	int bytes, sent, time;
+	unsigned char buf[MIDI_OUT_BUFFER_SIZE - 1];
+
+	DE_MID(("snd_echo_midi_output_write\n"));
+	/* No interrupts are involved: we have to check at regular intervals
+	if the card's output buffer has room for new data. */
+	sent = bytes = 0;
+	spin_lock_irqsave(&chip->lock, flags);
+	chip->midi_full = 0;
+	if (chip->midi_out && !snd_rawmidi_transmit_empty(chip->midi_out)) {
+		bytes = snd_rawmidi_transmit_peek(chip->midi_out, buf,
+						  MIDI_OUT_BUFFER_SIZE - 1);
+		DE_MID(("Try to send %d bytes...\n", bytes));
+		sent = write_midi(chip, buf, bytes);
+		if (sent < 0) {
+			snd_printk(KERN_ERR "write_midi() error %d\n", sent);
+			/* retry later */
+			sent = 9000;
+			chip->midi_full = 1;
+		} else if (sent > 0) {
+			DE_MID(("%d bytes sent\n", sent));
+			snd_rawmidi_transmit_ack(chip->midi_out, sent);
+		} else {
+			/* Buffer is full. DSP's internal buffer is 64 (128 ?)
+			bytes long. Let's wait until half of them are sent */
+			DE_MID(("Full\n"));
+			sent = 32;
+			chip->midi_full = 1;
+		}
+	}
+
+	/* We restart the timer only if there is some data left to send */
+	if (!snd_rawmidi_transmit_empty(chip->midi_out) && chip->tinuse) {
+		/* The timer will expire slightly after the data has been
+		   sent */
+		time = (sent << 3) / 25 + 1;	/* 8/25=0.32ms to send a byte */
+		mod_timer(&chip->timer, jiffies + (time * HZ + 999) / 1000);
+		DE_MID(("Timer armed(%d)\n", ((time * HZ + 999) / 1000)));
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+
+static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream,
+					 int up)
+{
+	struct echoaudio *chip = substream->rmidi->private_data;
+
+	DE_MID(("snd_echo_midi_output_trigger(%d)\n", up));
+	spin_lock_irq(&chip->lock);
+	if (up) {
+		if (!chip->tinuse) {
+			init_timer(&chip->timer);
+			chip->timer.function = snd_echo_midi_output_write;
+			chip->timer.data = (unsigned long)chip;
+			chip->tinuse = 1;
+		}
+	} else {
+		if (chip->tinuse) {
+			del_timer(&chip->timer);
+			chip->tinuse = 0;
+			DE_MID(("Timer removed\n"));
+		}
+	}
+	spin_unlock_irq(&chip->lock);
+
+	if (up && !chip->midi_full)
+		snd_echo_midi_output_write((unsigned long)chip);
+}
+
+
+
+static int snd_echo_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+	struct echoaudio *chip = substream->rmidi->private_data;
+
+	chip->midi_out = NULL;
+	DE_MID(("rawmidi_oclose\n"));
+	return 0;
+}
+
+
+
+static struct snd_rawmidi_ops snd_echo_midi_input = {
+	.open = snd_echo_midi_input_open,
+	.close = snd_echo_midi_input_close,
+	.trigger = snd_echo_midi_input_trigger,
+};
+
+static struct snd_rawmidi_ops snd_echo_midi_output = {
+	.open = snd_echo_midi_output_open,
+	.close = snd_echo_midi_output_close,
+	.trigger = snd_echo_midi_output_trigger,
+};
+
+
+
+/* <--snd_echo_probe() */
+static int __devinit snd_echo_midi_create(struct snd_card *card,
+					  struct echoaudio *chip)
+{
+	int err;
+
+	if ((err = snd_rawmidi_new(card, card->shortname, 0, 1, 1,
+				   &chip->rmidi)) < 0)
+		return err;
+
+	strcpy(chip->rmidi->name, card->shortname);
+	chip->rmidi->private_data = chip;
+
+	snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+			    &snd_echo_midi_input);
+	snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+			    &snd_echo_midi_output);
+
+	chip->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+		SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+	DE_INIT(("MIDI ok\n"));
+	return 0;
+}
diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c
new file mode 100644
index 000000000000..5dc512add372
--- /dev/null
+++ b/sound/pci/echoaudio/mona.c
@@ -0,0 +1,129 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHO24_FAMILY
+#define ECHOCARD_MONA
+#define ECHOCARD_NAME "Mona"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT	6
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT	0	/* 6 */
+#define PX_DIGITAL_OUT	6	/* 8 */
+#define PX_ANALOG_IN	14	/* 4 */
+#define PX_DIGITAL_IN	18	/* 8 */
+#define PX_NUM		26
+
+/* Bus indexes */
+#define BX_ANALOG_OUT	0	/* 6 */
+#define BX_DIGITAL_OUT	6	/* 8 */
+#define BX_ANALOG_IN	14	/* 4 */
+#define BX_DIGITAL_IN	18	/* 8 */
+#define BX_NUM		26
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER		0
+#define FW_MONA_301_DSP		1
+#define FW_MONA_361_DSP		2
+#define FW_MONA_301_1_ASIC48	3
+#define FW_MONA_301_1_ASIC96	4
+#define FW_MONA_361_1_ASIC48	5
+#define FW_MONA_361_1_ASIC96	6
+#define FW_MONA_2_ASIC		7
+
+static const struct firmware card_fw[] = {
+	{0, "loader_dsp.fw"},
+	{0, "mona_301_dsp.fw"},
+	{0, "mona_361_dsp.fw"},
+	{0, "mona_301_1_asic_48.fw"},
+	{0, "mona_301_1_asic_96.fw"},
+	{0, "mona_361_1_asic_48.fw"},
+	{0, "mona_361_1_asic_96.fw"},
+	{0, "mona_2_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+	{0x1057, 0x1801, 0xECC0, 0x0070, 0, 0, 0},	/* DSP 56301 Mona rev.0 */
+	{0x1057, 0x1801, 0xECC0, 0x0071, 0, 0, 0},	/* DSP 56301 Mona rev.1 */
+	{0x1057, 0x1801, 0xECC0, 0x0072, 0, 0, 0},	/* DSP 56301 Mona rev.2 */
+	{0x1057, 0x3410, 0xECC0, 0x0070, 0, 0, 0},	/* DSP 56361 Mona rev.0 */
+	{0x1057, 0x3410, 0xECC0, 0x0071, 0, 0, 0},	/* DSP 56361 Mona rev.1 */
+	{0x1057, 0x3410, 0xECC0, 0x0072, 0, 0, 0},	/* DSP 56361 Mona rev.2 */
+	{0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_SYNC_START,
+	.formats =	SNDRV_PCM_FMTBIT_U8 |
+			SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_3LE |
+			SNDRV_PCM_FMTBIT_S32_LE |
+			SNDRV_PCM_FMTBIT_S32_BE,
+	.rates = 	SNDRV_PCM_RATE_8000_48000 |
+			SNDRV_PCM_RATE_88200 |
+			SNDRV_PCM_RATE_96000,
+	.rate_min = 8000,
+	.rate_max = 96000,
+	.channels_min = 1,
+	.channels_max = 8,
+	.buffer_bytes_max = 262144,
+	.period_bytes_min = 32,
+	.period_bytes_max = 131072,
+	.periods_min = 2,
+	.periods_max = 220,
+	/* One page (4k) contains 512 instructions. I don't know if the hw
+	supports lists longer than this. In this case periods_max=220 is a
+	safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "mona_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio_gml.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c
new file mode 100644
index 000000000000..c0b4bf0be7d1
--- /dev/null
+++ b/sound/pci/echoaudio/mona_dsp.c
@@ -0,0 +1,428 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int write_control_reg(struct echoaudio *chip, u32 value, char force);
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int set_digital_mode(struct echoaudio *chip, u8 mode);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+			     const struct firmware *asic);
+static int check_asic_status(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+	int err;
+
+	DE_INIT(("init_hw() - Mona\n"));
+	snd_assert((subdevice_id & 0xfff0) == MONA, return -ENODEV);
+
+	if ((err = init_dsp_comm_page(chip))) {
+		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+		return err;
+	}
+
+	chip->device_id = device_id;
+	chip->subdevice_id = subdevice_id;
+	chip->bad_board = TRUE;
+	chip->input_clock_types =
+		ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
+		ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT;
+	chip->digital_modes =
+		ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+		ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+		ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
+
+	/* Mona comes in both '301 and '361 flavors */
+	if (chip->device_id == DEVICE_ID_56361)
+		chip->dsp_code_to_load = &card_fw[FW_MONA_361_DSP];
+	else
+		chip->dsp_code_to_load = &card_fw[FW_MONA_301_DSP];
+
+	chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
+	chip->professional_spdif = FALSE;
+	chip->digital_in_automute = TRUE;
+
+	if ((err = load_firmware(chip)) < 0)
+		return err;
+	chip->bad_board = FALSE;
+
+	if ((err = init_line_levels(chip)) < 0)
+		return err;
+
+	err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
+	snd_assert(err >= 0, return err);
+	err = set_professional_spdif(chip, TRUE);
+
+	DE_INIT(("init_hw done\n"));
+	return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+	u32 clocks_from_dsp, clock_bits;
+
+	/* Map the DSP clock detect bits to the generic driver clock
+	   detect bits */
+	clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+	clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+	if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF)
+		clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+	if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT)
+		clock_bits |= ECHO_CLOCK_BIT_ADAT;
+
+	if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD)
+		clock_bits |= ECHO_CLOCK_BIT_WORD;
+
+	return clock_bits;
+}
+
+
+
+/* Mona has an ASIC on the PCI card and another ASIC in the external box; 
+both need to be loaded. */
+static int load_asic(struct echoaudio *chip)
+{
+	u32 control_reg;
+	int err;
+	const struct firmware *asic;
+
+	if (chip->asic_loaded)
+		return 0;
+
+	mdelay(10);
+
+	if (chip->device_id == DEVICE_ID_56361)
+		asic = &card_fw[FW_MONA_361_1_ASIC48];
+	else
+		asic = &card_fw[FW_MONA_301_1_ASIC48];
+
+	err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC, asic);
+	if (err < 0)
+		return err;
+
+	chip->asic_code = asic;
+	mdelay(10);
+
+	/* Do the external one */
+	err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_EXTERNAL_ASIC,
+				&card_fw[FW_MONA_2_ASIC]);
+	if (err < 0)
+		return err;
+
+	mdelay(10);
+	err = check_asic_status(chip);
+
+	/* Set up the control register if the load succeeded -
+	   48 kHz, internal clock, S/PDIF RCA mode */
+	if (!err) {
+		control_reg = GML_CONVERTER_ENABLE | GML_48KHZ;
+		err = write_control_reg(chip, control_reg, TRUE);
+	}
+
+	return err;
+}
+
+
+
+/* Depending on what digital mode you want, Mona needs different ASICs
+loaded.  This function checks the ASIC needed for the new mode and sees
+if it matches the one already loaded. */
+static int switch_asic(struct echoaudio *chip, char double_speed)
+{
+	const struct firmware *asic;
+	int err;
+
+	/* Check the clock detect bits to see if this is
+	a single-speed clock or a double-speed clock; load
+	a new ASIC if necessary. */
+	if (chip->device_id == DEVICE_ID_56361) {
+		if (double_speed)
+			asic = &card_fw[FW_MONA_361_1_ASIC96];
+		else
+			asic = &card_fw[FW_MONA_361_1_ASIC48];
+	} else {
+		if (double_speed)
+			asic = &card_fw[FW_MONA_301_1_ASIC96];
+		else
+			asic = &card_fw[FW_MONA_301_1_ASIC48];
+	}
+
+	if (asic != chip->asic_code) {
+		/* Load the desired ASIC */
+		err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC,
+					asic);
+		if (err < 0)
+			return err;
+		chip->asic_code = asic;
+	}
+
+	return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+	u32 control_reg, clock;
+	const struct firmware *asic;
+	char force_write;
+
+	/* Only set the clock for internal mode. */
+	if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+		DE_ACT(("set_sample_rate: Cannot set sample rate - "
+			"clock not set to CLK_CLOCKININTERNAL\n"));
+		/* Save the rate anyhow */
+		chip->comm_page->sample_rate = cpu_to_le32(rate);
+		chip->sample_rate = rate;
+		return 0;
+	}
+
+	/* Now, check to see if the required ASIC is loaded */
+	if (rate >= 88200) {
+		if (chip->digital_mode == DIGITAL_MODE_ADAT)
+			return -EINVAL;
+		if (chip->device_id == DEVICE_ID_56361)
+			asic = &card_fw[FW_MONA_361_1_ASIC96];
+		else
+			asic = &card_fw[FW_MONA_301_1_ASIC96];
+	} else {
+		if (chip->device_id == DEVICE_ID_56361)
+			asic = &card_fw[FW_MONA_361_1_ASIC48];
+		else
+			asic = &card_fw[FW_MONA_301_1_ASIC48];
+	}
+
+	force_write = 0;
+	if (asic != chip->asic_code) {
+		int err;
+		/* Load the desired ASIC (load_asic_generic() can sleep) */
+		spin_unlock_irq(&chip->lock);
+		err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC,
+					asic);
+		spin_lock_irq(&chip->lock);
+
+		if (err < 0)
+			return err;
+		chip->asic_code = asic;
+		force_write = 1;
+	}
+
+	/* Compute the new control register value */
+	clock = 0;
+	control_reg = le32_to_cpu(chip->comm_page->control_register);
+	control_reg &= GML_CLOCK_CLEAR_MASK;
+	control_reg &= GML_SPDIF_RATE_CLEAR_MASK;
+
+	switch (rate) {
+	case 96000:
+		clock = GML_96KHZ;
+		break;
+	case 88200:
+		clock = GML_88KHZ;
+		break;
+	case 48000:
+		clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1;
+		break;
+	case 44100:
+		clock = GML_44KHZ;
+		/* Professional mode */
+		if (control_reg & GML_SPDIF_PRO_MODE)
+			clock |= GML_SPDIF_SAMPLE_RATE0;
+		break;
+	case 32000:
+		clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 |
+			GML_SPDIF_SAMPLE_RATE1;
+		break;
+	case 22050:
+		clock = GML_22KHZ;
+		break;
+	case 16000:
+		clock = GML_16KHZ;
+		break;
+	case 11025:
+		clock = GML_11KHZ;
+		break;
+	case 8000:
+		clock = GML_8KHZ;
+		break;
+	default:
+		DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+		return -EINVAL;
+	}
+
+	control_reg |= clock;
+
+	chip->comm_page->sample_rate = cpu_to_le32(rate);	/* ignored by the DSP */
+	chip->sample_rate = rate;
+	DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+
+	return write_control_reg(chip, control_reg, force_write);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+	u32 control_reg, clocks_from_dsp;
+	int err;
+
+	DE_ACT(("set_input_clock:\n"));
+
+	/* Prevent two simultaneous calls to switch_asic() */
+	if (atomic_read(&chip->opencount))
+		return -EAGAIN;
+
+	/* Mask off the clock select bits */
+	control_reg = le32_to_cpu(chip->comm_page->control_register) &
+		GML_CLOCK_CLEAR_MASK;
+	clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+	switch (clock) {
+	case ECHO_CLOCK_INTERNAL:
+		DE_ACT(("Set Mona clock to INTERNAL\n"));
+		chip->input_clock = ECHO_CLOCK_INTERNAL;
+		return set_sample_rate(chip, chip->sample_rate);
+	case ECHO_CLOCK_SPDIF:
+		if (chip->digital_mode == DIGITAL_MODE_ADAT)
+			return -EAGAIN;
+		spin_unlock_irq(&chip->lock);
+		err = switch_asic(chip, clocks_from_dsp &
+				  GML_CLOCK_DETECT_BIT_SPDIF96);
+		spin_lock_irq(&chip->lock);
+		if (err < 0)
+			return err;
+		DE_ACT(("Set Mona clock to SPDIF\n"));
+		control_reg |= GML_SPDIF_CLOCK;
+		if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96)
+			control_reg |= GML_DOUBLE_SPEED_MODE;
+		else
+			control_reg &= ~GML_DOUBLE_SPEED_MODE;
+		break;
+	case ECHO_CLOCK_WORD:
+		DE_ACT(("Set Mona clock to WORD\n"));
+		spin_unlock_irq(&chip->lock);
+		err = switch_asic(chip, clocks_from_dsp &
+				  GML_CLOCK_DETECT_BIT_WORD96);
+		spin_lock_irq(&chip->lock);
+		if (err < 0)
+			return err;
+		control_reg |= GML_WORD_CLOCK;
+		if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD96)
+			control_reg |= GML_DOUBLE_SPEED_MODE;
+		else
+			control_reg &= ~GML_DOUBLE_SPEED_MODE;
+		break;
+	case ECHO_CLOCK_ADAT:
+		DE_ACT(("Set Mona clock to ADAT\n"));
+		if (chip->digital_mode != DIGITAL_MODE_ADAT)
+			return -EAGAIN;
+		control_reg |= GML_ADAT_CLOCK;
+		control_reg &= ~GML_DOUBLE_SPEED_MODE;
+		break;
+	default:
+		DE_ACT(("Input clock 0x%x not supported for Mona\n", clock));
+		return -EINVAL;
+	}
+
+	chip->input_clock = clock;
+	return write_control_reg(chip, control_reg, TRUE);
+}
+
+
+
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+	u32 control_reg;
+	int err, incompatible_clock;
+
+	/* Set clock to "internal" if it's not compatible with the new mode */
+	incompatible_clock = FALSE;
+	switch (mode) {
+	case DIGITAL_MODE_SPDIF_OPTICAL:
+	case DIGITAL_MODE_SPDIF_RCA:
+		if (chip->input_clock == ECHO_CLOCK_ADAT)
+			incompatible_clock = TRUE;
+		break;
+	case DIGITAL_MODE_ADAT:
+		if (chip->input_clock == ECHO_CLOCK_SPDIF)
+			incompatible_clock = TRUE;
+		break;
+	default:
+		DE_ACT(("Digital mode not supported: %d\n", mode));
+		return -EINVAL;
+	}
+
+	spin_lock_irq(&chip->lock);
+
+	if (incompatible_clock) {	/* Switch to 48KHz, internal */
+		chip->sample_rate = 48000;
+		set_input_clock(chip, ECHO_CLOCK_INTERNAL);
+	}
+
+	/* Clear the current digital mode */
+	control_reg = le32_to_cpu(chip->comm_page->control_register);
+	control_reg &= GML_DIGITAL_MODE_CLEAR_MASK;
+
+	/* Tweak the control reg */
+	switch (mode) {
+	case DIGITAL_MODE_SPDIF_OPTICAL:
+		control_reg |= GML_SPDIF_OPTICAL_MODE;
+		break;
+	case DIGITAL_MODE_SPDIF_RCA:
+		/* GML_SPDIF_OPTICAL_MODE bit cleared */
+		break;
+	case DIGITAL_MODE_ADAT:
+		/* If the current ASIC is the 96KHz ASIC, switch the ASIC
+		   and set to 48 KHz */
+		if (chip->asic_code == &card_fw[FW_MONA_361_1_ASIC96] ||
+		    chip->asic_code == &card_fw[FW_MONA_301_1_ASIC96]) {
+			set_sample_rate(chip, 48000);
+		}
+		control_reg |= GML_ADAT_MODE;
+		control_reg &= ~GML_DOUBLE_SPEED_MODE;
+		break;
+	}
+
+	err = write_control_reg(chip, control_reg, FALSE);
+	spin_unlock_irq(&chip->lock);
+	if (err < 0)
+		return err;
+	chip->digital_mode = mode;
+
+	DE_ACT(("set_digital_mode to %d\n", mode));
+	return incompatible_clock;
+}
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 8c2a8174ece1..23201f3eeb12 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -408,7 +408,9 @@ static const struct hda_codec_preset *find_codec_preset(struct hda_codec *codec)
 			u32 mask = preset->mask;
 			if (! mask)
 				mask = ~0;
-			if (preset->id == (codec->vendor_id & mask))
+			if (preset->id == (codec->vendor_id & mask) &&
+			    (! preset->rev ||
+			     preset->rev == codec->revision_id))
 				return preset;
 		}
 	}
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index dd4e00a82b55..33b7d5806469 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -799,6 +799,8 @@ static struct hda_board_config ad1986a_cfg_tbl[] = {
 	{ .pci_subvendor = 0x1043, .pci_subdevice = 0x818f,
 	  .config = AD1986A_LAPTOP }, /* ASUS P5GV-MX */
 	{ .modelname = "laptop-eapd",	.config = AD1986A_LAPTOP_EAPD },
+	{ .pci_subvendor = 0x144d, .pci_subdevice = 0xc023,
+	  .config = AD1986A_LAPTOP_EAPD }, /* Samsung X60 Chane */
 	{ .pci_subvendor = 0x144d, .pci_subdevice = 0xc024,
 	  .config = AD1986A_LAPTOP_EAPD }, /* Samsung R65-T2300 Charis */
 	{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1153,
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 98b9f16c26ff..18d105263fea 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -78,6 +78,7 @@ enum {
 enum {
 	ALC262_BASIC,
 	ALC262_FUJITSU,
+	ALC262_HP_BPC,
 	ALC262_AUTO,
 	ALC262_MODEL_LAST /* last tag */
 };
@@ -85,6 +86,7 @@ enum {
 /* ALC861 models */
 enum {
 	ALC861_3ST,
+	ALC660_3ST,
 	ALC861_3ST_DIG,
 	ALC861_6ST_DIG,
 	ALC861_AUTO,
@@ -99,6 +101,17 @@ enum {
 	ALC882_MODEL_LAST,
 };
 
+/* ALC883 models */
+enum {
+	ALC883_3ST_2ch_DIG,
+	ALC883_3ST_6ch_DIG,
+	ALC883_3ST_6ch,
+	ALC883_6ST_DIG,
+	ALC888_DEMO_BOARD,
+	ALC883_AUTO,
+	ALC883_MODEL_LAST,
+};
+
 /* for GPIO Poll */
 #define GPIO_MASK	0x03
 
@@ -108,7 +121,8 @@ struct alc_spec {
 	unsigned int num_mixers;
 
 	const struct hda_verb *init_verbs[5];	/* initialization verbs
-						 * don't forget NULL termination!
+						 * don't forget NULL
+						 * termination!
 						 */
 	unsigned int num_init_verbs;
 
@@ -163,7 +177,9 @@ struct alc_spec {
  * configuration template - to be copied to the spec instance
  */
 struct alc_config_preset {
-	struct snd_kcontrol_new *mixers[5]; /* should be identical size with spec */
+	struct snd_kcontrol_new *mixers[5]; /* should be identical size
+					     * with spec
+					     */
 	const struct hda_verb *init_verbs[5];
 	unsigned int num_dacs;
 	hda_nid_t *dac_nids;
@@ -184,7 +200,8 @@ struct alc_config_preset {
 /*
  * input MUX handling
  */
-static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int alc_mux_enum_info(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_info *uinfo)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
@@ -194,7 +211,8 @@ static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
 	return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo);
 }
 
-static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_mux_enum_get(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
@@ -204,21 +222,24 @@ static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
 	return 0;
 }
 
-static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
 	return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol,
-				     spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
+				     spec->adc_nids[adc_idx],
+				     &spec->cur_mux[adc_idx]);
 }
 
 
 /*
  * channel mode setting
  */
-static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int alc_ch_mode_info(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_info *uinfo)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
@@ -226,20 +247,24 @@ static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_i
 				    spec->num_channel_mode);
 }
 
-static int alc_ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_ch_mode_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
 	return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
-				   spec->num_channel_mode, spec->multiout.max_channels);
+				   spec->num_channel_mode,
+				   spec->multiout.max_channels);
 }
 
-static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_ch_mode_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
 	return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
-				   spec->num_channel_mode, &spec->multiout.max_channels);
+				   spec->num_channel_mode,
+				   &spec->multiout.max_channels);
 }
 
 /*
@@ -290,7 +315,8 @@ static signed char alc_pin_mode_dir_info[5][2] = {
 #define alc_pin_mode_n_items(_dir) \
 	(alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1)
 
-static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int alc_pin_mode_info(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_info *uinfo)
 {
 	unsigned int item_num = uinfo->value.enumerated.item;
 	unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
@@ -305,40 +331,46 @@ static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
 	return 0;
 }
 
-static int alc_pin_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_pin_mode_get(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
 {
 	unsigned int i;
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	hda_nid_t nid = kcontrol->private_value & 0xffff;
 	unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
 	long *valp = ucontrol->value.integer.value;
-	unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00);
+	unsigned int pinctl = snd_hda_codec_read(codec, nid, 0,
+						 AC_VERB_GET_PIN_WIDGET_CONTROL,
+						 0x00);
 
 	/* Find enumerated value for current pinctl setting */
 	i = alc_pin_mode_min(dir);
-	while (alc_pin_mode_values[i]!=pinctl && i<=alc_pin_mode_max(dir))
+	while (alc_pin_mode_values[i] != pinctl && i <= alc_pin_mode_max(dir))
 		i++;
-	*valp = i<=alc_pin_mode_max(dir)?i:alc_pin_mode_min(dir);
+	*valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir);
 	return 0;
 }
 
-static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_pin_mode_put(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
 {
 	signed int change;
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	hda_nid_t nid = kcontrol->private_value & 0xffff;
 	unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
 	long val = *ucontrol->value.integer.value;
-	unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00);
+	unsigned int pinctl = snd_hda_codec_read(codec, nid, 0,
+						 AC_VERB_GET_PIN_WIDGET_CONTROL,
+						 0x00);
 
-	if (val<alc_pin_mode_min(dir) || val>alc_pin_mode_max(dir)) 
+	if (val < alc_pin_mode_min(dir) || val > alc_pin_mode_max(dir)) 
 		val = alc_pin_mode_min(dir);
 
 	change = pinctl != alc_pin_mode_values[val];
 	if (change) {
 		/* Set pin mode to that requested */
 		snd_hda_codec_write(codec,nid,0,AC_VERB_SET_PIN_WIDGET_CONTROL,
-			alc_pin_mode_values[val]);
+				    alc_pin_mode_values[val]);
 
 		/* Also enable the retasking pin's input/output as required 
 		 * for the requested pin mode.  Enum values of 2 or less are
@@ -351,15 +383,19 @@ static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
 		 * this turns out to be necessary in the future.
 		 */
 		if (val <= 2) {
-			snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE,
-				AMP_OUT_MUTE);
-			snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE,
-				AMP_IN_UNMUTE(0));
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_OUT_MUTE);
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_IN_UNMUTE(0));
 		} else {
-			snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE,
-				AMP_IN_MUTE(0));
-			snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE,
-				AMP_OUT_UNMUTE);
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_IN_MUTE(0));
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_OUT_UNMUTE);
 		}
 	}
 	return change;
@@ -378,7 +414,8 @@ static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
  * needed for any "production" models.
  */
 #ifdef CONFIG_SND_DEBUG
-static int alc_gpio_data_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int alc_gpio_data_info(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_info *uinfo)
 {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
 	uinfo->count = 1;
@@ -386,33 +423,38 @@ static int alc_gpio_data_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
 	uinfo->value.integer.max = 1;
 	return 0;
 }                                
-static int alc_gpio_data_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_gpio_data_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	hda_nid_t nid = kcontrol->private_value & 0xffff;
 	unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
 	long *valp = ucontrol->value.integer.value;
-	unsigned int val = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_GPIO_DATA,0x00);
+	unsigned int val = snd_hda_codec_read(codec, nid, 0,
+					      AC_VERB_GET_GPIO_DATA, 0x00);
 
 	*valp = (val & mask) != 0;
 	return 0;
 }
-static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_gpio_data_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
 {
 	signed int change;
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	hda_nid_t nid = kcontrol->private_value & 0xffff;
 	unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
 	long val = *ucontrol->value.integer.value;
-	unsigned int gpio_data = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_GPIO_DATA,0x00);
+	unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0,
+						    AC_VERB_GET_GPIO_DATA,
+						    0x00);
 
 	/* Set/unset the masked GPIO bit(s) as needed */
-	change = (val==0?0:mask) != (gpio_data & mask);
-	if (val==0)
+	change = (val == 0 ? 0 : mask) != (gpio_data & mask);
+	if (val == 0)
 		gpio_data &= ~mask;
 	else
 		gpio_data |= mask;
-	snd_hda_codec_write(codec,nid,0,AC_VERB_SET_GPIO_DATA,gpio_data);
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_GPIO_DATA, gpio_data);
 
 	return change;
 }
@@ -432,7 +474,8 @@ static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
  * necessary.
  */
 #ifdef CONFIG_SND_DEBUG
-static int alc_spdif_ctrl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int alc_spdif_ctrl_info(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *uinfo)
 {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
 	uinfo->count = 1;
@@ -440,33 +483,39 @@ static int alc_spdif_ctrl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
 	uinfo->value.integer.max = 1;
 	return 0;
 }                                
-static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	hda_nid_t nid = kcontrol->private_value & 0xffff;
 	unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
 	long *valp = ucontrol->value.integer.value;
-	unsigned int val = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_DIGI_CONVERT,0x00);
+	unsigned int val = snd_hda_codec_read(codec, nid, 0,
+					      AC_VERB_GET_DIGI_CONVERT, 0x00);
 
 	*valp = (val & mask) != 0;
 	return 0;
 }
-static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
 {
 	signed int change;
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	hda_nid_t nid = kcontrol->private_value & 0xffff;
 	unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
 	long val = *ucontrol->value.integer.value;
-	unsigned int ctrl_data = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_DIGI_CONVERT,0x00);
+	unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
+						    AC_VERB_GET_DIGI_CONVERT,
+						    0x00);
 
 	/* Set/unset the masked control bit(s) as needed */
-	change = (val==0?0:mask) != (ctrl_data & mask);
+	change = (val == 0 ? 0 : mask) != (ctrl_data & mask);
 	if (val==0)
 		ctrl_data &= ~mask;
 	else
 		ctrl_data |= mask;
-	snd_hda_codec_write(codec,nid,0,AC_VERB_SET_DIGI_CONVERT_1,ctrl_data);
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
+			    ctrl_data);
 
 	return change;
 }
@@ -481,14 +530,17 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
 /*
  * set up from the preset table
  */
-static void setup_preset(struct alc_spec *spec, const struct alc_config_preset *preset)
+static void setup_preset(struct alc_spec *spec,
+			 const struct alc_config_preset *preset)
 {
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
 		spec->mixers[spec->num_mixers++] = preset->mixers[i];
-	for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; i++)
-		spec->init_verbs[spec->num_init_verbs++] = preset->init_verbs[i];
+	for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i];
+	     i++)
+		spec->init_verbs[spec->num_init_verbs++] =
+			preset->init_verbs[i];
 	
 	spec->channel_mode = preset->channel_mode;
 	spec->num_channel_mode = preset->num_channel_mode;
@@ -517,8 +569,8 @@ static void setup_preset(struct alc_spec *spec, const struct alc_config_preset *
  * ALC880 3-stack model
  *
  * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e)
- * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18, F-Mic = 0x1b
- *                 HP = 0x19
+ * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18,
+ *                 F-Mic = 0x1b, HP = 0x19
  */
 
 static hda_nid_t alc880_dac_nids[4] = {
@@ -662,7 +714,8 @@ static struct snd_kcontrol_new alc880_capture_alt_mixer[] = {
 /*
  * ALC880 5-stack model
  *
- * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d), Side = 0x02 (0xd)
+ * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d),
+ *      Side = 0x02 (0xd)
  * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16
  *                 Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19
  */
@@ -700,7 +753,8 @@ static struct hda_channel_mode alc880_fivestack_modes[2] = {
 /*
  * ALC880 6-stack model
  *
- * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e), Side = 0x05 (0x0f)
+ * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e),
+ *      Side = 0x05 (0x0f)
  * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17,
  *   Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b
  */
@@ -811,7 +865,8 @@ static struct snd_kcontrol_new alc880_w810_base_mixer[] = {
  * Z710V model
  *
  * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d)
- * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?), Line = 0x1a
+ * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?),
+ *                 Line = 0x1a
  */
 
 static hda_nid_t alc880_z71v_dac_nids[1] = {
@@ -966,7 +1021,8 @@ static int alc_build_controls(struct hda_codec *codec)
 	}
 
 	if (spec->multiout.dig_out_nid) {
-		err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+		err = snd_hda_create_spdif_out_ctls(codec,
+						    spec->multiout.dig_out_nid);
 		if (err < 0)
 			return err;
 	}
@@ -999,8 +1055,8 @@ static struct hda_verb alc880_volume_init_verbs[] = {
 
 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
 	 * mixer widget
-	 * Note: PASD motherboards uses the Line In 2 as the input for front panel
-	 * mic (mic 2)
+	 * Note: PASD motherboards uses the Line In 2 as the input for front
+	 * panel mic (mic 2)
 	 */
 	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
 	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -1154,8 +1210,8 @@ static struct hda_verb alc880_pin_z71v_init_verbs[] = {
 
 /*
  * 6-stack pin configuration:
- * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18, f-mic = 0x19,
- * line = 0x1a, HP = 0x1b
+ * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18,
+ * f-mic = 0x19, line = 0x1a, HP = 0x1b
  */
 static struct hda_verb alc880_pin_6stack_init_verbs[] = {
 	{0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
@@ -1587,8 +1643,8 @@ static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 				       struct snd_pcm_substream *substream)
 {
 	struct alc_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
-						format, substream);
+	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+						stream_tag, format, substream);
 }
 
 static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
@@ -1640,7 +1696,8 @@ static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
 {
 	struct alc_spec *spec = codec->spec;
 
-	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
+	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+				   0, 0, 0);
 	return 0;
 }
 
@@ -1822,7 +1879,8 @@ static struct hda_channel_mode alc880_test_modes[4] = {
 	{ 8, NULL },
 };
 
-static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_info *uinfo)
 {
 	static char *texts[] = {
 		"N/A", "Line Out", "HP Out",
@@ -1837,7 +1895,8 @@ static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_e
 	return 0;
 }
 
-static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
@@ -1863,7 +1922,8 @@ static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_el
 	return 0;
 }
 
-static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
@@ -1881,15 +1941,18 @@ static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
 				     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 	new_ctl = ctls[ucontrol->value.enumerated.item[0]];
 	if (old_ctl != new_ctl) {
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl);
+		snd_hda_codec_write(codec, nid, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl);
 		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-				    ucontrol->value.enumerated.item[0] >= 3 ? 0xb080 : 0xb000);
+				    (ucontrol->value.enumerated.item[0] >= 3 ?
+				     0xb080 : 0xb000));
 		return 1;
 	}
 	return 0;
 }
 
-static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_info *uinfo)
 {
 	static char *texts[] = {
 		"Front", "Surround", "CLFE", "Side"
@@ -1903,7 +1966,8 @@ static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_e
 	return 0;
 }
 
-static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
@@ -1914,7 +1978,8 @@ static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_el
 	return 0;
 }
 
-static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
@@ -2739,7 +2804,8 @@ static int patch_alc880(struct hda_codec *codec)
 
 	board_config = snd_hda_check_board_config(codec, alc880_cfg_tbl);
 	if (board_config < 0 || board_config >= ALC880_MODEL_LAST) {
-		printk(KERN_INFO "hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...\n");
+		printk(KERN_INFO "hda_codec: Unknown model for ALC880, "
+		       "trying auto-probe from BIOS...\n");
 		board_config = ALC880_AUTO;
 	}
 
@@ -2750,7 +2816,9 @@ static int patch_alc880(struct hda_codec *codec)
 			alc_free(codec);
 			return err;
 		} else if (! err) {
-			printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS.  Using 3-stack mode...\n");
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using 3-stack mode...\n");
 			board_config = ALC880_3ST;
 		}
 	}
@@ -3947,7 +4015,8 @@ static int patch_alc260(struct hda_codec *codec)
 
 	board_config = snd_hda_check_board_config(codec, alc260_cfg_tbl);
 	if (board_config < 0 || board_config >= ALC260_MODEL_LAST) {
-		snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260\n");
+		snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260, "
+			   "trying auto-probe from BIOS...\n");
 		board_config = ALC260_AUTO;
 	}
 
@@ -3958,7 +4027,9 @@ static int patch_alc260(struct hda_codec *codec)
 			alc_free(codec);
 			return err;
 		} else if (! err) {
-			printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS.  Using base mode...\n");
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using base mode...\n");
 			board_config = ALC260_BASIC;
 		}
 	}
@@ -4320,9 +4391,12 @@ static struct snd_kcontrol_new alc882_capture_mixer[] = {
 static struct hda_board_config alc882_cfg_tbl[] = {
 	{ .modelname = "3stack-dig", .config = ALC882_3ST_DIG },
 	{ .modelname = "6stack-dig", .config = ALC882_6ST_DIG },
-	{ .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* MSI  */
-	{ .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* Foxconn */
-	{ .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* ECS */
+	{ .pci_subvendor = 0x1462, .pci_subdevice = 0x6668,
+	  .config = ALC882_6ST_DIG }, /* MSI  */
+	{ .pci_subvendor = 0x105b, .pci_subdevice = 0x6668,
+	  .config = ALC882_6ST_DIG }, /* Foxconn */
+	{ .pci_subvendor = 0x1019, .pci_subdevice = 0x6668,
+	  .config = ALC882_6ST_DIG }, /* ECS to Intel*/
 	{ .modelname = "auto", .config = ALC882_AUTO },
 	{}
 };
@@ -4439,10 +4513,6 @@ static void alc882_auto_init(struct hda_codec *codec)
 	alc882_auto_init_analog_input(codec);
 }
 
-/*
- *  ALC882 Headphone poll in 3.5.1a or 3.5.2
- */
-
 static int patch_alc882(struct hda_codec *codec)
 {
 	struct alc_spec *spec;
@@ -4457,7 +4527,8 @@ static int patch_alc882(struct hda_codec *codec)
 	board_config = snd_hda_check_board_config(codec, alc882_cfg_tbl);
 
 	if (board_config < 0 || board_config >= ALC882_MODEL_LAST) {
-		printk(KERN_INFO "hda_codec: Unknown model for ALC882, trying auto-probe from BIOS...\n");
+		printk(KERN_INFO "hda_codec: Unknown model for ALC882, "
+		       "trying auto-probe from BIOS...\n");
 		board_config = ALC882_AUTO;
 	}
 
@@ -4468,7 +4539,9 @@ static int patch_alc882(struct hda_codec *codec)
 			alc_free(codec);
 			return err;
 		} else if (! err) {
-			printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS.  Using base mode...\n");
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using base mode...\n");
 			board_config = ALC882_3ST_DIG;
 		}
 	}
@@ -4509,6 +4582,652 @@ static int patch_alc882(struct hda_codec *codec)
 }
 
 /*
+ * ALC883 support
+ *
+ * ALC883 is almost identical with ALC880 but has cleaner and more flexible
+ * configuration.  Each pin widget can choose any input DACs and a mixer.
+ * Each ADC is connected from a mixer of all inputs.  This makes possible
+ * 6-channel independent captures.
+ *
+ * In addition, an independent DAC for the multi-playback (not used in this
+ * driver yet).
+ */
+#define ALC883_DIGOUT_NID	0x06
+#define ALC883_DIGIN_NID	0x0a
+
+static hda_nid_t alc883_dac_nids[4] = {
+	/* front, rear, clfe, rear_surr */
+	0x02, 0x04, 0x03, 0x05
+};
+
+static hda_nid_t alc883_adc_nids[2] = {
+	/* ADC1-2 */
+	0x08, 0x09,
+};
+/* input MUX */
+/* FIXME: should be a matrix-type input source selection */
+
+static struct hda_input_mux alc883_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x1 },
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+	},
+};
+#define alc883_mux_enum_info alc_mux_enum_info
+#define alc883_mux_enum_get alc_mux_enum_get
+
+static int alc883_mux_enum_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	const struct hda_input_mux *imux = spec->input_mux;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 };
+	hda_nid_t nid = capture_mixers[adc_idx];
+	unsigned int *cur_val = &spec->cur_mux[adc_idx];
+	unsigned int i, idx;
+
+	idx = ucontrol->value.enumerated.item[0];
+	if (idx >= imux->num_items)
+		idx = imux->num_items - 1;
+	if (*cur_val == idx && ! codec->in_resume)
+		return 0;
+	for (i = 0; i < imux->num_items; i++) {
+		unsigned int v = (i == idx) ? 0x7000 : 0x7080;
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+				    v | (imux->items[i].index << 8));
+	}
+	*cur_val = idx;
+	return 1;
+}
+/*
+ * 2ch mode
+ */
+static struct hda_channel_mode alc883_3ST_2ch_modes[1] = {
+	{ 2, NULL }
+};
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_3ST_ch2_init[] = {
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_3ST_ch6_init[] = {
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+static struct hda_channel_mode alc883_3ST_6ch_modes[2] = {
+	{ 2, alc883_3ST_ch2_init },
+	{ 6, alc883_3ST_ch6_init },
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_sixstack_ch6_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+	{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc883_sixstack_ch8_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ } /* end */
+};
+
+static struct hda_channel_mode alc883_sixstack_modes[2] = {
+	{ 6, alc883_sixstack_ch6_init },
+	{ 8, alc883_sixstack_ch8_init },
+};
+
+/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
+ *                 Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
+ */
+
+static struct snd_kcontrol_new alc883_base_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc883_mux_enum_info,
+		.get = alc883_mux_enum_get,
+		.put = alc883_mux_enum_put,
+	},
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc883_mux_enum_info,
+		.get = alc883_mux_enum_get,
+		.put = alc883_mux_enum_put,
+	},
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc883_mux_enum_info,
+		.get = alc883_mux_enum_get,
+		.put = alc883_mux_enum_put,
+	},
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc883_chmode_mixer[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc_ch_mode_info,
+		.get = alc_ch_mode_get,
+		.put = alc_ch_mode_put,
+	},
+	{ } /* end */
+};
+
+static struct hda_verb alc883_init_verbs[] = {
+	/* ADC1: mute amp left and right */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* ADC2: mute amp left and right */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Front mixer: unmute input/output amp left and right (volume = 0) */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Rear mixer */
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* CLFE mixer */
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Side mixer */
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+	/* Front Pin: output 0 (0x0c) */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Rear Pin: output 1 (0x0d) */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+	/* CLFE Pin: output 2 (0x0e) */
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
+	/* Side Pin: output 3 (0x0f) */
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+	/* Mic (rear) pin: input vref at 80% */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Front Mic pin: input vref at 80% */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Line In pin: input */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Line-2 In: Headphone output (output 0 - 0x0c) */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* CD pin widget for input */
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+	/* Input mixer2 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+	/* Input mixer3 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+	{ }
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc883_auto_init_verbs[] = {
+	/*
+	 * Unmute ADC0-2 and set the default input to mic-in
+	 */
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+	 * mixer widget
+	 * Note: PASD motherboards uses the Line In 2 as the input for front panel
+	 * mic (mic 2)
+	 */
+	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+	/*
+	 * Set up output mixers (0x0c - 0x0f)
+	 */
+	/* set vol=0 to output mixers */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* set up input amps for analog loopback */
+	/* Amp Indices: DAC = 0, mixer = 1 */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+	/* Input mixer1 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	//{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+	/* Input mixer2 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	//{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+	{ }
+};
+
+/* capture mixer elements */
+static struct snd_kcontrol_new alc883_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc882_mux_enum_info,
+		.get = alc882_mux_enum_get,
+		.put = alc882_mux_enum_put,
+	},
+	{ } /* end */
+};
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc883_pcm_analog_playback	alc880_pcm_analog_playback
+#define alc883_pcm_analog_capture	alc880_pcm_analog_capture
+#define alc883_pcm_digital_playback	alc880_pcm_digital_playback
+#define alc883_pcm_digital_capture	alc880_pcm_digital_capture
+
+/*
+ * configuration and preset
+ */
+static struct hda_board_config alc883_cfg_tbl[] = {
+	{ .modelname = "3stack-dig", .config = ALC883_3ST_2ch_DIG },
+	{ .modelname = "6stack-dig", .config = ALC883_6ST_DIG },
+	{ .modelname = "6stack-dig-demo", .config = ALC888_DEMO_BOARD },
+	{ .pci_subvendor = 0x1462, .pci_subdevice = 0x6668,
+	  .config = ALC883_6ST_DIG }, /* MSI  */
+	{ .pci_subvendor = 0x105b, .pci_subdevice = 0x6668,
+	  .config = ALC883_6ST_DIG }, /* Foxconn */
+	{ .pci_subvendor = 0x1019, .pci_subdevice = 0x6668,
+	  .config = ALC883_3ST_6ch_DIG }, /* ECS to Intel*/
+	{ .pci_subvendor = 0x108e, .pci_subdevice = 0x534d,
+	  .config = ALC883_3ST_6ch },
+	{ .modelname = "auto", .config = ALC883_AUTO },
+	{}
+};
+
+static struct alc_config_preset alc883_presets[] = {
+	[ALC883_3ST_2ch_DIG] = {
+		.mixers = { alc883_3ST_2ch_mixer },
+		.init_verbs = { alc883_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+		.channel_mode = alc883_3ST_2ch_modes,
+		.input_mux = &alc883_capture_source,
+	},
+	[ALC883_3ST_6ch_DIG] = {
+		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+		.channel_mode = alc883_3ST_6ch_modes,
+		.input_mux = &alc883_capture_source,
+	},	
+	[ALC883_3ST_6ch] = {
+		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+		.channel_mode = alc883_3ST_6ch_modes,
+		.input_mux = &alc883_capture_source,
+	},	
+	[ALC883_6ST_DIG] = {
+		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+		.channel_mode = alc883_sixstack_modes,
+		.input_mux = &alc883_capture_source,
+	},
+	[ALC888_DEMO_BOARD] = {
+		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+		.channel_mode = alc883_sixstack_modes,
+		.input_mux = &alc883_capture_source,
+	},
+};
+
+
+/*
+ * BIOS auto configuration
+ */
+static void alc883_auto_set_output_and_unmute(struct hda_codec *codec,
+					      hda_nid_t nid, int pin_type,
+					      int dac_idx)
+{
+	/* set as output */
+	struct alc_spec *spec = codec->spec;
+	int idx; 
+	
+	if (spec->multiout.dac_nids[dac_idx] == 0x25)
+		idx = 4;
+	else
+		idx = spec->multiout.dac_nids[dac_idx] - 2;
+
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			    pin_type);
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+			    AMP_OUT_UNMUTE);
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
+
+}
+
+static void alc883_auto_init_multi_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i <= HDA_SIDE; i++) {
+		hda_nid_t nid = spec->autocfg.line_out_pins[i];	
+		if (nid)
+			alc883_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
+	}
+}
+
+static void alc883_auto_init_hp_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t pin;
+
+	pin = spec->autocfg.hp_pin;
+	if (pin) /* connect to front */
+		/* use dac 0 */
+		alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+}
+
+#define alc883_is_input_pin(nid)	alc880_is_input_pin(nid)
+#define ALC883_PIN_CD_NID		ALC880_PIN_CD_NID
+
+static void alc883_auto_init_analog_input(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		hda_nid_t nid = spec->autocfg.input_pins[i];
+		if (alc883_is_input_pin(nid)) {
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_PIN_WIDGET_CONTROL,
+					    (i <= AUTO_PIN_FRONT_MIC ?
+					     PIN_VREF80 : PIN_IN));
+			if (nid != ALC883_PIN_CD_NID)
+				snd_hda_codec_write(codec, nid, 0,
+						    AC_VERB_SET_AMP_GAIN_MUTE,
+						    AMP_OUT_MUTE);
+		}
+	}
+}
+
+/* almost identical with ALC880 parser... */
+static int alc883_parse_auto_config(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int err = alc880_parse_auto_config(codec);
+
+	if (err < 0)
+		return err;
+	else if (err > 0)
+		/* hack - override the init verbs */
+		spec->init_verbs[0] = alc883_auto_init_verbs;
+                spec->mixers[spec->num_mixers] = alc883_capture_mixer;
+		spec->num_mixers++;
+	return err;
+}
+
+/* additional initialization for auto-configuration model */
+static void alc883_auto_init(struct hda_codec *codec)
+{
+	alc883_auto_init_multi_out(codec);
+	alc883_auto_init_hp_out(codec);
+	alc883_auto_init_analog_input(codec);
+}
+
+static int patch_alc883(struct hda_codec *codec)
+{
+	struct alc_spec *spec;
+	int err, board_config;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	board_config = snd_hda_check_board_config(codec, alc883_cfg_tbl);
+	if (board_config < 0 || board_config >= ALC883_MODEL_LAST) {
+		printk(KERN_INFO "hda_codec: Unknown model for ALC883, "
+		       "trying auto-probe from BIOS...\n");
+		board_config = ALC883_AUTO;
+	}
+
+	if (board_config == ALC883_AUTO) {
+		/* automatic parse from the BIOS config */
+		err = alc883_parse_auto_config(codec);
+		if (err < 0) {
+			alc_free(codec);
+			return err;
+		} else if (! err) {
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using base mode...\n");
+			board_config = ALC883_3ST_2ch_DIG;
+		}
+	}
+
+	if (board_config != ALC883_AUTO)
+		setup_preset(spec, &alc883_presets[board_config]);
+
+	spec->stream_name_analog = "ALC883 Analog";
+	spec->stream_analog_playback = &alc883_pcm_analog_playback;
+	spec->stream_analog_capture = &alc883_pcm_analog_capture;
+
+	spec->stream_name_digital = "ALC883 Digital";
+	spec->stream_digital_playback = &alc883_pcm_digital_playback;
+	spec->stream_digital_capture = &alc883_pcm_digital_capture;
+
+	spec->adc_nids = alc883_adc_nids;
+	spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
+
+	codec->patch_ops = alc_patch_ops;
+	if (board_config == ALC883_AUTO)
+		spec->init_hook = alc883_auto_init;
+
+	return 0;
+}
+
+/*
  * ALC262 support
  */
 
@@ -4542,6 +5261,28 @@ static struct snd_kcontrol_new alc262_base_mixer[] = {
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT),
+	HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT),
+	{ } /* end */
+};
+
 #define alc262_capture_mixer		alc882_capture_mixer
 #define alc262_capture_alt_mixer	alc882_capture_alt_mixer
 
@@ -4645,6 +5386,17 @@ static struct hda_input_mux alc262_fujitsu_capture_source = {
 	},
 };
 
+static struct hda_input_mux alc262_HP_capture_source = {
+	.num_items = 5,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x3 },
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+		{ "AUX IN", 0x6 },
+	},
+};
+
 /* mute/unmute internal speaker according to the hp jack and mute state */
 static void alc262_fujitsu_automute(struct hda_codec *codec, int force)
 {
@@ -4868,6 +5620,93 @@ static struct hda_verb alc262_volume_init_verbs[] = {
 	{ }
 };
 
+static struct hda_verb alc262_HP_BPC_init_verbs[] = {
+	/*
+	 * Unmute ADC0-2 and set the default input to mic-in
+	 */
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+	 * mixer widget
+	 * Note: PASD motherboards uses the Line In 2 as the input for front panel
+	 * mic (mic 2)
+	 */
+	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
+        {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)},
+	
+	/*
+	 * Set up output mixers (0x0c - 0x0e)
+	 */
+	/* set vol=0 to output mixers */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+	/* set up input amps for analog loopback */
+	/* Amp Indices: DAC = 0, mixer = 1 */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+
+	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+        {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 },
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+        {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 },
+	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+
+
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+	/* Input mixer2 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+	/* Input mixer3 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+
+	{ }
+};
+
 /* pcm configuration: identiacal with ALC880 */
 #define alc262_pcm_analog_playback	alc880_pcm_analog_playback
 #define alc262_pcm_analog_capture	alc880_pcm_analog_capture
@@ -4928,7 +5767,16 @@ static void alc262_auto_init(struct hda_codec *codec)
 static struct hda_board_config alc262_cfg_tbl[] = {
 	{ .modelname = "basic", .config = ALC262_BASIC },
 	{ .modelname = "fujitsu", .config = ALC262_FUJITSU },
-	{ .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397, .config = ALC262_FUJITSU },
+	{ .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397,
+	  .config = ALC262_FUJITSU },
+	{ .pci_subvendor = 0x103c, .pci_subdevice = 0x208c,
+	  .config = ALC262_HP_BPC }, /* xw4400 */
+	{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3014,
+	  .config = ALC262_HP_BPC }, /* xw6400 */
+	{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3015,
+	  .config = ALC262_HP_BPC }, /* xw8400 */
+	{ .pci_subvendor = 0x103c, .pci_subdevice = 0x12fe,
+	  .config = ALC262_HP_BPC }, /* xw9400 */
 	{ .modelname = "auto", .config = ALC262_AUTO },
 	{}
 };
@@ -4956,6 +5804,16 @@ static struct alc_config_preset alc262_presets[] = {
 		.input_mux = &alc262_fujitsu_capture_source,
 		.unsol_event = alc262_fujitsu_unsol_event,
 	},
+	[ALC262_HP_BPC] = {
+		.mixers = { alc262_HP_BPC_mixer },
+		.init_verbs = { alc262_HP_BPC_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
+		.dac_nids = alc262_dac_nids,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(alc262_modes),
+		.channel_mode = alc262_modes,
+		.input_mux = &alc262_HP_capture_source,
+	},	
 };
 
 static int patch_alc262(struct hda_codec *codec)
@@ -4981,8 +5839,10 @@ static int patch_alc262(struct hda_codec *codec)
 #endif
 
 	board_config = snd_hda_check_board_config(codec, alc262_cfg_tbl);
+	
 	if (board_config < 0 || board_config >= ALC262_MODEL_LAST) {
-		printk(KERN_INFO "hda_codec: Unknown model for ALC262, trying auto-probe from BIOS...\n");
+		printk(KERN_INFO "hda_codec: Unknown model for ALC262, "
+		       "trying auto-probe from BIOS...\n");
 		board_config = ALC262_AUTO;
 	}
 
@@ -4993,7 +5853,9 @@ static int patch_alc262(struct hda_codec *codec)
 			alc_free(codec);
 			return err;
 		} else if (! err) {
-			printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS.  Using base mode...\n");
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using base mode...\n");
 			board_config = ALC262_BASIC;
 		}
 	}
@@ -5034,7 +5896,6 @@ static int patch_alc262(struct hda_codec *codec)
 	return 0;
 }
 
-
 /*
  *  ALC861 channel source setting (2/6 channel selection for 3-stack)
  */
@@ -5049,9 +5910,11 @@ static struct hda_verb alc861_threestack_ch2_init[] = {
 	/* set pin widget 18h (mic1/2) for input, for mic also enable the vref */
 	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
 
-        { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
-        { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, //mic
-        { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, //line in
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
+#if 0
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
+#endif
 	{ } /* end */
 };
 /*
@@ -5065,11 +5928,13 @@ static struct hda_verb alc861_threestack_ch6_init[] = {
 	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
 
 	{ 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 },
-        { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
 
-        { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
-        { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, //mic
-        { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, //line in
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
+#if 0
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/
+#endif
 	{ } /* end */
 };
 
@@ -5353,6 +6218,11 @@ static hda_nid_t alc861_dac_nids[4] = {
 	0x03, 0x06, 0x05, 0x04
 };
 
+static hda_nid_t alc660_dac_nids[3] = {
+	/* front, clfe, surround */
+	0x03, 0x05, 0x06
+};
+
 static hda_nid_t alc861_adc_nids[1] = {
 	/* ADC0-2 */
 	0x08,
@@ -5605,7 +6475,10 @@ static void alc861_auto_init(struct hda_codec *codec)
  */
 static struct hda_board_config alc861_cfg_tbl[] = {
 	{ .modelname = "3stack", .config = ALC861_3ST },
-	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xd600, .config = ALC861_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xd600,
+	  .config = ALC861_3ST },
+	{ .pci_subvendor = 0x1043, .pci_subdevice = 0x81e7,
+	  .config = ALC660_3ST },
 	{ .modelname = "3stack-dig", .config = ALC861_3ST_DIG },
 	{ .modelname = "6stack-dig", .config = ALC861_6ST_DIG },
 	{ .modelname = "auto", .config = ALC861_AUTO },
@@ -5648,6 +6521,17 @@ static struct alc_config_preset alc861_presets[] = {
 		.adc_nids = alc861_adc_nids,
 		.input_mux = &alc861_capture_source,
 	},
+	[ALC660_3ST] = {
+		.mixers = { alc861_3ST_mixer },
+		.init_verbs = { alc861_threestack_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc660_dac_nids),
+		.dac_nids = alc660_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
+		.channel_mode = alc861_threestack_modes,
+		.num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+		.adc_nids = alc861_adc_nids,
+		.input_mux = &alc861_capture_source,
+	},
 };	
 
 
@@ -5664,8 +6548,10 @@ static int patch_alc861(struct hda_codec *codec)
 	codec->spec = spec;	
 
         board_config = snd_hda_check_board_config(codec, alc861_cfg_tbl);
+
 	if (board_config < 0 || board_config >= ALC861_MODEL_LAST) {
-		printk(KERN_INFO "hda_codec: Unknown model for ALC861, trying auto-probe from BIOS...\n");
+		printk(KERN_INFO "hda_codec: Unknown model for ALC861, "
+		       "trying auto-probe from BIOS...\n");
 		board_config = ALC861_AUTO;
 	}
 
@@ -5676,7 +6562,9 @@ static int patch_alc861(struct hda_codec *codec)
 			alc_free(codec);
 			return err;
 		} else if (! err) {
-			printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS.  Using base mode...\n");
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using base mode...\n");
 		   board_config = ALC861_3ST_DIG;
 		}
 	}
@@ -5707,8 +6595,12 @@ struct hda_codec_preset snd_hda_preset_realtek[] = {
 	{ .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
  	{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
 	{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
-	{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
+	{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
 	{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
-	{ .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
+	{ .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 },
+	{ .id = 0x10ec0861, .rev = 0x100300, .name = "ALC861",
+	  .patch = patch_alc861 },
+        { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
+	  .patch = patch_alc861 },
 	{} /* terminator */
 };
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 36f199442fdc..fb4bed0759d1 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -42,6 +42,9 @@
 #define STAC_D945GTP3		1
 #define STAC_D945GTP5		2
 #define STAC_MACMINI		3
+#define STAC_D965_2112		4
+#define STAC_D965_284B		5
+#define STAC_922X_MODELS	6	/* number of 922x models */
 
 struct sigmatel_spec {
 	struct snd_kcontrol_new *mixers[4];
@@ -107,10 +110,24 @@ static hda_nid_t stac922x_adc_nids[2] = {
         0x06, 0x07,
 };
 
+static hda_nid_t stac9227_adc_nids[2] = {
+        0x07, 0x08,
+};
+
+#if 0
+static hda_nid_t d965_2112_dac_nids[3] = {
+        0x02, 0x03, 0x05,
+};
+#endif
+
 static hda_nid_t stac922x_mux_nids[2] = {
         0x12, 0x13,
 };
 
+static hda_nid_t stac9227_mux_nids[2] = {
+        0x15, 0x16,
+};
+
 static hda_nid_t stac927x_adc_nids[3] = {
         0x07, 0x08, 0x09
 };
@@ -173,6 +190,24 @@ static struct hda_verb stac922x_core_init[] = {
 	{}
 };
 
+static struct hda_verb stac9227_core_init[] = {
+	/* set master volume and direct control */	
+	{ 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+	/* unmute node 0x1b */
+	{ 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	{}
+};
+
+static struct hda_verb d965_2112_core_init[] = {
+	/* set master volume and direct control */	
+	{ 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+	/* unmute node 0x1b */
+	{ 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* select node 0x03 as DAC */	
+	{ 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{}
+};
+
 static struct hda_verb stac927x_core_init[] = {
 	/* set master volume and direct control */	
 	{ 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
@@ -212,6 +247,21 @@ static struct snd_kcontrol_new stac922x_mixer[] = {
 	{ } /* end */
 };
 
+/* This needs to be generated dynamically based on sequence */
+static struct snd_kcontrol_new stac9227_mixer[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Input Source",
+		.count = 1,
+		.info = stac92xx_mux_enum_info,
+		.get = stac92xx_mux_enum_get,
+		.put = stac92xx_mux_enum_put,
+	},
+	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x1b, 0x0, HDA_OUTPUT),
+	{ } /* end */
+};
+
 static snd_kcontrol_new_t stac927x_mixer[] = {
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -291,11 +341,17 @@ static unsigned int d945gtp5_pin_configs[10] = {
 	0x02a19320, 0x40000100,
 };
 
-static unsigned int *stac922x_brd_tbl[] = {
-	ref922x_pin_configs,
-	d945gtp3_pin_configs,
-	d945gtp5_pin_configs,
-	NULL,		/* STAC_MACMINI */
+static unsigned int d965_2112_pin_configs[10] = {
+	0x0221401f, 0x40000100, 0x40000100, 0x01014011,
+	0x01a19021, 0x01813024, 0x01452130, 0x40000100,
+	0x02a19320, 0x40000100,
+};
+
+static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
+	[STAC_REF] =	ref922x_pin_configs,
+	[STAC_D945GTP3] = d945gtp3_pin_configs,
+	[STAC_D945GTP5] = d945gtp5_pin_configs,
+	[STAC_D965_2112] = d965_2112_pin_configs,
 };
 
 static struct hda_board_config stac922x_cfg_tbl[] = {
@@ -330,6 +386,12 @@ static struct hda_board_config stac922x_cfg_tbl[] = {
 	{ .pci_subvendor = 0x8384,
 	  .pci_subdevice = 0x7680,
 	  .config = STAC_MACMINI },	/* Apple Mac Mini (early 2006) */
+	{ .pci_subvendor = PCI_VENDOR_ID_INTEL,
+	  .pci_subdevice = 0x2112,
+	  .config = STAC_D965_2112 },
+	{ .pci_subvendor = PCI_VENDOR_ID_INTEL,
+	  .pci_subdevice = 0x284b,
+	  .config = STAC_D965_284B },
 	{} /* terminator */
 };
 
@@ -713,7 +775,8 @@ static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cf
  * A and B is not supported.
  */
 /* 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)
+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;
@@ -732,10 +795,13 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, const struct aut
 }
 
 /* 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)
+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" };
+	static const char *chname[4] = {
+		"Front", "Surround", NULL /*CLFE*/, "Side"
+	};
 	hda_nid_t nid;
 	int i, err;
 
@@ -893,10 +959,12 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
 		return err;
 	if (! spec->autocfg.line_outs)
 		return 0; /* can't find valid pin config */
+
 	if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
 		return err;
-	if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
-		return err;
+	if (spec->multiout.num_dacs == 0)
+		if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
+			return err;
 
 	if ((err = stac92xx_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
 	    (err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg)) < 0 ||
@@ -1194,7 +1262,8 @@ static int patch_stac922x(struct hda_codec *codec)
 	codec->spec = spec;
 	spec->board_config = snd_hda_check_board_config(codec, stac922x_cfg_tbl);
 	if (spec->board_config < 0)
-                snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, using BIOS defaults\n");
+                snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
+			    "using BIOS defaults\n");
 	else if (stac922x_brd_tbl[spec->board_config] != NULL) {
 		spec->num_pins = 10;
 		spec->pin_nids = stac922x_pin_nids;
@@ -1210,6 +1279,25 @@ static int patch_stac922x(struct hda_codec *codec)
 	spec->mixer = stac922x_mixer;
 
 	spec->multiout.dac_nids = spec->dac_nids;
+	
+	switch (spec->board_config) {
+	case STAC_D965_2112:
+		spec->adc_nids = stac9227_adc_nids;
+		spec->mux_nids = stac9227_mux_nids;
+#if 0
+		spec->multiout.dac_nids = d965_2112_dac_nids;
+		spec->multiout.num_dacs = ARRAY_SIZE(d965_2112_dac_nids);
+#endif
+		spec->init = d965_2112_core_init;
+		spec->mixer = stac9227_mixer;
+		break;
+	case STAC_D965_284B:
+		spec->adc_nids = stac9227_adc_nids;
+		spec->mux_nids = stac9227_mux_nids;
+		spec->init = stac9227_core_init;
+		spec->mixer = stac9227_mixer;
+		break;
+	}
 
 	err = stac92xx_parse_auto_config(codec, 0x08, 0x09);
 	if (err < 0) {
diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c
index b5754b32b802..fec9440cb310 100644
--- a/sound/pci/ice1712/revo.c
+++ b/sound/pci/ice1712/revo.c
@@ -87,12 +87,25 @@ static void revo_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
  * initialize the chips on M-Audio Revolution cards
  */
 
+static unsigned int revo71_num_stereo_front[] = {2};
+static char *revo71_channel_names_front[] = {"PCM Playback Volume"};
+
+static unsigned int revo71_num_stereo_surround[] = {1, 1, 2, 2};
+static char *revo71_channel_names_surround[] = {"PCM Center Playback Volume", "PCM LFE Playback Volume",
+						"PCM Side Playback Volume", "PCM Rear Playback Volume"};
+
+static unsigned int revo51_num_stereo[] = {2, 1, 1, 2};
+static char *revo51_channel_names[] = {"PCM Playback Volume", "PCM Center Playback Volume",
+					"PCM LFE Playback Volume", "PCM Rear Playback Volume"};
+
 static struct snd_akm4xxx akm_revo_front __devinitdata = {
 	.type = SND_AK4381,
 	.num_dacs = 2,
 	.ops = {
 		.set_rate_val = revo_set_rate_val
-	}
+	},
+	.num_stereo = revo71_num_stereo_front,
+	.channel_names = revo71_channel_names_front
 };
 
 static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = {
@@ -113,7 +126,9 @@ static struct snd_akm4xxx akm_revo_surround __devinitdata = {
 	.num_dacs = 6,
 	.ops = {
 		.set_rate_val = revo_set_rate_val
-	}
+	},
+	.num_stereo = revo71_num_stereo_surround,
+	.channel_names = revo71_channel_names_surround
 };
 
 static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
@@ -133,7 +148,9 @@ static struct snd_akm4xxx akm_revo51 __devinitdata = {
 	.num_dacs = 6,
 	.ops = {
 		.set_rate_val = revo_set_rate_val
-	}
+	},
+	.num_stereo = revo51_num_stereo,
+	.channel_names = revo51_channel_names
 };
 
 static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = {
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 627de9525a32..d32d83d970cc 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -3096,6 +3096,32 @@ static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
 }
 
 /*
+ * C-Media CM106/CM106+ have four 16-bit internal registers that are nicely
+ * documented in the device's data sheet.
+ */
+static int snd_usb_cm106_write_int_reg(struct usb_device *dev, int reg, u16 value)
+{
+	u8 buf[4];
+	buf[0] = 0x20;
+	buf[1] = value & 0xff;
+	buf[2] = (value >> 8) & 0xff;
+	buf[3] = reg;
+	return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION,
+			       USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
+			       0, 0, &buf, 4, 1000);
+}
+
+static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
+{
+	/*
+	 * Enable line-out driver mode, set headphone source to front
+	 * channels, enable stereo mic.
+	 */
+	return snd_usb_cm106_write_int_reg(dev, 2, 0x8004);
+}
+
+
+/*
  * Setup quirks
  */
 #define AUDIOPHILE_SET			0x01 /* if set, parse device_setup */
@@ -3365,6 +3391,12 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
 			goto __err_val;
 	}
 
+	/* C-Media CM106 / Turtle Beach Audio Advantage Roadie */
+	if (id == USB_ID(0x10f5, 0x0200)) {
+		if (snd_usb_cm106_boot_quirk(dev) < 0)
+			goto __err_val;
+	}
+
 	/*
 	 * found a config.  now register to ALSA
 	 */