Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/sound/pci/au88x0/Makefile b/sound/pci/au88x0/Makefile
new file mode 100644
index 0000000..d0a66bc
--- /dev/null
+++ b/sound/pci/au88x0/Makefile
@@ -0,0 +1,7 @@
+snd-au8810-objs := au8810.o
+snd-au8820-objs := au8820.o
+snd-au8830-objs := au8830.o
+
+obj-$(CONFIG_SND_AU8810) += snd-au8810.o
+obj-$(CONFIG_SND_AU8820) += snd-au8820.o
+obj-$(CONFIG_SND_AU8830) += snd-au8830.o
diff --git a/sound/pci/au88x0/au8810.c b/sound/pci/au88x0/au8810.c
new file mode 100644
index 0000000..fce22c7
--- /dev/null
+++ b/sound/pci/au88x0/au8810.c
@@ -0,0 +1,17 @@
+#include "au8810.h"
+#include "au88x0.h"
+static struct pci_device_id snd_vortex_ids[] = {
+	{PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_ADVANTAGE,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1,},
+	{0,}
+};
+
+#include "au88x0_core.c"
+#include "au88x0_pcm.c"
+#include "au88x0_mixer.c"
+#include "au88x0_mpu401.c"
+#include "au88x0_game.c"
+#include "au88x0_eq.c"
+#include "au88x0_a3d.c"
+#include "au88x0_xtalk.c"
+#include "au88x0.c"
diff --git a/sound/pci/au88x0/au8810.h b/sound/pci/au88x0/au8810.h
new file mode 100644
index 0000000..3837d2b
--- /dev/null
+++ b/sound/pci/au88x0/au8810.h
@@ -0,0 +1,229 @@
+/*
+    Aureal Advantage Soundcard driver.
+ */
+
+#define CHIP_AU8810
+
+#define CARD_NAME "Aureal Advantage 3D Sound Processor"
+#define CARD_NAME_SHORT "au8810"
+
+#define NR_ADB		0x10
+#define NR_WT		0x00
+#define NR_SRC		0x10
+#define NR_A3D		0x10
+#define NR_MIXIN	0x20
+#define NR_MIXOUT	0x10
+
+
+/* ADBDMA */
+#define VORTEX_ADBDMA_STAT 0x27e00	/* read only, subbuffer, DMA pos */
+#define		POS_MASK 0x00000fff
+#define     POS_SHIFT 0x0
+#define 	ADB_SUBBUF_MASK 0x00003000	/* ADB only. */
+#define     ADB_SUBBUF_SHIFT 0xc	/* ADB only. */
+#define VORTEX_ADBDMA_CTRL 0x27180	/* write only; format, flags, DMA pos */
+#define		OFFSET_MASK 0x00000fff
+#define     OFFSET_SHIFT 0x0
+#define		IE_MASK 0x00001000	/* interrupt enable. */
+#define     IE_SHIFT 0xc
+#define     DIR_MASK 0x00002000	/* Direction */
+#define     DIR_SHIFT 0xd
+#define		FMT_MASK 0x0003c000
+#define		FMT_SHIFT 0xe
+// The ADB masks and shift also are valid for the wtdma, except if specified otherwise.
+#define VORTEX_ADBDMA_BUFCFG0 0x27100
+#define VORTEX_ADBDMA_BUFCFG1 0x27104
+#define VORTEX_ADBDMA_BUFBASE 0x27000
+#define VORTEX_ADBDMA_START 0x27c00	/* Which subbuffer starts */
+
+#define VORTEX_ADBDMA_STATUS 0x27A90	/* stored at AdbDma->this_10 / 2 DWORD in size. */
+
+/* WTDMA */
+#define VORTEX_WTDMA_CTRL 0x27fd8	/* format, DMA pos */
+#define VORTEX_WTDMA_STAT 0x27fe8	/* DMA subbuf, DMA pos */
+#define     WT_SUBBUF_MASK 0x3
+#define     WT_SUBBUF_SHIFT 0xc
+#define VORTEX_WTDMA_BUFBASE 0x27fc0
+#define VORTEX_WTDMA_BUFCFG0 0x27fd0
+#define VORTEX_WTDMA_BUFCFG1 0x27fd4
+#define VORTEX_WTDMA_START 0x27fe4	/* which subbuffer is first */
+
+/* ADB */
+#define VORTEX_ADB_SR 0x28400	/* Samplerates enable/disable */
+#define VORTEX_ADB_RTBASE 0x28000
+#define VORTEX_ADB_RTBASE_COUNT 173
+#define VORTEX_ADB_CHNBASE 0x282b4
+#define VORTEX_ADB_CHNBASE_COUNT 24
+#define 	ROUTE_MASK	0xffff
+#define		SOURCE_MASK	0xff00
+#define     ADB_MASK   0xff
+#define		ADB_SHIFT 0x8
+
+/* ADB address */
+#define		OFFSET_ADBDMA	0x00
+#define		OFFSET_SRCIN	0x40
+#define		OFFSET_SRCOUT	0x20
+#define		OFFSET_MIXIN	0x50
+#define		OFFSET_MIXOUT	0x30
+#define		OFFSET_CODECIN	0x70
+#define		OFFSET_CODECOUT	0x88
+#define		OFFSET_SPORTIN	0x78	/* ch 0x13 */
+#define		OFFSET_SPORTOUT	0x90
+#define		OFFSET_SPDIFOUT	0x92	/* ch 0x14 check this! */
+#define		OFFSET_EQIN	0xa0
+#define		OFFSET_EQOUT	0x7e	/* 2 routes on ch 0x11 */
+#define		OFFSET_XTALKOUT	0x66	/* crosstalk canceller (source) */
+#define		OFFSET_XTALKIN	0x96	/* crosstalk canceller (sink) */
+#define		OFFSET_A3DIN	0x70	/* ADB sink. */
+#define		OFFSET_A3DOUT	0xA6	/* ADB source. 2 routes per slice = 8 */
+#define		OFFSET_EFXIN	0x80	/* ADB sink. */
+#define		OFFSET_EFXOUT	0x68	/* ADB source. */
+
+/* ADB route translate helper */
+#define ADB_DMA(x) (x)
+#define ADB_SRCOUT(x) (x + OFFSET_SRCOUT)
+#define ADB_SRCIN(x) (x + OFFSET_SRCIN)
+#define ADB_MIXOUT(x) (x + OFFSET_MIXOUT)
+#define ADB_MIXIN(x) (x + OFFSET_MIXIN)
+#define ADB_CODECIN(x) (x + OFFSET_CODECIN)
+#define ADB_CODECOUT(x) (x + OFFSET_CODECOUT)
+#define ADB_SPORTIN(x) (x + OFFSET_SPORTIN)
+#define ADB_SPORTOUT(x) (x + OFFSET_SPORTOUT)
+#define ADB_SPDIFOUT(x)	(x + OFFSET_SPDIFOUT)
+#define ADB_EQIN(x) (x + OFFSET_EQIN)
+#define ADB_EQOUT(x) (x + OFFSET_EQOUT)
+#define ADB_A3DOUT(x) (x + OFFSET_A3DOUT)	/* 0x10 A3D blocks */
+#define ADB_A3DIN(x) (x + OFFSET_A3DIN)
+#define ADB_XTALKIN(x) (x + OFFSET_XTALKIN)
+#define ADB_XTALKOUT(x) (x + OFFSET_XTALKOUT)
+
+#define MIX_OUTL    0xe
+#define MIX_OUTR    0xf
+#define MIX_INL     0x1e
+#define MIX_INR     0x1f
+#define MIX_DEFIGAIN 0x08	/* 0x8 => 6dB */
+#define MIX_DEFOGAIN 0x08
+
+/* MIXER */
+#define VORTEX_MIXER_SR 0x21f00
+#define VORTEX_MIXER_CLIP 0x21f80
+#define VORTEX_MIXER_CHNBASE 0x21e40
+#define VORTEX_MIXER_RTBASE 0x21e00
+#define 	MIXER_RTBASE_SIZE 0x38
+#define VORTEX_MIX_ENIN 0x21a00	/* Input enable bits. 4 bits wide. */
+#define VORTEX_MIX_SMP 0x21c00	/* AU8820: 0x9c00 */
+
+/* MIX */
+#define VORTEX_MIX_INVOL_A 0x21000	/* in? */
+#define VORTEX_MIX_INVOL_B 0x20000	/* out? */
+#define VORTEX_MIX_VOL_A 0x21800
+#define VORTEX_MIX_VOL_B 0x20800
+
+#define 	VOL_MIN 0x80	/* Input volume when muted. */
+#define		VOL_MAX 0x7f	/* FIXME: Not confirmed! Just guessed. */
+
+/* SRC */
+#define VORTEX_SRC_CHNBASE		0x26c40
+#define VORTEX_SRC_RTBASE		0x26c00
+#define VORTEX_SRCBLOCK_SR		0x26cc0
+#define VORTEX_SRC_SOURCE		0x26cc4
+#define VORTEX_SRC_SOURCESIZE	0x26cc8
+/* Params
+	0x26e00	: 1 U0
+	0x26e40	: 2 CR
+	0x26e80	: 3 U3
+	0x26ec0	: 4 DRIFT1
+	0x26f00 : 5 U1
+	0x26f40	: 6 DRIFT2
+	0x26f80	: 7 U2 : Target rate, direction
+*/
+
+#define VORTEX_SRC_CONVRATIO	0x26e40
+#define VORTEX_SRC_DRIFT0		0x26e80
+#define VORTEX_SRC_DRIFT1		0x26ec0
+#define VORTEX_SRC_DRIFT2		0x26f40
+#define VORTEX_SRC_U0			0x26e00
+#define		U0_SLOWLOCK		0x200
+#define VORTEX_SRC_U1			0x26f00
+#define VORTEX_SRC_U2			0x26f80
+#define VORTEX_SRC_DATA			0x26800	/* 0xc800 */
+#define VORTEX_SRC_DATA0		0x26000
+
+/* FIFO */
+#define VORTEX_FIFO_ADBCTRL 0x16100	/* Control bits. */
+#define VORTEX_FIFO_WTCTRL 0x16000
+#define		FIFO_RDONLY	0x00000001
+#define		FIFO_CTRL	0x00000002	/* Allow ctrl. ? */
+#define		FIFO_VALID	0x00000010
+#define 	FIFO_EMPTY	0x00000020
+#define		FIFO_U0		0x00001000	/* Unknown. */
+#define		FIFO_U1		0x00010000
+#define		FIFO_SIZE_BITS 5
+#define		FIFO_SIZE	(1<<FIFO_SIZE_BITS)	// 0x20
+#define 	FIFO_MASK	(FIFO_SIZE-1)	//0x1f    /* at shift left 0xc */
+//#define       FIFO_MASK       0x1f    /* at shift left 0xb */
+//#define               FIFO_SIZE       0x20
+#define 	FIFO_BITS	0x03880000
+#define VORTEX_FIFO_ADBDATA	0x14000
+#define VORTEX_FIFO_WTDATA	0x10000
+
+/* CODEC */
+#define VORTEX_CODEC_CTRL	0x29184
+#define VORTEX_CODEC_EN		0x29190
+#define		EN_CODEC0	0x00000300
+#define 	EN_AC98		0x00000c00 /* Modem AC98 slots. */
+#define		EN_CODEC1	0x00003000
+#define		EN_CODEC	(EN_CODEC0 | EN_CODEC1)
+#define		EN_SPORT	0x00030000
+#define		EN_SPDIF	0x000c0000
+
+#define VORTEX_CODEC_CHN 	0x29080
+#define VORTEX_CODEC_WRITE	0x00800000
+#define VORTEX_CODEC_ADDSHIFT 	16
+#define VORTEX_CODEC_ADDMASK	0x7f0000	/* 0x000f0000 */
+#define VORTEX_CODEC_DATSHIFT	0
+#define VORTEX_CODEC_DATMASK	0xffff
+#define VORTEX_CODEC_IO		0x29188
+
+/* SPDIF */
+#define VORTEX_SPDIF_FLAGS	0x2205c
+#define VORTEX_SPDIF_CFG0	0x291D0
+#define VORTEX_SPDIF_CFG1	0x291D4
+#define VORTEX_SPDIF_SMPRATE	0x29194
+
+/* Sample timer */
+#define VORTEX_SMP_TIME		0x29198
+
+#define VORTEX_MODEM_CTRL	0x291ac
+
+/* IRQ */
+#define VORTEX_IRQ_SOURCE 0x2a000	/* Interrupt source flags. */
+#define VORTEX_IRQ_CTRL 0x2a004	/* Interrupt source mask. */
+
+#define VORTEX_STAT	0x2a008	/* Status */
+
+#define VORTEX_CTRL		0x2a00c
+#define 	CTRL_MIDI_EN	0x00000001
+#define 	CTRL_MIDI_PORT	0x00000060
+#define 	CTRL_GAME_EN	0x00000008
+#define 	CTRL_GAME_PORT	0x00000e00
+//#define       CTRL_IRQ_ENABLE 0x01004000
+#define 	CTRL_IRQ_ENABLE	0x00004000
+
+/* write: Timer period config / read: TIMER IRQ ack. */
+#define VORTEX_IRQ_STAT		0x2919c
+
+/* DMA */
+#define VORTEX_ENGINE_CTRL	0x27ae8
+#define 	ENGINE_INIT	0x1380000
+
+/* MIDI *//* GAME. */
+#define VORTEX_MIDI_DATA	0x28800
+#define VORTEX_MIDI_CMD		0x28804	/* Write command / Read status */
+
+#define VORTEX_CTRL2		0x2880c
+#define		CTRL2_GAME_ADCMODE 0x40
+#define VORTEX_GAME_LEGACY	0x28808
+#define VORTEX_GAME_AXIS	0x28810
+#define		AXIS_SIZE 4
+#define		AXIS_RANGE 0x1fff
diff --git a/sound/pci/au88x0/au8820.c b/sound/pci/au88x0/au8820.c
new file mode 100644
index 0000000..d1fbcce
--- /dev/null
+++ b/sound/pci/au88x0/au8820.c
@@ -0,0 +1,15 @@
+#include "au8820.h"
+#include "au88x0.h"
+static struct pci_device_id snd_vortex_ids[] = {
+	{PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_1,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
+	{0,}
+};
+
+#include "au88x0_synth.c"
+#include "au88x0_core.c"
+#include "au88x0_pcm.c"
+#include "au88x0_mpu401.c"
+#include "au88x0_game.c"
+#include "au88x0_mixer.c"
+#include "au88x0.c"
diff --git a/sound/pci/au88x0/au8820.h b/sound/pci/au88x0/au8820.h
new file mode 100644
index 0000000..be8022e
--- /dev/null
+++ b/sound/pci/au88x0/au8820.h
@@ -0,0 +1,209 @@
+/*
+    Aureal Vortex Soundcard driver.
+
+    IO addr collected from asp4core.vxd:
+    function    address
+    0005D5A0    13004
+    00080674    14004
+    00080AFF    12818
+
+ */
+
+#define CHIP_AU8820
+
+#define CARD_NAME "Aureal Vortex 3D Sound Processor"
+#define CARD_NAME_SHORT "au8820"
+
+/* Number of ADB and WT channels */
+#define NR_ADB		0x10
+#define NR_WT		0x20
+#define NR_SRC		0x10
+#define NR_A3D		0x00
+#define NR_MIXIN	0x10
+#define NR_MIXOUT 	0x10
+
+
+/* ADBDMA */
+#define VORTEX_ADBDMA_STAT 0x105c0	/* read only, subbuffer, DMA pos */
+#define		POS_MASK 0x00000fff
+#define     POS_SHIFT 0x0
+#define 	ADB_SUBBUF_MASK 0x00003000	/* ADB only. */
+#define     ADB_SUBBUF_SHIFT 0xc	/* ADB only. */
+#define VORTEX_ADBDMA_CTRL 0x10580	/* write only, format, flags, DMA pos */
+#define		OFFSET_MASK 0x00000fff
+#define     OFFSET_SHIFT 0x0
+#define		IE_MASK 0x00001000	/* interrupt enable. */
+#define     IE_SHIFT 0xc
+#define     DIR_MASK 0x00002000	/* Direction. */
+#define     DIR_SHIFT 0xd
+#define		FMT_MASK 0x0003c000
+#define		FMT_SHIFT 0xe
+// The masks and shift also work for the wtdma, if not specified otherwise.
+#define VORTEX_ADBDMA_BUFCFG0 0x10400
+#define VORTEX_ADBDMA_BUFCFG1 0x10404
+#define VORTEX_ADBDMA_BUFBASE 0x10200
+#define VORTEX_ADBDMA_START 0x106c0	/* Which subbuffer starts */
+#define VORTEX_ADBDMA_STATUS 0x10600	/* stored at AdbDma->this_10 / 2 DWORD in size. */
+
+/* ADB */
+#define VORTEX_ADB_SR 0x10a00	/* Samplerates enable/disable */
+#define VORTEX_ADB_RTBASE 0x10800
+#define VORTEX_ADB_RTBASE_COUNT 103
+#define VORTEX_ADB_CHNBASE 0x1099c
+#define VORTEX_ADB_CHNBASE_COUNT 22
+#define 	ROUTE_MASK	0x3fff
+#define     ADB_MASK   0x7f
+#define		ADB_SHIFT 0x7
+//#define     ADB_MIX_MASK 0xf
+/* ADB address */
+#define		OFFSET_ADBDMA	0x00
+#define		OFFSET_SRCOUT	0x10	/* on channel 0x11 */
+#define		OFFSET_SRCIN	0x10	/* on channel < 0x11 */
+#define		OFFSET_MIXOUT	0x20	/* source */
+#define		OFFSET_MIXIN	0x30	/* sink */
+#define		OFFSET_CODECIN	0x48	/* ADB source */
+#define		OFFSET_CODECOUT	0x58	/* ADB sink/target */
+#define		OFFSET_SPORTOUT	0x60	/* sink */
+#define		OFFSET_SPORTIN	0x50	/* source */
+#define		OFFSET_EFXOUT	0x50	/* sink */
+#define		OFFSET_EFXIN	0x40	/* source */
+#define		OFFSET_A3DOUT	0x00	/* This card has no HRTF :( */
+#define		OFFSET_A3DIN	0x00
+#define		OFFSET_WTOUT	0x58	/*  */
+
+/* ADB route translate helper */
+#define ADB_DMA(x) (x + OFFSET_ADBDMA)
+#define ADB_SRCOUT(x) (x + OFFSET_SRCOUT)
+#define ADB_SRCIN(x) (x + OFFSET_SRCIN)
+#define ADB_MIXOUT(x) (x + OFFSET_MIXOUT)
+#define ADB_MIXIN(x) (x + OFFSET_MIXIN)
+#define ADB_CODECIN(x) (x + OFFSET_CODECIN)
+#define ADB_CODECOUT(x) (x + OFFSET_CODECOUT)
+#define ADB_SPORTOUT(x) (x + OFFSET_SPORTOUT)
+#define ADB_SPORTIN(x) (x + OFFSET_SPORTIN)	/*  */
+#define ADB_A3DOUT(x) (x + OFFSET_A3DOUT)	/* 8 A3D blocks */
+#define ADB_A3DIN(x) (x + OFFSET_A3DIN)
+#define ADB_WTOUT(x,y) (y + OFFSET_WTOUT)
+
+/* WTDMA */
+#define VORTEX_WTDMA_CTRL 0x10500	/* format, DMA pos */
+#define VORTEX_WTDMA_STAT 0x10500	/* DMA subbuf, DMA pos */
+#define     WT_SUBBUF_MASK (0x3 << WT_SUBBUF_SHIFT)
+#define     WT_SUBBUF_SHIFT 0x15
+#define VORTEX_WTDMA_BUFBASE 0x10000
+#define VORTEX_WTDMA_BUFCFG0 0x10300
+#define VORTEX_WTDMA_BUFCFG1 0x10304
+#define VORTEX_WTDMA_START 0x10640	/* which subbuffer is first */
+
+#define VORTEX_WT_BASE 0x9000
+
+/* MIXER */
+#define VORTEX_MIXER_SR 0x9f00
+#define VORTEX_MIXER_CLIP 0x9f80
+#define VORTEX_MIXER_CHNBASE 0x9e40
+#define VORTEX_MIXER_RTBASE 0x9e00
+#define 	MIXER_RTBASE_SIZE 0x26
+#define VORTEX_MIX_ENIN 0x9a00	/* Input enable bits. 4 bits wide. */
+#define VORTEX_MIX_SMP 0x9c00
+
+/* MIX */
+#define VORTEX_MIX_INVOL_A 0x9000	/* in? */
+#define VORTEX_MIX_INVOL_B 0x8000	/* out? */
+#define VORTEX_MIX_VOL_A 0x9800
+#define VORTEX_MIX_VOL_B 0x8800
+
+#define 	VOL_MIN 0x80	/* Input volume when muted. */
+#define		VOL_MAX 0x7f	/* FIXME: Not confirmed! Just guessed. */
+
+//#define MIX_OUTL    0xe
+//#define MIX_OUTR    0xf
+//#define MIX_INL     0xe
+//#define MIX_INR     0xf
+#define MIX_DEFIGAIN 0x08	/* 0x8 => 6dB */
+#define MIX_DEFOGAIN 0x08
+
+/* SRC */
+#define VORTEX_SRCBLOCK_SR	0xccc0
+#define VORTEX_SRC_CHNBASE	0xcc40
+#define VORTEX_SRC_RTBASE	0xcc00
+#define VORTEX_SRC_SOURCE	0xccc4
+#define VORTEX_SRC_SOURCESIZE 0xccc8
+#define VORTEX_SRC_U0		0xce00
+#define VORTEX_SRC_DRIFT0	0xce80
+#define VORTEX_SRC_DRIFT1	0xcec0
+#define VORTEX_SRC_U1		0xcf00
+#define VORTEX_SRC_DRIFT2	0xcf40
+#define VORTEX_SRC_U2		0xcf80
+#define VORTEX_SRC_DATA		0xc800
+#define VORTEX_SRC_DATA0	0xc000
+#define VORTEX_SRC_CONVRATIO 0xce40
+//#define     SRC_RATIO(x) ((((x<<15)/48000) + 1)/2) /* Playback */
+//#define     SRC_RATIO2(x) ((((48000<<15)/x) + 1)/2) /* Recording */
+
+/* FIFO */
+#define VORTEX_FIFO_ADBCTRL 0xf800	/* Control bits. */
+#define VORTEX_FIFO_WTCTRL 0xf840
+#define		FIFO_RDONLY	0x00000001
+#define		FIFO_CTRL	0x00000002	/* Allow ctrl. ? */
+#define		FIFO_VALID	0x00000010
+#define 	FIFO_EMPTY	0x00000020
+#define		FIFO_U0		0x00001000	/* Unknown. */
+#define		FIFO_U1		0x00010000
+#define		FIFO_SIZE_BITS 5
+#define		FIFO_SIZE	(1<<FIFO_SIZE_BITS)	// 0x20
+#define 	FIFO_MASK	(FIFO_SIZE-1)	//0x1f    /* at shift left 0xc */
+#define VORTEX_FIFO_ADBDATA 0xe000
+#define VORTEX_FIFO_WTDATA 0xe800
+
+/* CODEC */
+#define VORTEX_CODEC_CTRL 0x11984
+#define VORTEX_CODEC_EN 0x11990
+#define		EN_CODEC	0x00000300
+#define		EN_SPORT	0x00030000
+#define		EN_SPDIF	0x000c0000
+#define VORTEX_CODEC_CHN 0x11880
+#define VORTEX_CODEC_WRITE 0x00800000
+#define VORTEX_CODEC_ADDSHIFT 16
+#define VORTEX_CODEC_ADDMASK 0x7f0000	/* 0x000f0000 */
+#define VORTEX_CODEC_DATSHIFT 0
+#define VORTEX_CODEC_DATMASK 0xffff
+#define VORTEX_CODEC_IO 0x11988
+
+#define VORTEX_SPDIF_FLAGS		0x1005c	/* FIXME */
+#define VORTEX_SPDIF_CFG0		0x119D0
+#define VORTEX_SPDIF_CFG1		0x119D4
+#define VORTEX_SPDIF_SMPRATE	0x11994
+
+/* Sample timer */
+#define VORTEX_SMP_TIME 0x11998
+
+/* IRQ */
+#define VORTEX_IRQ_SOURCE 0x12800	/* Interrupt source flags. */
+#define VORTEX_IRQ_CTRL 0x12804	/* Interrupt source mask. */
+
+#define VORTEX_STAT		0x12808	/* ?? */
+
+#define VORTEX_CTRL 0x1280c
+#define 	CTRL_MIDI_EN 0x00000001
+#define 	CTRL_MIDI_PORT 0x00000060
+#define 	CTRL_GAME_EN 0x00000008
+#define 	CTRL_GAME_PORT 0x00000e00
+#define 	CTRL_IRQ_ENABLE 0x4000
+
+/* write: Timer period config / read: TIMER IRQ ack. */
+#define VORTEX_IRQ_STAT 0x1199c
+
+/* DMA */
+#define VORTEX_DMA_BUFFER 0x10200
+#define VORTEX_ENGINE_CTRL 0x1060c
+#define 	ENGINE_INIT 0x0L
+
+		     /* MIDI *//* GAME. */
+#define VORTEX_MIDI_DATA 0x11000
+#define VORTEX_MIDI_CMD 0x11004	/* Write command / Read status */
+#define VORTEX_GAME_LEGACY 0x11008
+#define VORTEX_CTRL2 0x1100c
+#define 	CTRL2_GAME_ADCMODE 0x40
+#define VORTEX_GAME_AXIS 0x11010
+#define 	AXIS_SIZE 4
+#define		AXIS_RANGE 0x1fff
diff --git a/sound/pci/au88x0/au8830.c b/sound/pci/au88x0/au8830.c
new file mode 100644
index 0000000..d4f2717
--- /dev/null
+++ b/sound/pci/au88x0/au8830.c
@@ -0,0 +1,18 @@
+#include "au8830.h"
+#include "au88x0.h"
+static struct pci_device_id snd_vortex_ids[] = {
+	{PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_2,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
+	{0,}
+};
+
+#include "au88x0_synth.c"
+#include "au88x0_core.c"
+#include "au88x0_pcm.c"
+#include "au88x0_mixer.c"
+#include "au88x0_mpu401.c"
+#include "au88x0_game.c"
+#include "au88x0_eq.c"
+#include "au88x0_a3d.c"
+#include "au88x0_xtalk.c"
+#include "au88x0.c"
diff --git a/sound/pci/au88x0/au8830.h b/sound/pci/au88x0/au8830.h
new file mode 100644
index 0000000..aa77826
--- /dev/null
+++ b/sound/pci/au88x0/au8830.h
@@ -0,0 +1,256 @@
+/*
+    Aureal Vortex Soundcard driver.
+
+    IO addr collected from asp4core.vxd:
+    function    address
+    0005D5A0    13004
+    00080674    14004
+    00080AFF    12818
+
+ */
+
+#define CHIP_AU8830
+
+#define CARD_NAME "Aureal Vortex 2 3D Sound Processor"
+#define CARD_NAME_SHORT "au8830"
+
+#define NR_ADB 0x20
+#define NR_SRC 0x10
+#define NR_A3D 0x10
+#define NR_MIXIN 0x20
+#define NR_MIXOUT 0x10
+#define NR_WT 0x40
+
+/* ADBDMA */
+#define VORTEX_ADBDMA_STAT 0x27e00	/* read only, subbuffer, DMA pos */
+#define		POS_MASK 0x00000fff
+#define     POS_SHIFT 0x0
+#define 	ADB_SUBBUF_MASK 0x00003000	/* ADB only. */
+#define     ADB_SUBBUF_SHIFT 0xc	/* ADB only. */
+#define VORTEX_ADBDMA_CTRL 0x27a00	/* write only; format, flags, DMA pos */
+#define		OFFSET_MASK 0x00000fff
+#define     OFFSET_SHIFT 0x0
+#define		IE_MASK 0x00001000	/* interrupt enable. */
+#define     IE_SHIFT 0xc
+#define     DIR_MASK 0x00002000	/* Direction. */
+#define     DIR_SHIFT 0xd
+#define		FMT_MASK 0x0003c000
+#define		FMT_SHIFT 0xe
+#define		ADB_FIFO_EN_SHIFT	0x15
+#define		ADB_FIFO_EN			(1 << 0x15)
+// The ADB masks and shift also are valid for the wtdma, except if specified otherwise.
+#define VORTEX_ADBDMA_BUFCFG0 0x27800
+#define VORTEX_ADBDMA_BUFCFG1 0x27804
+#define VORTEX_ADBDMA_BUFBASE 0x27400
+#define VORTEX_ADBDMA_START 0x27c00	/* Which subbuffer starts */
+
+#define VORTEX_ADBDMA_STATUS 0x27A90	/* stored at AdbDma->this_10 / 2 DWORD in size. */
+/* Starting at the MSB, each pair of bits seem to be the current DMA page. */
+/* This current page bits are consistent (same value) with VORTEX_ADBDMA_STAT) */
+
+/* DMA */
+#define VORTEX_ENGINE_CTRL 0x27ae8
+#define 	ENGINE_INIT 0x1380000
+
+/* WTDMA */
+#define VORTEX_WTDMA_CTRL 0x27900	/* format, DMA pos */
+#define VORTEX_WTDMA_STAT 0x27d00	/* DMA subbuf, DMA pos */
+#define     WT_SUBBUF_MASK 0x3
+#define     WT_SUBBUF_SHIFT 0xc
+#define VORTEX_WTDMA_BUFBASE 0x27000
+#define VORTEX_WTDMA_BUFCFG0 0x27600
+#define VORTEX_WTDMA_BUFCFG1 0x27604
+#define VORTEX_WTDMA_START 0x27b00	/* which subbuffer is first */
+
+/* ADB */
+#define VORTEX_ADB_SR 0x28400	/* Samplerates enable/disable */
+#define VORTEX_ADB_RTBASE 0x28000
+#define VORTEX_ADB_RTBASE_COUNT 173
+#define VORTEX_ADB_CHNBASE 0x282b4
+#define VORTEX_ADB_CHNBASE_COUNT 24
+#define 	ROUTE_MASK	0xffff
+#define		SOURCE_MASK	0xff00
+#define     ADB_MASK   0xff
+#define		ADB_SHIFT 0x8
+/* ADB address */
+#define		OFFSET_ADBDMA	0x00
+#define		OFFSET_ADBDMAB	0x20
+#define		OFFSET_SRCIN	0x40
+#define		OFFSET_SRCOUT	0x20	/* ch 0x11 */
+#define		OFFSET_MIXIN	0x50	/* ch 0x11 */
+#define		OFFSET_MIXOUT	0x30	/* ch 0x11 */
+#define		OFFSET_CODECIN	0x70 /* ch 0x11 */	/* adb source */
+#define		OFFSET_CODECOUT	0x88 /* ch 0x11 */	/* adb target */
+#define		OFFSET_SPORTIN	0x78	/* ch 0x13 ADB source. 2 routes. */
+#define		OFFSET_SPORTOUT	0x90	/* ch 0x13 ADB sink. 2 routes. */
+#define		OFFSET_SPDIFIN	0x7A	/* ch 0x14 ADB source. */
+#define		OFFSET_SPDIFOUT	0x92	/* ch 0x14 ADB sink. */
+#define		OFFSET_AC98IN	0x7c	/* ch 0x14 ADB source. */
+#define		OFFSET_AC98OUT	0x94	/* ch 0x14 ADB sink. */
+#define		OFFSET_EQIN		0xa0	/* ch 0x11 */
+#define		OFFSET_EQOUT	0x7e /* ch 0x11 */	/* 2 routes on ch 0x11 */
+#define		OFFSET_A3DIN	0x70	/* ADB sink. */
+#define		OFFSET_A3DOUT	0xA6	/* ADB source. 2 routes per slice = 8 */
+#define		OFFSET_WT0		0x40	/* WT bank 0 output. 0x40 - 0x65 */
+#define		OFFSET_WT1		0x80	/* WT bank 1 output. 0x80 - 0xA5 */
+/* WT sources offset : 0x00-0x1f Direct stream. */
+/* WT sources offset : 0x20-0x25 Mixed Output. */
+#define		OFFSET_XTALKOUT	0x66	/* crosstalk canceller (source) 2 routes */
+#define		OFFSET_XTALKIN	0x96	/* crosstalk canceller (sink). 10 routes */
+#define		OFFSET_EFXOUT	0x68	/* ADB source. 8 routes. */
+#define		OFFSET_EFXIN	0x80	/* ADB sink. 8 routes. */
+
+/* ADB route translate helper */
+#define ADB_DMA(x) (x)
+#define ADB_SRCOUT(x) (x + OFFSET_SRCOUT)
+#define ADB_SRCIN(x) (x + OFFSET_SRCIN)
+#define ADB_MIXOUT(x) (x + OFFSET_MIXOUT)
+#define ADB_MIXIN(x) (x + OFFSET_MIXIN)
+#define ADB_CODECIN(x) (x + OFFSET_CODECIN)
+#define ADB_CODECOUT(x) (x + OFFSET_CODECOUT)
+#define ADB_SPORTIN(x) (x + OFFSET_SPORTIN)
+#define ADB_SPORTOUT(x) (x + OFFSET_SPORTOUT)
+#define ADB_SPDIFIN(x)	(x + OFFSET_SPDIFIN)
+#define ADB_SPDIFOUT(x)	(x + OFFSET_SPDIFOUT)
+#define ADB_EQIN(x) (x + OFFSET_EQIN)
+#define ADB_EQOUT(x) (x + OFFSET_EQOUT)
+#define ADB_A3DOUT(x) (x + OFFSET_A3DOUT)	/* 0x10 A3D blocks */
+#define ADB_A3DIN(x) (x + OFFSET_A3DIN)
+//#define ADB_WTOUT(x) ((x<x20)?(x + OFFSET_WT0):(x + OFFSET_WT1))
+#define ADB_WTOUT(x,y) (((x)==0)?((y) + OFFSET_WT0):((y) + OFFSET_WT1))
+#define ADB_XTALKIN(x) ((x) + OFFSET_XTALKIN)
+#define ADB_XTALKOUT(x) ((x) + OFFSET_XTALKOUT)
+
+#define MIX_DEFIGAIN 0x08
+#define MIX_DEFOGAIN 0x08	/* 0x8->6dB  (6dB = x4) 16 to 18 bit conversion? */
+
+/* MIXER */
+#define VORTEX_MIXER_SR 0x21f00
+#define VORTEX_MIXER_CLIP 0x21f80
+#define VORTEX_MIXER_CHNBASE 0x21e40
+#define VORTEX_MIXER_RTBASE 0x21e00
+#define 	MIXER_RTBASE_SIZE 0x38
+#define VORTEX_MIX_ENIN 0x21a00	/* Input enable bits. 4 bits wide. */
+#define VORTEX_MIX_SMP 0x21c00	/* wave data buffers. AU8820: 0x9c00 */
+
+/* MIX */
+#define VORTEX_MIX_INVOL_B 0x20000	/* Input volume current */
+#define VORTEX_MIX_VOL_B 0x20800	/* Output Volume current */
+#define VORTEX_MIX_INVOL_A 0x21000	/* Input Volume target */
+#define VORTEX_MIX_VOL_A 0x21800	/* Output Volume target */
+
+#define 	VOL_MIN 0x80	/* Input volume when muted. */
+#define		VOL_MAX 0x7f	/* FIXME: Not confirmed! Just guessed. */
+
+/* SRC */
+#define VORTEX_SRC_CHNBASE		0x26c40
+#define VORTEX_SRC_RTBASE		0x26c00
+#define VORTEX_SRCBLOCK_SR		0x26cc0
+#define VORTEX_SRC_SOURCE		0x26cc4
+#define VORTEX_SRC_SOURCESIZE	0x26cc8
+/* Params
+	0x26e00	: 1 U0
+	0x26e40	: 2 CR
+	0x26e80	: 3 U3
+	0x26ec0	: 4 DRIFT1
+	0x26f00 : 5 U1
+	0x26f40	: 6 DRIFT2
+	0x26f80	: 7 U2 : Target rate, direction
+*/
+
+#define VORTEX_SRC_CONVRATIO	0x26e40
+#define VORTEX_SRC_DRIFT0		0x26e80
+#define VORTEX_SRC_DRIFT1		0x26ec0
+#define VORTEX_SRC_DRIFT2		0x26f40
+#define VORTEX_SRC_U0			0x26e00
+#define		U0_SLOWLOCK		0x200
+#define VORTEX_SRC_U1			0x26f00
+#define VORTEX_SRC_U2			0x26f80
+#define VORTEX_SRC_DATA			0x26800	/* 0xc800 */
+#define VORTEX_SRC_DATA0		0x26000
+
+/* FIFO */
+#define VORTEX_FIFO_ADBCTRL 0x16100	/* Control bits. */
+#define VORTEX_FIFO_WTCTRL 0x16000
+#define		FIFO_RDONLY	0x00000001
+#define		FIFO_CTRL	0x00000002	/* Allow ctrl. ? */
+#define		FIFO_VALID	0x00000010
+#define 	FIFO_EMPTY	0x00000020
+#define		FIFO_U0		0x00002000	/* Unknown. */
+#define		FIFO_U1		0x00040000
+#define		FIFO_SIZE_BITS 6
+#define		FIFO_SIZE	(1<<(FIFO_SIZE_BITS))	// 0x40
+#define 	FIFO_MASK	(FIFO_SIZE-1)	//0x3f    /* at shift left 0xc */
+#define 	FIFO_BITS	0x1c400000
+#define VORTEX_FIFO_ADBDATA 0x14000
+#define VORTEX_FIFO_WTDATA 0x10000
+
+#define VORTEX_FIFO_GIRT	0x17000	/* wt0, wt1, adb */
+#define		GIRT_COUNT	3
+
+/* CODEC */
+
+#define VORTEX_CODEC_CHN 0x29080	/* The name "CHN" is wrong. */
+
+#define VORTEX_CODEC_CTRL 0x29184
+#define VORTEX_CODEC_IO 0x29188
+#define 	VORTEX_CODEC_WRITE 0x00800000
+#define 	VORTEX_CODEC_ADDSHIFT 16
+#define 	VORTEX_CODEC_ADDMASK 0x7f0000	/* 0x000f0000 */
+#define 	VORTEX_CODEC_DATSHIFT 0
+#define 	VORTEX_CODEC_DATMASK 0xffff
+
+#define VORTEX_CODEC_SPORTCTRL 0x2918c
+
+#define VORTEX_CODEC_EN 0x29190
+#define		EN_AUDIO0		0x00000300
+#define		EN_MODEM		0x00000c00
+#define		EN_AUDIO1		0x00003000
+#define		EN_SPORT		0x00030000
+#define		EN_SPDIF		0x000c0000
+#define		EN_CODEC		(EN_AUDIO1 | EN_AUDIO0)
+
+#define VORTEX_SPDIF_SMPRATE	0x29194
+
+#define VORTEX_SPDIF_FLAGS		0x2205c
+#define VORTEX_SPDIF_CFG0		0x291D0	/* status data */
+#define VORTEX_SPDIF_CFG1		0x291D4
+
+#define VORTEX_SMP_TIME			0x29198	/* Sample counter/timer */
+#define VORTEX_SMP_TIMER		0x2919c
+#define VORTEX_CODEC2_CTRL		0x291a0
+
+#define VORTEX_MODEM_CTRL		0x291ac
+
+/* IRQ */
+#define VORTEX_IRQ_SOURCE 0x2a000	/* Interrupt source flags. */
+#define VORTEX_IRQ_CTRL 0x2a004	/* Interrupt source mask. */
+
+//#define VORTEX_IRQ_U0 0x2a008 /* ?? */
+#define VORTEX_STAT		0x2a008	/* Some sort of status */
+#define 	STAT_IRQ	0x00000001	/* This bitis set if the IRQ is valid. */
+
+#define VORTEX_CTRL		0x2a00c
+#define 	CTRL_MIDI_EN	0x00000001
+#define 	CTRL_MIDI_PORT	0x00000060
+#define 	CTRL_GAME_EN	0x00000008
+#define 	CTRL_GAME_PORT	0x00000e00
+#define 	CTRL_IRQ_ENABLE	0x00004000
+#define		CTRL_SPDIF		0x00000000	/* unknown. Please find this value */
+#define 	CTRL_SPORT		0x00200000
+#define 	CTRL_RST		0x00800000
+#define 	CTRL_UNKNOWN	0x01000000
+
+/* write: Timer period config / read: TIMER IRQ ack. */
+#define VORTEX_IRQ_STAT 0x2919c
+
+		     /* MIDI *//* GAME. */
+#define VORTEX_MIDI_DATA 0x28800
+#define VORTEX_MIDI_CMD 0x28804	/* Write command / Read status */
+
+#define VORTEX_GAME_LEGACY 0x28808
+#define VORTEX_CTRL2 0x2880c
+#define		CTRL2_GAME_ADCMODE 0x40
+#define VORTEX_GAME_AXIS 0x28810	/* Axis base register. 4 axis's */
+#define		AXIS_SIZE 4
+#define		AXIS_RANGE 0x1fff
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
new file mode 100644
index 0000000..889b4a1
--- /dev/null
+++ b/sound/pci/au88x0/au88x0.c
@@ -0,0 +1,388 @@
+/*
+ * ALSA driver for the Aureal Vortex family of soundprocessors.
+ * Author: Manuel Jander (mjander@embedded.cl)
+ *
+ *   This driver is the result of the OpenVortex Project from Savannah
+ * (savannah.nongnu.org/projects/openvortex). I would like to thank
+ * the developers of OpenVortex, Jeff Muizelaar and Kester Maddock, from
+ * whom i got plenty of help, and their codebase was invaluable.
+ *   Thanks to the ALSA developers, they helped a lot working out
+ * the ALSA part.
+ *   Thanks also to Sourceforge for maintaining the old binary drivers,
+ * and the forum, where developers could comunicate.
+ *
+ * Now at least i can play Legacy DOOM with MIDI music :-)
+ */
+
+#include "au88x0.h"
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <sound/initval.h>
+
+// module parameters (see "Module Parameters")
+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;
+static int pcifix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 255 };
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+module_param_array(pcifix, int, NULL, 0444);
+MODULE_PARM_DESC(pcifix, "Enable VIA-workaround for " CARD_NAME " soundcard.");
+
+MODULE_DESCRIPTION("Aureal vortex");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Aureal Semiconductor Inc., Aureal Vortex Sound Processor}}");
+
+MODULE_DEVICE_TABLE(pci, snd_vortex_ids);
+
+static void vortex_fix_latency(struct pci_dev *vortex)
+{
+	int rc;
+	if (!(rc = pci_write_config_byte(vortex, 0x40, 0xff))) {
+			printk(KERN_INFO CARD_NAME
+			       ": vortex latency is 0xff\n");
+	} else {
+		printk(KERN_WARNING CARD_NAME
+				": could not set vortex latency: pci error 0x%x\n", rc);
+	}
+}
+
+static void vortex_fix_agp_bridge(struct pci_dev *via)
+{
+	int rc;
+	u8 value;
+
+	/*
+	 * only set the bit (Extend PCI#2 Internal Master for
+	 * Efficient Handling of Dummy Requests) if the can
+	 * read the config and it is not already set
+	 */
+
+	if (!(rc = pci_read_config_byte(via, 0x42, &value))
+			&& ((value & 0x10)
+				|| !(rc = pci_write_config_byte(via, 0x42, value | 0x10)))) {
+		printk(KERN_INFO CARD_NAME
+				": bridge config is 0x%x\n", value | 0x10);
+	} else {
+		printk(KERN_WARNING CARD_NAME
+				": could not set vortex latency: pci error 0x%x\n", rc);
+	}
+}
+
+static void __devinit snd_vortex_workaround(struct pci_dev *vortex, int fix)
+{
+	struct pci_dev *via;
+
+	/* autodetect if workarounds are required */
+	if (fix == 255) {
+		/* VIA KT133 */
+		via = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8365_1, NULL);
+		/* VIA Apollo */
+		if (via == NULL) {
+			via = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C598_1, NULL);
+		}
+		/* AMD Irongate */
+		if (via == NULL) {
+			via = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL);
+		}
+		if (via) {
+			printk(KERN_INFO CARD_NAME ": Activating latency workaround...\n");
+			vortex_fix_latency(vortex);
+			vortex_fix_agp_bridge(via);
+		}
+	} else {
+		if (fix & 0x1)
+			vortex_fix_latency(vortex);
+		if ((fix & 0x2) && (via = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8365_1, NULL)))
+			vortex_fix_agp_bridge(via);
+		if ((fix & 0x4) && (via = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C598_1, NULL)))
+			vortex_fix_agp_bridge(via);
+		if ((fix & 0x8) && (via = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL)))
+			vortex_fix_agp_bridge(via);
+	}
+}
+
+// component-destructor
+// (see "Management of Cards and Components")
+static int snd_vortex_dev_free(snd_device_t * device)
+{
+	vortex_t *vortex = device->device_data;
+
+	vortex_gameport_unregister(vortex);
+	vortex_core_shutdown(vortex);
+	// Take down PCI interface.
+	synchronize_irq(vortex->irq);
+	free_irq(vortex->irq, vortex);
+	pci_release_regions(vortex->pci_dev);
+	pci_disable_device(vortex->pci_dev);
+	kfree(vortex);
+
+	return 0;
+}
+
+// chip-specific constructor
+// (see "Management of Cards and Components")
+static int __devinit
+snd_vortex_create(snd_card_t * card, struct pci_dev *pci, vortex_t ** rchip)
+{
+	vortex_t *chip;
+	int err;
+	static snd_device_ops_t ops = {
+		.dev_free = snd_vortex_dev_free,
+	};
+
+	*rchip = NULL;
+
+	// check PCI availability (DMA).
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	if (!pci_dma_supported(pci, VORTEX_DMA_MASK)) {
+		printk(KERN_ERR "error to set DMA mask\n");
+		return -ENXIO;
+	}
+	pci_set_dma_mask(pci, VORTEX_DMA_MASK);
+
+	chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	chip->card = card;
+
+	// initialize the stuff
+	chip->pci_dev = pci;
+	chip->io = pci_resource_start(pci, 0);
+	chip->vendor = pci->vendor;
+	chip->device = pci->device;
+	chip->card = card;
+	chip->irq = -1;
+
+	// (1) PCI resource allocation
+	// Get MMIO area
+	//
+	if ((err = pci_request_regions(pci, CARD_NAME_SHORT)) != 0)
+		goto regions_out;
+
+	chip->mmio = ioremap_nocache(pci_resource_start(pci, 0),
+	                             pci_resource_len(pci, 0));
+	if (!chip->mmio) {
+		printk(KERN_ERR "MMIO area remap failed.\n");
+		err = -ENOMEM;
+		goto ioremap_out;
+	}
+
+	/* Init audio core.
+	 * This must be done before we do request_irq otherwise we can get spurious
+	 * interupts that we do not handle properly and make a mess of things */
+	if ((err = vortex_core_init(chip)) != 0) {
+		printk(KERN_ERR "hw core init failed\n");
+		goto core_out;
+	}
+
+	if ((err = request_irq(pci->irq, vortex_interrupt,
+	                       SA_INTERRUPT | SA_SHIRQ, CARD_NAME_SHORT,
+	                       chip)) != 0) {
+		printk(KERN_ERR "cannot grab irq\n");
+		goto irq_out;
+	}
+	chip->irq = pci->irq;
+
+	pci_set_master(pci);
+	// End of PCI setup.
+
+	// Register alsa root device.
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		goto alloc_out;
+	}
+
+	*rchip = chip;
+
+	return 0;
+
+      alloc_out:
+	synchronize_irq(chip->irq);
+	free_irq(chip->irq, chip);
+      irq_out:
+	vortex_core_shutdown(chip);
+      core_out:
+	iounmap(chip->mmio);
+      ioremap_out:
+	pci_release_regions(chip->pci_dev);
+      regions_out:
+	pci_disable_device(chip->pci_dev);
+	//FIXME: this not the right place to unregister the gameport
+	vortex_gameport_unregister(chip);
+	return err;
+}
+
+// constructor -- see "Constructor" sub-section
+static int __devinit
+snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+	static int dev;
+	snd_card_t *card;
+	vortex_t *chip;
+	int err;
+
+	// (1)
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+	// (2)
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	// (3)
+	if ((err = snd_vortex_create(card, pci, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_vortex_workaround(pci, pcifix[dev]);
+	// (4) Alloc components.
+	// ADB pcm.
+	if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_ADB)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#ifndef CHIP_AU8820
+	// ADB SPDIF
+	if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_SPDIF, 1)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	// A3D
+	if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_A3D, NR_A3D)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+	/*
+	   // ADB I2S
+	   if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_I2S, 1)) < 0) {
+	   snd_card_free(card);
+	   return err;
+	   }
+	 */
+#ifndef CHIP_AU8810
+	// WT pcm.
+	if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_WT, NR_WT)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+	// snd_ac97_mixer and Vortex mixer.
+	if ((err = snd_vortex_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_vortex_midi(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	vortex_gameport_register(chip);
+
+#if 0
+	if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_VORTEX_SYNTH,
+			       sizeof(snd_vortex_synth_arg_t), &wave) < 0
+	    || wave == NULL) {
+		snd_printk("Can't initialize Aureal wavetable synth\n");
+	} else {
+		snd_vortex_synth_arg_t *arg;
+
+		arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
+		strcpy(wave->name, "Aureal Synth");
+		arg->hwptr = vortex;
+		arg->index = 1;
+		arg->seq_ports = seq_ports[dev];
+		arg->max_voices = max_synth_voices[dev];
+	}
+#endif
+
+	// (5)
+	strcpy(card->driver, CARD_NAME_SHORT);
+	strcpy(card->shortname, CARD_NAME_SHORT);
+	sprintf(card->longname, "%s at 0x%lx irq %i",
+		card->shortname, chip->io, chip->irq);
+
+	if ((err = pci_read_config_word(pci, PCI_DEVICE_ID,
+				  &(chip->device))) < 0) {
+		snd_card_free(card);
+		return err;
+	}	
+	if ((err = pci_read_config_word(pci, PCI_VENDOR_ID,
+				  &(chip->vendor))) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = pci_read_config_byte(pci, PCI_REVISION_ID,
+				  &(chip->rev))) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#ifdef CHIP_AU8830
+	if ((chip->rev) != 0xfe && (chip->rev) != 0xfa) {
+		printk(KERN_ALERT
+		       "vortex: The revision (%x) of your card has not been seen before.\n",
+		       chip->rev);
+		printk(KERN_ALERT
+		       "vortex: Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n");
+		snd_card_free(card);
+		err = -ENODEV;
+		return err;
+	}
+#endif
+
+	// (6)
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	// (7)
+	pci_set_drvdata(pci, card);
+	dev++;
+	vortex_connect_default(chip, 1);
+	vortex_enable_int(chip);
+	return 0;
+}
+
+// destructor -- see "Destructor" sub-section
+static void __devexit snd_vortex_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+// pci_driver definition
+static struct pci_driver driver = {
+	.name = CARD_NAME_SHORT,
+	.id_table = snd_vortex_ids,
+	.probe = snd_vortex_probe,
+	.remove = __devexit_p(snd_vortex_remove),
+};
+
+// initialization of the module
+static int __init alsa_card_vortex_init(void)
+{
+	return pci_module_init(&driver);
+}
+
+// clean up the module
+static void __exit alsa_card_vortex_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_vortex_init)
+module_exit(alsa_card_vortex_exit)
diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h
new file mode 100644
index 0000000..ee1ede1
--- /dev/null
+++ b/sound/pci/au88x0/au88x0.h
@@ -0,0 +1,284 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+ 
+#ifndef __SOUND_AU88X0_H
+#define __SOUND_AU88X0_H
+
+#ifdef __KERNEL__
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/mpu401.h>
+#include <sound/hwdep.h>
+#include <sound/ac97_codec.h>
+
+#endif
+
+#ifndef CHIP_AU8820
+#include "au88x0_eq.h"
+#include "au88x0_a3d.h"
+#endif
+#ifndef CHIP_AU8810
+#include "au88x0_wt.h"
+#endif
+
+#define	VORTEX_DMA_MASK	0xffffffff
+
+#define	hwread(x,y) readl((x)+((y)>>2))
+#define	hwwrite(x,y,z) writel((z),(x)+((y)>>2))
+
+/* Vortex MPU401 defines. */
+#define	MIDI_CLOCK_DIV		0x61
+/* Standart MPU401 defines. */
+#define	MPU401_RESET		0xff
+#define	MPU401_ENTER_UART	0x3f
+#define	MPU401_ACK		0xfe
+
+// Get src register value to convert from x to y.
+#define	SRC_RATIO(x,y)		((((x<<15)/y) + 1)/2)
+
+/* FIFO software state constants. */
+#define FIFO_STOP 0
+#define FIFO_START 1
+#define FIFO_PAUSE 2
+
+/* IRQ flags */
+#define IRQ_ERR_MASK	0x00ff
+#define IRQ_FATAL	0x0001
+#define IRQ_PARITY	0x0002
+#define IRQ_REG		0x0004
+#define IRQ_FIFO	0x0008
+#define IRQ_DMA		0x0010
+#define IRQ_PCMOUT	0x0020	/* PCM OUT page crossing */
+#define IRQ_TIMER	0x1000
+#define IRQ_MIDI	0x2000
+#define IRQ_MODEM	0x4000
+
+/* ADB Resource */
+#define VORTEX_RESOURCE_DMA	0x00000000
+#define VORTEX_RESOURCE_SRC	0x00000001
+#define VORTEX_RESOURCE_MIXIN	0x00000002
+#define VORTEX_RESOURCE_MIXOUT	0x00000003
+#define VORTEX_RESOURCE_A3D	0x00000004
+#define VORTEX_RESOURCE_LAST	0x00000005
+
+/* Check for SDAC bit in "Extended audio ID" AC97 register */
+//#define VORTEX_IS_QUAD(x) (((x)->codec == NULL) ?  0 : ((x)->codec->ext_id&0x80))
+#define VORTEX_IS_QUAD(x) ((x)->isquad)
+/* Check if chip has bug. */
+#define IS_BAD_CHIP(x) (\
+	(x->rev == 0xfe && x->device == PCI_DEVICE_ID_AUREAL_VORTEX_2) || \
+	(x->rev == 0xfe && x->device == PCI_DEVICE_ID_AUREAL_ADVANTAGE))
+
+
+/* PCM devices */
+#define VORTEX_PCM_ADB		0
+#define VORTEX_PCM_SPDIF	1
+#define VORTEX_PCM_A3D		2
+#define VORTEX_PCM_WT		3
+#define VORTEX_PCM_I2S		4
+#define VORTEX_PCM_LAST		5
+
+#define MIX_CAPT(x) (vortex->mixcapt[x])
+#define MIX_PLAYB(x) (vortex->mixplayb[x])
+#define MIX_SPDIF(x) (vortex->mixspdif[x])
+
+#define NR_WTPB 0x20		/* WT channels per eahc bank. */
+
+/* Structs */
+typedef struct {
+	//int this_08;          /* Still unknown */
+	int fifo_enabled;	/* this_24 */
+	int fifo_status;	/* this_1c */
+	int dma_ctrl;		/* this_78 (ADB), this_7c (WT) */
+	int dma_unknown;	/* this_74 (ADB), this_78 (WT). WDM: +8 */
+	int cfg0;
+	int cfg1;
+
+	int nr_ch;		/* Nr of PCM channels in use */
+	int type;		/* Output type (ac97, a3d, spdif, i2s, dsp) */
+	int dma;		/* Hardware DMA index. */
+	int dir;		/* Stream Direction. */
+	u32 resources[5];
+
+	/* Virtual page extender stuff */
+	int nr_periods;
+	int period_bytes;
+	snd_pcm_sgbuf_t *sgbuf;	/* DMA Scatter Gather struct */
+	int period_real;
+	int period_virt;
+
+	snd_pcm_substream_t *substream;
+} stream_t;
+
+typedef struct snd_vortex vortex_t;
+struct snd_vortex {
+	/* ALSA structs. */
+	snd_card_t *card;
+	snd_pcm_t *pcm[VORTEX_PCM_LAST];
+
+	snd_rawmidi_t *rmidi;	/* Legacy Midi interface. */
+	ac97_t *codec;
+
+	/* Stream structs. */
+	stream_t dma_adb[NR_ADB];
+	int spdif_sr;
+#ifndef CHIP_AU8810
+	stream_t dma_wt[NR_WT];
+	wt_voice_t wt_voice[NR_WT];	/* WT register cache. */
+	char mixwt[(NR_WT / NR_WTPB) * 6];	/* WT mixin objects */
+#endif
+
+	/* Global resources */
+	s8 mixcapt[2];
+	s8 mixplayb[4];
+#ifndef CHIP_AU8820
+	s8 mixspdif[2];
+	s8 mixa3d[2];	/* mixers which collect all a3d streams. */
+	s8 mixxtlk[2];	/* crosstalk canceler mixer inputs. */
+#endif
+	u32 fixed_res[5];
+
+#ifndef CHIP_AU8820
+	/* Hardware equalizer structs */
+	eqlzr_t eq;
+	/* A3D structs */
+	a3dsrc_t a3d[NR_A3D];
+	/* Xtalk canceler */
+	int xt_mode;		/* 1: speakers, 0:headphones. */
+#endif
+
+	int isquad;		/* cache of extended ID codec flag. */
+
+	/* Gameport stuff. */
+	struct gameport *gameport;
+
+	/* PCI hardware resources */
+	unsigned long io;
+	unsigned long __iomem *mmio;
+	unsigned int irq;
+	spinlock_t lock;
+
+	/* PCI device */
+	struct pci_dev *pci_dev;
+	u16 vendor;
+	u16 device;
+	u8 rev;
+};
+
+/* Functions. */
+
+/* SRC */
+static void vortex_adb_setsrc(vortex_t * vortex, int adbdma,
+			      unsigned int cvrt, int dir);
+
+/* DMA Engines. */
+static void vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
+				     snd_pcm_sgbuf_t * sgbuf, int size,
+				     int count);
+static void vortex_adbdma_setmode(vortex_t * vortex, int adbdma, int ie,
+				  int dir, int fmt, int d,
+				  unsigned long offset);
+static void vortex_adbdma_setstartbuffer(vortex_t * vortex, int adbdma, int sb);
+#ifndef CHIP_AU8810
+static void vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma,
+				    snd_pcm_sgbuf_t * sgbuf, int size,
+				    int count);
+static void vortex_wtdma_setmode(vortex_t * vortex, int wtdma, int ie, int fmt, int d,	/*int e, */
+				 unsigned long offset);
+static void vortex_wtdma_setstartbuffer(vortex_t * vortex, int wtdma, int sb);
+#endif
+
+static void vortex_adbdma_startfifo(vortex_t * vortex, int adbdma);
+//static void vortex_adbdma_stopfifo(vortex_t *vortex, int adbdma);
+static void vortex_adbdma_pausefifo(vortex_t * vortex, int adbdma);
+static void vortex_adbdma_resumefifo(vortex_t * vortex, int adbdma);
+static int inline vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma);
+static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma);
+
+#ifndef CHIP_AU8810
+static void vortex_wtdma_startfifo(vortex_t * vortex, int wtdma);
+static void vortex_wtdma_stopfifo(vortex_t * vortex, int wtdma);
+static void vortex_wtdma_pausefifo(vortex_t * vortex, int wtdma);
+static void vortex_wtdma_resumefifo(vortex_t * vortex, int wtdma);
+static int inline vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma);
+#endif
+
+/* global stuff. */
+static void vortex_codec_init(vortex_t * vortex);
+static void vortex_codec_write(ac97_t * codec, unsigned short addr,
+			       unsigned short data);
+static unsigned short vortex_codec_read(ac97_t * codec, unsigned short addr);
+static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode);
+
+static int vortex_core_init(vortex_t * card);
+static int vortex_core_shutdown(vortex_t * card);
+static void vortex_enable_int(vortex_t * card);
+static irqreturn_t vortex_interrupt(int irq, void *dev_id,
+				    struct pt_regs *regs);
+static int vortex_alsafmt_aspfmt(int alsafmt);
+
+/* Connection  stuff. */
+static void vortex_connect_default(vortex_t * vortex, int en);
+static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch,
+				 int dir, int type);
+static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out,
+				  int restype);
+#ifndef CHIP_AU8810
+static int vortex_wt_allocroute(vortex_t * vortex, int dma, int nr_ch);
+static void vortex_wt_connect(vortex_t * vortex, int en);
+static void vortex_wt_init(vortex_t * vortex);
+#endif
+
+static void vortex_route(vortex_t * vortex, int en, unsigned char channel,
+			 unsigned char source, unsigned char dest);
+#if 0
+static void vortex_routes(vortex_t * vortex, int en, unsigned char channel,
+			  unsigned char source, unsigned char dest0,
+			  unsigned char dest1);
+#endif
+static void vortex_connection_mixin_mix(vortex_t * vortex, int en,
+					unsigned char mixin,
+					unsigned char mix, int a);
+static void vortex_mix_setinputvolumebyte(vortex_t * vortex,
+					  unsigned char mix, int mixin,
+					  unsigned char vol);
+static void vortex_mix_setvolumebyte(vortex_t * vortex, unsigned char mix,
+				     unsigned char vol);
+
+/* A3D functions. */
+#ifndef CHIP_AU8820
+static void vortex_Vort3D(vortex_t * v, int en);
+static void vortex_Vort3D_connect(vortex_t * vortex, int en);
+static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en);
+#endif
+
+/* Driver stuff. */
+static int __devinit vortex_gameport_register(vortex_t * card);
+static void vortex_gameport_unregister(vortex_t * card);
+#ifndef CHIP_AU8820
+static int __devinit vortex_eq_init(vortex_t * vortex);
+static int __devexit vortex_eq_free(vortex_t * vortex);
+#endif
+/* ALSA stuff. */
+static int __devinit snd_vortex_new_pcm(vortex_t * vortex, int idx, int nr);
+static int __devinit snd_vortex_mixer(vortex_t * vortex);
+static int __devinit snd_vortex_midi(vortex_t * vortex);
+#endif
diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c
new file mode 100644
index 0000000..9ea2ba7
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_a3d.c
@@ -0,0 +1,914 @@
+/***************************************************************************
+ *            au88x0_a3d.c
+ *
+ *  Fri Jul 18 14:16:22 2003
+ *  Copyright  2003  mjander
+ *  mjander@users.sourceforge.net
+ *
+ * A3D. You may think i'm crazy, but this may work someday. Who knows...
+ ****************************************************************************/
+
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "au88x0_a3d.h"
+#include "au88x0_a3ddata.c"
+#include "au88x0_xtalk.h"
+#include "au88x0.h"
+
+static void
+a3dsrc_SetTimeConsts(a3dsrc_t * a, short HrtfTrack, short ItdTrack,
+		     short GTrack, short CTrack)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	hwwrite(vortex->mmio,
+		a3d_addrA(a->slice, a->source, A3D_A_HrtfTrackTC), HrtfTrack);
+	hwwrite(vortex->mmio,
+		a3d_addrA(a->slice, a->source, A3D_A_ITDTrackTC), ItdTrack);
+	hwwrite(vortex->mmio,
+		a3d_addrA(a->slice, a->source, A3D_A_GainTrackTC), GTrack);
+	hwwrite(vortex->mmio,
+		a3d_addrA(a->slice, a->source, A3D_A_CoeffTrackTC), CTrack);
+}
+
+#if 0
+static void
+a3dsrc_GetTimeConsts(a3dsrc_t * a, short *HrtfTrack, short *ItdTrack,
+		     short *GTrack, short *CTrack)
+{
+	// stub!
+}
+
+#endif
+/* Atmospheric absorbtion. */
+
+static void
+a3dsrc_SetAtmosTarget(a3dsrc_t * a, short aa, short b, short c, short d,
+		      short e)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	hwwrite(vortex->mmio,
+		a3d_addrB(a->slice, a->source, A3D_B_A21Target),
+		(e << 0x10) | d);
+	hwwrite(vortex->mmio,
+		a3d_addrB(a->slice, a->source, A3D_B_B10Target),
+		(b << 0x10) | aa);
+	hwwrite(vortex->mmio,
+		a3d_addrB(a->slice, a->source, A3D_B_B2Target), c);
+}
+
+static void
+a3dsrc_SetAtmosCurrent(a3dsrc_t * a, short aa, short b, short c, short d,
+		       short e)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	hwwrite(vortex->mmio,
+		a3d_addrB(a->slice, a->source, A3D_B_A12Current),
+		(e << 0x10) | d);
+	hwwrite(vortex->mmio,
+		a3d_addrB(a->slice, a->source, A3D_B_B01Current),
+		(b << 0x10) | aa);
+	hwwrite(vortex->mmio,
+		a3d_addrB(a->slice, a->source, A3D_B_B2Current), c);
+}
+
+static void
+a3dsrc_SetAtmosState(a3dsrc_t * a, short x1, short x2, short y1, short y2)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	hwwrite(vortex->mmio, a3d_addrA(a->slice, a->source, A3D_A_x1), x1);
+	hwwrite(vortex->mmio, a3d_addrA(a->slice, a->source, A3D_A_x2), x2);
+	hwwrite(vortex->mmio, a3d_addrA(a->slice, a->source, A3D_A_y1), y1);
+	hwwrite(vortex->mmio, a3d_addrA(a->slice, a->source, A3D_A_y2), y2);
+}
+
+#if 0
+static void
+a3dsrc_GetAtmosTarget(a3dsrc_t * a, short *aa, short *b, short *c,
+		      short *d, short *e)
+{
+}
+static void
+a3dsrc_GetAtmosCurrent(a3dsrc_t * a, short *bb01, short *ab01, short *b2,
+		       short *aa12, short *ba12)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	*aa12 =
+	    hwread(vortex->mmio,
+		   a3d_addrA(a->slice, a->source, A3D_A_A12Current));
+	*ba12 =
+	    hwread(vortex->mmio,
+		   a3d_addrB(a->slice, a->source, A3D_B_A12Current));
+	*ab01 =
+	    hwread(vortex->mmio,
+		   a3d_addrA(a->slice, a->source, A3D_A_B01Current));
+	*bb01 =
+	    hwread(vortex->mmio,
+		   a3d_addrB(a->slice, a->source, A3D_B_B01Current));
+	*b2 =
+	    hwread(vortex->mmio,
+		   a3d_addrA(a->slice, a->source, A3D_A_B2Current));
+}
+
+static void
+a3dsrc_GetAtmosState(a3dsrc_t * a, short *x1, short *x2, short *y1, short *y2)
+{
+
+}
+
+#endif
+/* HRTF */
+
+static void
+a3dsrc_SetHrtfTarget(a3dsrc_t * a, a3d_Hrtf_t const aa, a3d_Hrtf_t const b)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	int i;
+
+	for (i = 0; i < HRTF_SZ; i++)
+		hwwrite(vortex->mmio,
+			a3d_addrB(a->slice, a->source,
+				  A3D_B_HrtfTarget) + (i << 2),
+			(b[i] << 0x10) | aa[i]);
+}
+
+static void
+a3dsrc_SetHrtfCurrent(a3dsrc_t * a, a3d_Hrtf_t const aa, a3d_Hrtf_t const b)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	int i;
+
+	for (i = 0; i < HRTF_SZ; i++)
+		hwwrite(vortex->mmio,
+			a3d_addrB(a->slice, a->source,
+				  A3D_B_HrtfCurrent) + (i << 2),
+			(b[i] << 0x10) | aa[i]);
+}
+
+static void
+a3dsrc_SetHrtfState(a3dsrc_t * a, a3d_Hrtf_t const aa, a3d_Hrtf_t const b)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	int i;
+
+	for (i = 0; i < HRTF_SZ; i++)
+		hwwrite(vortex->mmio,
+			a3d_addrB(a->slice, a->source,
+				  A3D_B_HrtfDelayLine) + (i << 2),
+			(b[i] << 0x10) | aa[i]);
+}
+
+static void a3dsrc_SetHrtfOutput(a3dsrc_t * a, short left, short right)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	hwwrite(vortex->mmio,
+		a3d_addrA(a->slice, a->source, A3D_A_HrtfOutL), left);
+	hwwrite(vortex->mmio,
+		a3d_addrA(a->slice, a->source, A3D_A_HrtfOutR), right);
+}
+
+#if 0
+static void a3dsrc_GetHrtfTarget(a3dsrc_t * a, a3d_Hrtf_t aa, a3d_Hrtf_t b)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	int i;
+
+	for (i = 0; i < HRTF_SZ; i++)
+		aa[i] =
+		    hwread(vortex->mmio,
+			   a3d_addrA(a->slice, a->source,
+				     A3D_A_HrtfTarget + (i << 2)));
+	for (i = 0; i < HRTF_SZ; i++)
+		b[i] =
+		    hwread(vortex->mmio,
+			   a3d_addrB(a->slice, a->source,
+				     A3D_B_HrtfTarget + (i << 2)));
+}
+
+static void a3dsrc_GetHrtfCurrent(a3dsrc_t * a, a3d_Hrtf_t aa, a3d_Hrtf_t b)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	int i;
+
+	for (i = 0; i < HRTF_SZ; i++)
+		aa[i] =
+		    hwread(vortex->mmio,
+			   a3d_addrA(a->slice, a->source,
+				     A3D_A_HrtfCurrent + (i << 2)));
+	for (i = 0; i < HRTF_SZ; i++)
+		b[i] =
+		    hwread(vortex->mmio,
+			   a3d_addrB(a->slice, a->source,
+				     A3D_B_HrtfCurrent + (i << 2)));
+}
+
+static void a3dsrc_GetHrtfState(a3dsrc_t * a, a3d_Hrtf_t aa, a3d_Hrtf_t b)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	int i;
+	// FIXME: verify this!
+	for (i = 0; i < HRTF_SZ; i++)
+		aa[i] =
+		    hwread(vortex->mmio,
+			   a3d_addrA(a->slice, a->source,
+				     A3D_A_HrtfDelayLine + (i << 2)));
+	for (i = 0; i < HRTF_SZ; i++)
+		b[i] =
+		    hwread(vortex->mmio,
+			   a3d_addrB(a->slice, a->source,
+				     A3D_B_HrtfDelayLine + (i << 2)));
+}
+
+static void a3dsrc_GetHrtfOutput(a3dsrc_t * a, short *left, short *right)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	*left =
+	    hwread(vortex->mmio,
+		   a3d_addrA(a->slice, a->source, A3D_A_HrtfOutL));
+	*right =
+	    hwread(vortex->mmio,
+		   a3d_addrA(a->slice, a->source, A3D_A_HrtfOutR));
+}
+
+#endif
+
+/* Interaural Time Difference. 
+ * "The other main clue that humans use to locate sounds, is called 
+ * Interaural Time Difference (ITD). The differences in distance from 
+ * the sound source to a listeners ears means  that the sound will 
+ * reach one ear slightly before the other....", found somewhere with google.*/
+static void a3dsrc_SetItdTarget(a3dsrc_t * a, short litd, short ritd)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+
+	if (litd < 0)
+		litd = 0;
+	if (litd > 0x57FF)
+		litd = 0x57FF;
+	if (ritd < 0)
+		ritd = 0;
+	if (ritd > 0x57FF)
+		ritd = 0x57FF;
+	hwwrite(vortex->mmio,
+		a3d_addrB(a->slice, a->source, A3D_B_ITDTarget),
+		(ritd << 0x10) | litd);
+	//hwwrite(vortex->mmio, addr(0x191DF+5, this04, this08), (ritd<<0x10)|litd);
+}
+
+static void a3dsrc_SetItdCurrent(a3dsrc_t * a, short litd, short ritd)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+
+	if (litd < 0)
+		litd = 0;
+	if (litd > 0x57FF)
+		litd = 0x57FF;
+	if (ritd < 0)
+		ritd = 0;
+	if (ritd > 0x57FF)
+		ritd = 0x57FF;
+	hwwrite(vortex->mmio,
+		a3d_addrB(a->slice, a->source, A3D_B_ITDCurrent),
+		(ritd << 0x10) | litd);
+	//hwwrite(vortex->mmio, addr(0x191DF+1, this04, this08), (ritd<<0x10)|litd);
+}
+
+static void a3dsrc_SetItdDline(a3dsrc_t * a, a3d_ItdDline_t const dline)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	int i;
+	/* 45 != 40 -> Check this ! */
+	for (i = 0; i < DLINE_SZ; i++)
+		hwwrite(vortex->mmio,
+			a3d_addrA(a->slice, a->source,
+				  A3D_A_ITDDelayLine) + (i << 2), dline[i]);
+}
+
+#if 0
+static void a3dsrc_GetItdTarget(a3dsrc_t * a, short *litd, short *ritd)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	*ritd =
+	    hwread(vortex->mmio,
+		   a3d_addrA(a->slice, a->source, A3D_A_ITDTarget));
+	*litd =
+	    hwread(vortex->mmio,
+		   a3d_addrB(a->slice, a->source, A3D_B_ITDTarget));
+}
+
+static void a3dsrc_GetItdCurrent(a3dsrc_t * a, short *litd, short *ritd)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+
+	*ritd =
+	    hwread(vortex->mmio,
+		   a3d_addrA(a->slice, a->source, A3D_A_ITDCurrent));
+	*litd =
+	    hwread(vortex->mmio,
+		   a3d_addrB(a->slice, a->source, A3D_B_ITDCurrent));
+}
+
+static void a3dsrc_GetItdDline(a3dsrc_t * a, a3d_ItdDline_t dline)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	int i;
+
+	for (i = 0; i < DLINE_SZ; i++)
+		dline[i] =
+		    hwread(vortex->mmio,
+			   a3d_addrA(a->slice, a->source,
+				     A3D_A_ITDDelayLine + (i << 2)));
+}
+
+#endif
+/* This is may be used for ILD Interaural Level Difference. */
+
+static void a3dsrc_SetGainTarget(a3dsrc_t * a, short left, short right)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	hwwrite(vortex->mmio,
+		a3d_addrB(a->slice, a->source, A3D_B_GainTarget),
+		(right << 0x10) | left);
+}
+
+static void a3dsrc_SetGainCurrent(a3dsrc_t * a, short left, short right)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	hwwrite(vortex->mmio,
+		a3d_addrB(a->slice, a->source, A3D_B_GainCurrent),
+		(right << 0x10) | left);
+}
+
+#if 0
+static void a3dsrc_GetGainTarget(a3dsrc_t * a, short *left, short *right)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	*right =
+	    hwread(vortex->mmio,
+		   a3d_addrA(a->slice, a->source, A3D_A_GainTarget));
+	*left =
+	    hwread(vortex->mmio,
+		   a3d_addrB(a->slice, a->source, A3D_B_GainTarget));
+}
+
+static void a3dsrc_GetGainCurrent(a3dsrc_t * a, short *left, short *right)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	*right =
+	    hwread(vortex->mmio,
+		   a3d_addrA(a->slice, a->source, A3D_A_GainCurrent));
+	*left =
+	    hwread(vortex->mmio,
+		   a3d_addrB(a->slice, a->source, A3D_B_GainCurrent));
+}
+
+/* CA3dIO this func seems to be inlined all over this place. */
+static void CA3dIO_WriteReg(a3dsrc_t * a, unsigned long addr, short aa, short b)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	hwwrite(vortex->mmio, addr, (aa << 0x10) | b);
+}
+
+#endif
+/* Generic A3D stuff */
+
+static void a3dsrc_SetA3DSampleRate(a3dsrc_t * a, int sr)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	int esp0 = 0;
+
+	esp0 = (((esp0 & 0x7fffffff) | 0xB8000000) & 0x7) | ((sr & 0x1f) << 3);
+	hwwrite(vortex->mmio, A3D_SLICE_Control + ((a->slice) << 0xd), esp0);
+	//hwwrite(vortex->mmio, 0x19C38 + (this08<<0xd), esp0);
+}
+
+static void a3dsrc_EnableA3D(a3dsrc_t * a)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	hwwrite(vortex->mmio, A3D_SLICE_Control + ((a->slice) << 0xd),
+		0xF0000001);
+	//hwwrite(vortex->mmio, 0x19C38 + (this08<<0xd), 0xF0000001);
+}
+
+static void a3dsrc_DisableA3D(a3dsrc_t * a)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	hwwrite(vortex->mmio, A3D_SLICE_Control + ((a->slice) << 0xd),
+		0xF0000000);
+}
+
+static void a3dsrc_SetA3DControlReg(a3dsrc_t * a, unsigned long ctrl)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	hwwrite(vortex->mmio, A3D_SLICE_Control + ((a->slice) << 0xd), ctrl);
+}
+
+static void a3dsrc_SetA3DPointerReg(a3dsrc_t * a, unsigned long ptr)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	hwwrite(vortex->mmio, A3D_SLICE_Pointers + ((a->slice) << 0xd), ptr);
+}
+
+#if 0
+static void a3dsrc_GetA3DSampleRate(a3dsrc_t * a, int *sr)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	*sr = ((hwread(vortex->mmio, A3D_SLICE_Control + (a->slice << 0xd))
+		>> 3) & 0x1f);
+	//*sr = ((hwread(vortex->mmio, 0x19C38 + (this08<<0xd))>>3)&0x1f);
+}
+
+static void a3dsrc_GetA3DControlReg(a3dsrc_t * a, unsigned long *ctrl)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	*ctrl = hwread(vortex->mmio, A3D_SLICE_Control + ((a->slice) << 0xd));
+}
+
+static void a3dsrc_GetA3DPointerReg(a3dsrc_t * a, unsigned long *ptr)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	*ptr = hwread(vortex->mmio, A3D_SLICE_Pointers + ((a->slice) << 0xd));
+}
+
+#endif
+static void a3dsrc_ZeroSliceIO(a3dsrc_t * a)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+	int i;
+
+	for (i = 0; i < 8; i++)
+		hwwrite(vortex->mmio,
+			A3D_SLICE_VDBDest +
+			((((a->slice) << 0xb) + i) << 2), 0);
+	for (i = 0; i < 4; i++)
+		hwwrite(vortex->mmio,
+			A3D_SLICE_VDBSource +
+			((((a->slice) << 0xb) + i) << 2), 0);
+}
+
+/* Reset Single A3D source. */
+static void a3dsrc_ZeroState(a3dsrc_t * a)
+{
+
+	//printk("vortex: ZeroState slice: %d, source %d\n", a->slice, a->source);
+
+	a3dsrc_SetAtmosState(a, 0, 0, 0, 0);
+	a3dsrc_SetHrtfState(a, A3dHrirZeros, A3dHrirZeros);
+	a3dsrc_SetItdDline(a, A3dItdDlineZeros);
+	a3dsrc_SetHrtfOutput(a, 0, 0);
+	a3dsrc_SetTimeConsts(a, 0, 0, 0, 0);
+
+	a3dsrc_SetAtmosCurrent(a, 0, 0, 0, 0, 0);
+	a3dsrc_SetAtmosTarget(a, 0, 0, 0, 0, 0);
+	a3dsrc_SetItdCurrent(a, 0, 0);
+	a3dsrc_SetItdTarget(a, 0, 0);
+	a3dsrc_SetGainCurrent(a, 0, 0);
+	a3dsrc_SetGainTarget(a, 0, 0);
+
+	a3dsrc_SetHrtfCurrent(a, A3dHrirZeros, A3dHrirZeros);
+	a3dsrc_SetHrtfTarget(a, A3dHrirZeros, A3dHrirZeros);
+}
+
+/* Reset entire A3D engine */
+static void a3dsrc_ZeroStateA3D(a3dsrc_t * a)
+{
+	int i, var, var2;
+
+	if ((a->vortex) == NULL) {
+		printk("vortex: ZeroStateA3D: ERROR: a->vortex is NULL\n");
+		return;
+	}
+
+	a3dsrc_SetA3DControlReg(a, 0);
+	a3dsrc_SetA3DPointerReg(a, 0);
+
+	var = a->slice;
+	var2 = a->source;
+	for (i = 0; i < 4; i++) {
+		a->slice = i;
+		a3dsrc_ZeroSliceIO(a);
+		//a3dsrc_ZeroState(a);
+	}
+	a->source = var2;
+	a->slice = var;
+}
+
+/* Program A3D block as pass through */
+static void a3dsrc_ProgramPipe(a3dsrc_t * a)
+{
+	a3dsrc_SetTimeConsts(a, 0, 0, 0, 0);
+	a3dsrc_SetAtmosCurrent(a, 0, 0x4000, 0, 0, 0);
+	a3dsrc_SetAtmosTarget(a, 0x4000, 0, 0, 0, 0);
+	a3dsrc_SetItdCurrent(a, 0, 0);
+	a3dsrc_SetItdTarget(a, 0, 0);
+	a3dsrc_SetGainCurrent(a, 0x7fff, 0x7fff);
+	a3dsrc_SetGainTarget(a, 0x7fff, 0x7fff);
+
+	/* SET HRTF HERE */
+
+	/* Single spike leads to identity transfer function. */
+	a3dsrc_SetHrtfCurrent(a, A3dHrirImpulse, A3dHrirImpulse);
+	a3dsrc_SetHrtfTarget(a, A3dHrirImpulse, A3dHrirImpulse);
+
+	/* Test: Sounds saturated. */
+	//a3dsrc_SetHrtfCurrent(a, A3dHrirSatTest, A3dHrirSatTest);
+	//a3dsrc_SetHrtfTarget(a, A3dHrirSatTest, A3dHrirSatTest);      
+}
+
+/* VDB = Vortex audio Dataflow Bus */
+#if 0
+static void a3dsrc_ClearVDBData(a3dsrc_t * a, unsigned long aa)
+{
+	vortex_t *vortex = (vortex_t *) (a->vortex);
+
+	// ((aa >> 2) << 8) - (aa >> 2)
+	hwwrite(vortex->mmio,
+		a3d_addrS(a->slice, A3D_SLICE_VDBDest) + (a->source << 2), 0);
+	hwwrite(vortex->mmio,
+		a3d_addrS(a->slice,
+			  A3D_SLICE_VDBDest + 4) + (a->source << 2), 0);
+	/*
+	   hwwrite(vortex->mmio, 0x19c00 + (((aa>>2)*255*4)+aa)*8, 0);
+	   hwwrite(vortex->mmio, 0x19c04 + (((aa>>2)*255*4)+aa)*8, 0);
+	 */
+}
+#endif
+
+/* A3D HwSource stuff. */
+
+static void vortex_A3dSourceHw_Initialize(vortex_t * v, int source, int slice)
+{
+	a3dsrc_t *a3dsrc = &(v->a3d[source + (slice * 4)]);
+	//a3dsrc_t *a3dsrc = &(v->a3d[source + (slice*4)]);
+
+	a3dsrc->vortex = (void *)v;
+	a3dsrc->source = source;	/* source */
+	a3dsrc->slice = slice;	/* slice */
+	a3dsrc_ZeroState(a3dsrc);
+	/* Added by me. */
+	a3dsrc_SetA3DSampleRate(a3dsrc, 0x11);
+}
+
+static int Vort3DRend_Initialize(vortex_t * v, unsigned short mode)
+{
+	v->xt_mode = mode;	/* this_14 */
+
+	vortex_XtalkHw_init(v);
+	vortex_XtalkHw_SetGainsAllChan(v);
+	switch (v->xt_mode) {
+	case XT_SPEAKER0:
+		vortex_XtalkHw_ProgramXtalkNarrow(v);
+		break;
+	case XT_SPEAKER1:
+		vortex_XtalkHw_ProgramXtalkWide(v);
+		break;
+	default:
+	case XT_HEADPHONE:
+		vortex_XtalkHw_ProgramPipe(v);
+		break;
+	case XT_DIAMOND:
+		vortex_XtalkHw_ProgramDiamondXtalk(v);
+		break;
+	}
+	vortex_XtalkHw_SetSampleRate(v, 0x11);
+	vortex_XtalkHw_Enable(v);
+	return 0;
+}
+
+/* 3D Sound entry points. */
+
+static int vortex_a3d_register_controls(vortex_t * vortex);
+static void vortex_a3d_unregister_controls(vortex_t * vortex);
+/* A3D base support init/shudown */
+static void vortex_Vort3D(vortex_t * v, int en)
+{
+	int i;
+	if (en) {
+		Vort3DRend_Initialize(v, XT_HEADPHONE);
+		for (i = 0; i < NR_A3D; i++) {
+			vortex_A3dSourceHw_Initialize(v, i % 4, i >> 2);
+			a3dsrc_ZeroStateA3D(&(v->a3d[0]));
+		}
+	} else {
+		vortex_XtalkHw_Disable(v);
+	}
+	/* Register ALSA controls */
+	if (en) {
+		vortex_a3d_register_controls(v);
+	} else {
+		vortex_a3d_unregister_controls(v);
+	}
+}
+
+/* Make A3D subsystem connections. */
+static void vortex_Vort3D_connect(vortex_t * v, int en)
+{
+	int i;
+	
+// Disable AU8810 routes, since they seem to be wrong (in au8810.h).
+#ifdef CHIP_AU8810
+	return;
+#endif
+	
+#if 1
+	/* Alloc Xtalk mixin resources */
+	v->mixxtlk[0] =
+	    vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
+	if (v->mixxtlk[0] < 0) {
+		printk
+		    ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n");
+		return;
+	}
+	v->mixxtlk[1] =
+	    vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
+	if (v->mixxtlk[1] < 0) {
+		printk
+		    ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n");
+		return;
+	}
+#endif
+
+	/* Connect A3D -> XTALK */
+	for (i = 0; i < 4; i++) {
+		// 2 outputs per each A3D slice. 
+		vortex_route(v, en, 0x11, ADB_A3DOUT(i * 2), ADB_XTALKIN(i));
+		vortex_route(v, en, 0x11, ADB_A3DOUT(i * 2) + 1, ADB_XTALKIN(5 + i));
+	}
+#if 0
+	vortex_route(v, en, 0x11, ADB_XTALKOUT(0), ADB_EQIN(2));
+	vortex_route(v, en, 0x11, ADB_XTALKOUT(1), ADB_EQIN(3));
+#else
+	/* Connect XTalk -> mixer */
+	vortex_route(v, en, 0x11, ADB_XTALKOUT(0), ADB_MIXIN(v->mixxtlk[0]));
+	vortex_route(v, en, 0x11, ADB_XTALKOUT(1), ADB_MIXIN(v->mixxtlk[1]));
+	vortex_connection_mixin_mix(v, en, v->mixxtlk[0], v->mixplayb[0], 0);
+	vortex_connection_mixin_mix(v, en, v->mixxtlk[1], v->mixplayb[1], 0);
+	vortex_mix_setinputvolumebyte(v, v->mixplayb[0], v->mixxtlk[0],
+				      en ? MIX_DEFIGAIN : VOL_MIN);
+	vortex_mix_setinputvolumebyte(v, v->mixplayb[1], v->mixxtlk[1],
+				      en ? MIX_DEFIGAIN : VOL_MIN);
+	if (VORTEX_IS_QUAD(v)) {
+		vortex_connection_mixin_mix(v, en, v->mixxtlk[0],
+					    v->mixplayb[2], 0);
+		vortex_connection_mixin_mix(v, en, v->mixxtlk[1],
+					    v->mixplayb[3], 0);
+		vortex_mix_setinputvolumebyte(v, v->mixplayb[2],
+					      v->mixxtlk[0],
+					      en ? MIX_DEFIGAIN : VOL_MIN);
+		vortex_mix_setinputvolumebyte(v, v->mixplayb[3],
+					      v->mixxtlk[1],
+					      en ? MIX_DEFIGAIN : VOL_MIN);
+	}
+#endif
+}
+
+/* Initialize one single A3D source. */
+static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en)
+{
+	if (a->vortex == NULL) {
+		printk
+		    ("vortex: Vort3D_InitializeSource: A3D source not initialized\n");
+		return;
+	}
+	if (en) {
+		a3dsrc_ProgramPipe(a);
+		a3dsrc_SetA3DSampleRate(a, 0x11);
+		a3dsrc_SetTimeConsts(a, HrtfTCDefault,
+				     ItdTCDefault, GainTCDefault,
+				     CoefTCDefault);
+		/* Remark: zero gain is muted. */
+		//a3dsrc_SetGainTarget(a,0,0);
+		//a3dsrc_SetGainCurrent(a,0,0);
+		a3dsrc_EnableA3D(a);
+	} else {
+		a3dsrc_DisableA3D(a);
+		a3dsrc_ZeroState(a);
+	}
+}
+
+/* Conversion of coordinates into 3D parameters. */
+
+static void vortex_a3d_coord2hrtf(a3d_Hrtf_t hrtf, int *coord)
+{
+	/* FIXME: implement this. */
+
+}
+static void vortex_a3d_coord2itd(a3d_Itd_t itd, int *coord)
+{
+	/* FIXME: implement this. */
+
+}
+static void vortex_a3d_coord2ild(a3d_LRGains_t ild, int left, int right)
+{
+	/* FIXME: implement this. */
+
+}
+static void vortex_a3d_translate_filter(a3d_atmos_t filter, int *params)
+{
+	/* FIXME: implement this. */
+
+}
+
+/* ALSA control interface.  */
+
+static int
+snd_vortex_a3d_hrtf_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 6;
+	uinfo->value.integer.min = 0x00000000;
+	uinfo->value.integer.max = 0xffffffff;
+	return 0;
+}
+static int
+snd_vortex_a3d_itd_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0x00000000;
+	uinfo->value.integer.max = 0xffffffff;
+	return 0;
+}
+static int
+snd_vortex_a3d_ild_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0x00000000;
+	uinfo->value.integer.max = 0xffffffff;
+	return 0;
+}
+static int
+snd_vortex_a3d_filter_info(snd_kcontrol_t *
+			   kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 4;
+	uinfo->value.integer.min = 0x00000000;
+	uinfo->value.integer.max = 0xffffffff;
+	return 0;
+}
+
+static int
+snd_vortex_a3d_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	//a3dsrc_t *a = kcontrol->private_data;
+	/* No read yet. Would this be really useable/needed ? */
+
+	return 0;
+}
+
+static int
+snd_vortex_a3d_hrtf_put(snd_kcontrol_t *
+			kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	a3dsrc_t *a = kcontrol->private_data;
+	int changed = 1, i;
+	int coord[6];
+	for (i = 0; i < 6; i++)
+		coord[i] = ucontrol->value.integer.value[i];
+	/* Translate orientation coordinates to a3d params. */
+	vortex_a3d_coord2hrtf(a->hrtf[0], coord);
+	vortex_a3d_coord2hrtf(a->hrtf[1], coord);
+	a3dsrc_SetHrtfTarget(a, a->hrtf[0], a->hrtf[1]);
+	a3dsrc_SetHrtfCurrent(a, a->hrtf[0], a->hrtf[1]);
+	return changed;
+}
+
+static int
+snd_vortex_a3d_itd_put(snd_kcontrol_t *
+		       kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	a3dsrc_t *a = kcontrol->private_data;
+	int coord[6];
+	int i, changed = 1;
+	for (i = 0; i < 6; i++)
+		coord[i] = ucontrol->value.integer.value[i];
+	/* Translate orientation coordinates to a3d params. */
+	vortex_a3d_coord2itd(a->hrtf[0], coord);
+	vortex_a3d_coord2itd(a->hrtf[1], coord);
+	/* Inter aural time difference. */
+	a3dsrc_SetItdTarget(a, a->itd[0], a->itd[1]);
+	a3dsrc_SetItdCurrent(a, a->itd[0], a->itd[1]);
+	a3dsrc_SetItdDline(a, a->dline);
+	return changed;
+}
+
+static int
+snd_vortex_a3d_ild_put(snd_kcontrol_t *
+		       kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	a3dsrc_t *a = kcontrol->private_data;
+	int changed = 1;
+	int l, r;
+	/* There may be some scale tranlation needed here. */
+	l = ucontrol->value.integer.value[0];
+	r = ucontrol->value.integer.value[1];
+	vortex_a3d_coord2ild(a->ild, l, r);
+	/* Left Right panning. */
+	a3dsrc_SetGainTarget(a, l, r);
+	a3dsrc_SetGainCurrent(a, l, r);
+	return changed;
+}
+
+static int
+snd_vortex_a3d_filter_put(snd_kcontrol_t
+			  * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	a3dsrc_t *a = kcontrol->private_data;
+	int i, changed = 1;
+	int params[6];
+	for (i = 0; i < 6; i++)
+		params[i] = ucontrol->value.integer.value[i];
+	/* Translate generic filter params to a3d filter params. */
+	vortex_a3d_translate_filter(a->filter, params);
+	/* Atmospheric absorbtion and filtering. */
+	a3dsrc_SetAtmosTarget(a, a->filter[0],
+			      a->filter[1], a->filter[2],
+			      a->filter[3], a->filter[4]);
+	a3dsrc_SetAtmosCurrent(a, a->filter[0],
+			       a->filter[1], a->filter[2],
+			       a->filter[3], a->filter[4]);
+	return changed;
+}
+
+static snd_kcontrol_new_t vortex_a3d_kcontrol __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+	.name = "Playback PCM advanced processing",
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = snd_vortex_a3d_hrtf_info,
+	.get = snd_vortex_a3d_get,
+	.put = snd_vortex_a3d_hrtf_put,
+};
+
+/* Control (un)registration. */
+static int vortex_a3d_register_controls(vortex_t * vortex)
+{
+	snd_kcontrol_t *kcontrol;
+	int err, i;
+	/* HRTF controls. */
+	for (i = 0; i < NR_A3D; i++) {
+		if ((kcontrol =
+		     snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+			return -ENOMEM;
+		kcontrol->id.numid = CTRLID_HRTF;
+		kcontrol->info = snd_vortex_a3d_hrtf_info;
+		kcontrol->put = snd_vortex_a3d_hrtf_put;
+		if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+			return err;
+	}
+	/* ITD controls. */
+	for (i = 0; i < NR_A3D; i++) {
+		if ((kcontrol =
+		     snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+			return -ENOMEM;
+		kcontrol->id.numid = CTRLID_ITD;
+		kcontrol->info = snd_vortex_a3d_itd_info;
+		kcontrol->put = snd_vortex_a3d_itd_put;
+		if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+			return err;
+	}
+	/* ILD (gains) controls. */
+	for (i = 0; i < NR_A3D; i++) {
+		if ((kcontrol =
+		     snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+			return -ENOMEM;
+		kcontrol->id.numid = CTRLID_GAINS;
+		kcontrol->info = snd_vortex_a3d_ild_info;
+		kcontrol->put = snd_vortex_a3d_ild_put;
+		if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+			return err;
+	}
+	/* Filter controls. */
+	for (i = 0; i < NR_A3D; i++) {
+		if ((kcontrol =
+		     snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+			return -ENOMEM;
+		kcontrol->id.numid = CTRLID_FILTER;
+		kcontrol->info = snd_vortex_a3d_filter_info;
+		kcontrol->put = snd_vortex_a3d_filter_put;
+		if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+			return err;
+	}
+	return 0;
+}
+
+static void vortex_a3d_unregister_controls(vortex_t * vortex)
+{
+
+}
+
+/* End of File*/
diff --git a/sound/pci/au88x0/au88x0_a3d.h b/sound/pci/au88x0/au88x0_a3d.h
new file mode 100644
index 0000000..0584c65
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_a3d.h
@@ -0,0 +1,123 @@
+/***************************************************************************
+ *            au88x0_a3d.h
+ *
+ *  Fri Jul 18 14:16:03 2003
+ *  Copyright  2003  mjander
+ *  mjander@users.sourceforge.net
+ ****************************************************************************/
+
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _AU88X0_A3D_H
+#define _AU88X0_A3D_H
+
+//#include <openal.h>
+
+#define HRTF_SZ 0x38
+#define DLINE_SZ 0x28
+
+#define CTRLID_HRTF		1
+#define CTRLID_ITD		2
+#define CTRLID_ILD		4
+#define CTRLID_FILTER	8
+#define CTRLID_GAINS	16
+
+/* 3D parameter structs */
+typedef unsigned short int a3d_Hrtf_t[HRTF_SZ];
+typedef unsigned short int a3d_ItdDline_t[DLINE_SZ];
+typedef unsigned short int a3d_atmos_t[5];
+typedef unsigned short int a3d_LRGains_t[2];
+typedef unsigned short int a3d_Itd_t[2];
+typedef unsigned short int a3d_Ild_t[2];
+
+typedef struct {
+	void *vortex;		// Formerly CAsp4HwIO*, now vortex_t*.
+	unsigned int source;	/* this_04 */
+	unsigned int slice;	/* this_08 */
+	a3d_Hrtf_t hrtf[2];
+	a3d_Itd_t itd;
+	a3d_Ild_t ild;
+	a3d_ItdDline_t dline;
+	a3d_atmos_t filter;
+} a3dsrc_t;
+
+/* First Register bank */
+
+#define A3D_A_HrtfCurrent	0x18000	/* 56 ULONG */
+#define A3D_A_GainCurrent	0x180E0
+#define A3D_A_GainTarget	0x180E4
+#define A3D_A_A12Current	0x180E8	/* Atmospheric current. */
+#define A3D_A_A21Target		0x180EC	/* Atmospheric target */
+#define A3D_A_B01Current	0x180F0	/* Atmospheric current */
+#define A3D_A_B10Target		0x180F4	/* Atmospheric target */
+#define A3D_A_B2Current		0x180F8	/* Atmospheric current */
+#define A3D_A_B2Target		0x180FC	/* Atmospheric target */
+#define A3D_A_HrtfTarget	0x18100	/* 56 ULONG */
+#define A3D_A_ITDCurrent	0x181E0
+#define A3D_A_ITDTarget		0x181E4
+#define A3D_A_HrtfDelayLine	0x181E8	/* 56 ULONG */
+#define A3D_A_ITDDelayLine	0x182C8	/* 40/45 ULONG */
+#define A3D_A_HrtfTrackTC	0x1837C	/* Time Constants */
+#define A3D_A_GainTrackTC	0x18380
+#define A3D_A_CoeffTrackTC	0x18384
+#define A3D_A_ITDTrackTC	0x18388
+#define A3D_A_x1			0x1838C
+#define A3D_A_x2			0x18390
+#define A3D_A_y1			0x18394
+#define A3D_A_y2			0x18398
+#define A3D_A_HrtfOutL		0x1839C
+#define A3D_A_HrtfOutR		0x183A0
+#define 	A3D_A_TAIL		0x183A4
+
+/* Second register bank */
+#define A3D_B_HrtfCurrent	0x19000	/* 56 ULONG */
+#define A3D_B_GainCurrent	0x190E0
+#define A3D_B_GainTarget	0x190E4
+#define A3D_B_A12Current	0x190E8
+#define A3D_B_A21Target		0x190EC
+#define A3D_B_B01Current	0x190F0
+#define A3D_B_B10Target		0x190F4
+#define A3D_B_B2Current		0x190F8
+#define A3D_B_B2Target		0x190FC
+#define A3D_B_HrtfTarget	0x19100	/* 56 ULONG */
+#define A3D_B_ITDCurrent	0x191E0
+#define A3D_B_ITDTarget		0x191E4
+#define A3D_B_HrtfDelayLine	0x191E8	/* 56 ULONG */
+#define 	A3D_B_TAIL		0x192C8
+
+/* There are 4 slices, 4 a3d each = 16 a3d sources. */
+#define A3D_SLICE_BANK_A		0x18000	/* 4 sources */
+#define A3D_SLICE_BANK_B		0x19000	/* 4 sources */
+#define A3D_SLICE_VDBDest		0x19C00	/* 8 ULONG */
+#define A3D_SLICE_VDBSource		0x19C20	/* 4 ULONG */
+#define A3D_SLICE_ABReg			0x19C30
+#define A3D_SLICE_CReg			0x19C34
+#define A3D_SLICE_Control		0x19C38
+#define A3D_SLICE_DebugReserved	0x19C3c	/* Dangerous! */
+#define A3D_SLICE_Pointers		0x19C40
+#define 	A3D_SLICE_TAIL		0x1A000
+
+// Slice size: 0x2000
+// Source size: 0x3A4, 0x2C8
+
+/* Address generator macro. */
+#define a3d_addrA(slice,source,reg) (((slice)<<0xd)+((source)*0x3A4)+(reg))
+#define a3d_addrB(slice,source,reg) (((slice)<<0xd)+((source)*0x2C8)+(reg))
+#define a3d_addrS(slice,reg) (((slice)<<0xd)+(reg))
+//#define a3d_addr(slice,source,reg) (((reg)>=0x19000) ? a3d_addr2((slice),(source),(reg)) : a3d_addr1((slice),(source),(reg)))
+
+#endif				/* _AU88X0_A3D_H */
diff --git a/sound/pci/au88x0/au88x0_a3ddata.c b/sound/pci/au88x0/au88x0_a3ddata.c
new file mode 100644
index 0000000..6fab4bb
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_a3ddata.c
@@ -0,0 +1,91 @@
+/***************************************************************************
+ *            au88x0_a3ddata.c
+ *
+ *  Wed Nov 19 21:11:32 2003
+ *  Copyright  2003  mjander
+ *  mjander@users.sourceforge.org
+ ****************************************************************************/
+
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library 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.
+ */
+
+/* Constant initializer values. */
+
+static const a3d_Hrtf_t A3dHrirZeros = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0,
+	0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0,
+	0, 0, 0
+};
+
+static const a3d_Hrtf_t A3dHrirImpulse = {
+	0x7fff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0,
+	0, 0, 0
+};
+
+static const a3d_Hrtf_t A3dHrirOnes = {
+	0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+	0x7fff,
+	0x7fff,
+	0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+	0x7fff,
+	0x7fff,
+	0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+	0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+	0x7fff,
+	0x7fff,
+	0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+	0x7fff,
+	0x7fff,
+	0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff
+};
+
+static const a3d_Hrtf_t A3dHrirSatTest = {
+	0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+	0x7fff,
+	0x7fff,
+	0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001,
+	0x8001,
+	0x8001,
+	0x7fff, 0x0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const a3d_Hrtf_t A3dHrirDImpulse = {
+	0, 0x7fff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0,
+	0, 0, 0
+};
+
+static const a3d_ItdDline_t A3dItdDlineZeros = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static short const GainTCDefault = 0x300;
+static short const ItdTCDefault = 0x0C8;
+static short const HrtfTCDefault = 0x147;
+static short const CoefTCDefault = 0x300;
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
new file mode 100644
index 0000000..f0eda4b
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -0,0 +1,2837 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library 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.
+ */
+
+/*
+    Vortex core low level functions.
+	
+ Author: Manuel Jander (mjander@users.sourceforge.cl)
+ These functions are mainly the result of translations made
+ from the original disassembly of the au88x0 binary drivers,
+ written by Aureal before they went down.
+ Many thanks to the Jeff Muizelaar, Kester Maddock, and whoever
+ contributed to the OpenVortex project.
+ The author of this file, put the few available pieces together
+ and translated the rest of the riddle (Mix, Src and connection stuff).
+ Some things are still to be discovered, and their meanings are unclear.
+
+ Some of these functions aren't intended to be really used, rather
+ to help to understand how does the AU88X0 chips work. Keep them in, because
+ they could be used somewhere in the future.
+
+ This code hasn't been tested or proof read thoroughly. If you wanna help,
+ take a look at the AU88X0 assembly and check if this matches.
+ Functions tested ok so far are (they show the desired effect
+ at least):
+   vortex_routes(); (1 bug fixed).
+   vortex_adb_addroute();
+   vortex_adb_addroutes();
+   vortex_connect_codecplay();
+   vortex_src_flushbuffers();
+   vortex_adbdma_setmode();  note: still some unknown arguments!
+   vortex_adbdma_startfifo();
+   vortex_adbdma_stopfifo();
+   vortex_fifo_setadbctrl(); note: still some unknown arguments!
+   vortex_mix_setinputvolumebyte();
+   vortex_mix_enableinput();
+   vortex_mixer_addWTD(); (fixed)
+   vortex_connection_adbdma_src_src();
+   vortex_connection_adbdma_src();
+   vortex_src_change_convratio();
+   vortex_src_addWTD(); (fixed)
+
+ History:
+
+ 01-03-2003 First revision.
+ 01-21-2003 Some bug fixes.
+ 17-02-2003 many bugfixes after a big versioning mess.
+ 18-02-2003 JAAAAAHHHUUUUUU!!!! The mixer works !! I'm just so happy !
+			 (2 hours later...) I cant believe it! Im really lucky today.
+			 Now the SRC is working too! Yeah! XMMS works !
+ 20-02-2003 First steps into the ALSA world.
+ 28-02-2003 As my birthday present, i discovered how the DMA buffer pages really
+            work :-). It was all wrong.
+ 12-03-2003 ALSA driver starts working (2 channels).
+ 16-03-2003 More srcblock_setupchannel discoveries.
+ 12-04-2003 AU8830 playback support. Recording in the works.
+ 17-04-2003 vortex_route() and vortex_routes() bug fixes. AU8830 recording
+ 			works now, but chipn' dale effect is still there.
+ 16-05-2003 SrcSetupChannel cleanup. Moved the Src setup stuff entirely
+            into au88x0_pcm.c .
+ 06-06-2003 Buffer shifter bugfix. Mixer volume fix.
+ 07-12-2003 A3D routing finally fixed. Believed to be OK.
+ 25-03-2004 Many thanks to Claudia, for such valuable bug reports.
+ 
+*/
+
+#include "au88x0.h"
+#include "au88x0_a3d.h"
+#include <linux/delay.h>
+
+/*  MIXER (CAsp4Mix.s and CAsp4Mixer.s) */
+
+// FIXME: get rid of this.
+static int mchannels[NR_MIXIN];
+static int rampchs[NR_MIXIN];
+
+static void vortex_mixer_en_sr(vortex_t * vortex, int channel)
+{
+	hwwrite(vortex->mmio, VORTEX_MIXER_SR,
+		hwread(vortex->mmio, VORTEX_MIXER_SR) | (0x1 << channel));
+}
+static void vortex_mixer_dis_sr(vortex_t * vortex, int channel)
+{
+	hwwrite(vortex->mmio, VORTEX_MIXER_SR,
+		hwread(vortex->mmio, VORTEX_MIXER_SR) & ~(0x1 << channel));
+}
+
+#if 0
+static void
+vortex_mix_muteinputgain(vortex_t * vortex, unsigned char mix,
+			 unsigned char channel)
+{
+	hwwrite(vortex->mmio, VORTEX_MIX_INVOL_A + ((mix << 5) + channel),
+		0x80);
+	hwwrite(vortex->mmio, VORTEX_MIX_INVOL_B + ((mix << 5) + channel),
+		0x80);
+}
+
+static int vortex_mix_getvolume(vortex_t * vortex, unsigned char mix)
+{
+	int a;
+	a = hwread(vortex->mmio, VORTEX_MIX_VOL_A + (mix << 2)) & 0xff;
+	//FP2LinearFrac(a);
+	return (a);
+}
+
+static int
+vortex_mix_getinputvolume(vortex_t * vortex, unsigned char mix,
+			  int channel, int *vol)
+{
+	int a;
+	if (!(mchannels[mix] & (1 << channel)))
+		return 0;
+	a = hwread(vortex->mmio,
+		   VORTEX_MIX_INVOL_A + (((mix << 5) + channel) << 2));
+	/*
+	   if (rampchs[mix] == 0)
+	   a = FP2LinearFrac(a);
+	   else
+	   a = FP2LinearFracWT(a);
+	 */
+	*vol = a;
+	return (0);
+}
+
+static unsigned int vortex_mix_boost6db(unsigned char vol)
+{
+	return (vol + 8);	/* WOW! what a complex function! */
+}
+
+static void vortex_mix_rampvolume(vortex_t * vortex, int mix)
+{
+	int ch;
+	char a;
+	// This function is intended for ramping down only (see vortex_disableinput()).
+	for (ch = 0; ch < 0x20; ch++) {
+		if (((1 << ch) & rampchs[mix]) == 0)
+			continue;
+		a = hwread(vortex->mmio,
+			   VORTEX_MIX_INVOL_B + (((mix << 5) + ch) << 2));
+		if (a > -126) {
+			a -= 2;
+			hwwrite(vortex->mmio,
+				VORTEX_MIX_INVOL_A +
+				(((mix << 5) + ch) << 2), a);
+			hwwrite(vortex->mmio,
+				VORTEX_MIX_INVOL_B +
+				(((mix << 5) + ch) << 2), a);
+		} else
+			vortex_mix_killinput(vortex, mix, ch);
+	}
+}
+
+static int
+vortex_mix_getenablebit(vortex_t * vortex, unsigned char mix, int mixin)
+{
+	int addr, temp;
+	if (mixin >= 0)
+		addr = mixin;
+	else
+		addr = mixin + 3;
+	addr = ((mix << 3) + (addr >> 2)) << 2;
+	temp = hwread(vortex->mmio, VORTEX_MIX_ENIN + addr);
+	return ((temp >> (mixin & 3)) & 1);
+}
+#endif
+static void
+vortex_mix_setvolumebyte(vortex_t * vortex, unsigned char mix,
+			 unsigned char vol)
+{
+	int temp;
+	hwwrite(vortex->mmio, VORTEX_MIX_VOL_A + (mix << 2), vol);
+	if (1) {		/*if (this_10) */
+		temp = hwread(vortex->mmio, VORTEX_MIX_VOL_B + (mix << 2));
+		if ((temp != 0x80) || (vol == 0x80))
+			return;
+	}
+	hwwrite(vortex->mmio, VORTEX_MIX_VOL_B + (mix << 2), vol);
+}
+
+static void
+vortex_mix_setinputvolumebyte(vortex_t * vortex, unsigned char mix,
+			      int mixin, unsigned char vol)
+{
+	int temp;
+
+	hwwrite(vortex->mmio,
+		VORTEX_MIX_INVOL_A + (((mix << 5) + mixin) << 2), vol);
+	if (1) {		/* this_10, initialized to 1. */
+		temp =
+		    hwread(vortex->mmio,
+			   VORTEX_MIX_INVOL_B + (((mix << 5) + mixin) << 2));
+		if ((temp != 0x80) || (vol == 0x80))
+			return;
+	}
+	hwwrite(vortex->mmio,
+		VORTEX_MIX_INVOL_B + (((mix << 5) + mixin) << 2), vol);
+}
+
+static void
+vortex_mix_setenablebit(vortex_t * vortex, unsigned char mix, int mixin, int en)
+{
+	int temp, addr;
+
+	if (mixin < 0)
+		addr = (mixin + 3);
+	else
+		addr = mixin;
+	addr = ((mix << 3) + (addr >> 2)) << 2;
+	temp = hwread(vortex->mmio, VORTEX_MIX_ENIN + addr);
+	if (en)
+		temp |= (1 << (mixin & 3));
+	else
+		temp &= ~(1 << (mixin & 3));
+	/* Mute input. Astatic void crackling? */
+	hwwrite(vortex->mmio,
+		VORTEX_MIX_INVOL_B + (((mix << 5) + mixin) << 2), 0x80);
+	/* Looks like clear buffer. */
+	hwwrite(vortex->mmio, VORTEX_MIX_SMP + (mixin << 2), 0x0);
+	hwwrite(vortex->mmio, VORTEX_MIX_SMP + 4 + (mixin << 2), 0x0);
+	/* Write enable bit. */
+	hwwrite(vortex->mmio, VORTEX_MIX_ENIN + addr, temp);
+}
+
+static void
+vortex_mix_killinput(vortex_t * vortex, unsigned char mix, int mixin)
+{
+	rampchs[mix] &= ~(1 << mixin);
+	vortex_mix_setinputvolumebyte(vortex, mix, mixin, 0x80);
+	mchannels[mix] &= ~(1 << mixin);
+	vortex_mix_setenablebit(vortex, mix, mixin, 0);
+}
+
+static void
+vortex_mix_enableinput(vortex_t * vortex, unsigned char mix, int mixin)
+{
+	vortex_mix_killinput(vortex, mix, mixin);
+	if ((mchannels[mix] & (1 << mixin)) == 0) {
+		vortex_mix_setinputvolumebyte(vortex, mix, mixin, 0x80);	/*0x80 : mute */
+		mchannels[mix] |= (1 << mixin);
+	}
+	vortex_mix_setenablebit(vortex, mix, mixin, 1);
+}
+
+static void
+vortex_mix_disableinput(vortex_t * vortex, unsigned char mix, int channel,
+			int ramp)
+{
+	if (ramp) {
+		rampchs[mix] |= (1 << channel);
+		// Register callback.
+		//vortex_mix_startrampvolume(vortex);
+		vortex_mix_killinput(vortex, mix, channel);
+	} else
+		vortex_mix_killinput(vortex, mix, channel);
+}
+
+static int
+vortex_mixer_addWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
+{
+	int temp, lifeboat = 0, prev;
+
+	temp = hwread(vortex->mmio, VORTEX_MIXER_SR);
+	if ((temp & (1 << ch)) == 0) {
+		hwwrite(vortex->mmio, VORTEX_MIXER_CHNBASE + (ch << 2), mix);
+		vortex_mixer_en_sr(vortex, ch);
+		return 1;
+	}
+	prev = VORTEX_MIXER_CHNBASE + (ch << 2);
+	temp = hwread(vortex->mmio, prev);
+	while (temp & 0x10) {
+		prev = VORTEX_MIXER_RTBASE + ((temp & 0xf) << 2);
+		temp = hwread(vortex->mmio, prev);
+		//printk(KERN_INFO "vortex: mixAddWTD: while addr=%x, val=%x\n", prev, temp);
+		if ((++lifeboat) > 0xf) {
+			printk(KERN_ERR
+			       "vortex_mixer_addWTD: lifeboat overflow\n");
+			return 0;
+		}
+	}
+	hwwrite(vortex->mmio, VORTEX_MIXER_RTBASE + ((temp & 0xf) << 2), mix);
+	hwwrite(vortex->mmio, prev, (temp & 0xf) | 0x10);
+	return 1;
+}
+
+static int
+vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
+{
+	int esp14 = -1, esp18, eax, ebx, edx, ebp, esi = 0;
+	//int esp1f=edi(while)=src, esp10=ch;
+
+	eax = hwread(vortex->mmio, VORTEX_MIXER_SR);
+	if (((1 << ch) & eax) == 0) {
+		printk(KERN_ERR "mix ALARM %x\n", eax);
+		return 0;
+	}
+	ebp = VORTEX_MIXER_CHNBASE + (ch << 2);
+	esp18 = hwread(vortex->mmio, ebp);
+	if (esp18 & 0x10) {
+		ebx = (esp18 & 0xf);
+		if (mix == ebx) {
+			ebx = VORTEX_MIXER_RTBASE + (mix << 2);
+			edx = hwread(vortex->mmio, ebx);
+			//7b60
+			hwwrite(vortex->mmio, ebp, edx);
+			hwwrite(vortex->mmio, ebx, 0);
+		} else {
+			//7ad3
+			edx =
+			    hwread(vortex->mmio,
+				   VORTEX_MIXER_RTBASE + (ebx << 2));
+			//printk(KERN_INFO "vortex: mixdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src);
+			while ((edx & 0xf) != mix) {
+				if ((esi) > 0xf) {
+					printk(KERN_ERR
+					       "vortex: mixdelWTD: error lifeboat overflow\n");
+					return 0;
+				}
+				esp14 = ebx;
+				ebx = edx & 0xf;
+				ebp = ebx << 2;
+				edx =
+				    hwread(vortex->mmio,
+					   VORTEX_MIXER_RTBASE + ebp);
+				//printk(KERN_INFO "vortex: mixdelWTD: while addr=%x, val=%x\n", ebp, edx);
+				esi++;
+			}
+			//7b30
+			ebp = ebx << 2;
+			if (edx & 0x10) {	/* Delete entry in between others */
+				ebx = VORTEX_MIXER_RTBASE + ((edx & 0xf) << 2);
+				edx = hwread(vortex->mmio, ebx);
+				//7b60
+				hwwrite(vortex->mmio,
+					VORTEX_MIXER_RTBASE + ebp, edx);
+				hwwrite(vortex->mmio, ebx, 0);
+				//printk(KERN_INFO "vortex mixdelWTD between addr= 0x%x, val= 0x%x\n", ebp, edx);
+			} else {	/* Delete last entry */
+				//7b83
+				if (esp14 == -1)
+					hwwrite(vortex->mmio,
+						VORTEX_MIXER_CHNBASE +
+						(ch << 2), esp18 & 0xef);
+				else {
+					ebx = (0xffffffe0 & edx) | (0xf & ebx);
+					hwwrite(vortex->mmio,
+						VORTEX_MIXER_RTBASE +
+						(esp14 << 2), ebx);
+					//printk(KERN_INFO "vortex mixdelWTD last addr= 0x%x, val= 0x%x\n", esp14, ebx);
+				}
+				hwwrite(vortex->mmio,
+					VORTEX_MIXER_RTBASE + ebp, 0);
+				return 1;
+			}
+		}
+	} else {
+		//printk(KERN_INFO "removed last mix\n");
+		//7be0
+		vortex_mixer_dis_sr(vortex, ch);
+		hwwrite(vortex->mmio, ebp, 0);
+	}
+	return 1;
+}
+
+static void vortex_mixer_init(vortex_t * vortex)
+{
+	unsigned long addr;
+	int x;
+
+	// FIXME: get rid of this crap.
+	memset(mchannels, 0, NR_MIXOUT * sizeof(int));
+	memset(rampchs, 0, NR_MIXOUT * sizeof(int));
+
+	addr = VORTEX_MIX_SMP + 0x17c;
+	for (x = 0x5f; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0);
+		addr -= 4;
+	}
+	addr = VORTEX_MIX_ENIN + 0x1fc;
+	for (x = 0x7f; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0);
+		addr -= 4;
+	}
+	addr = VORTEX_MIX_SMP + 0x17c;
+	for (x = 0x5f; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0);
+		addr -= 4;
+	}
+	addr = VORTEX_MIX_INVOL_A + 0x7fc;
+	for (x = 0x1ff; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0x80);
+		addr -= 4;
+	}
+	addr = VORTEX_MIX_VOL_A + 0x3c;
+	for (x = 0xf; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0x80);
+		addr -= 4;
+	}
+	addr = VORTEX_MIX_INVOL_B + 0x7fc;
+	for (x = 0x1ff; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0x80);
+		addr -= 4;
+	}
+	addr = VORTEX_MIX_VOL_B + 0x3c;
+	for (x = 0xf; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0x80);
+		addr -= 4;
+	}
+	addr = VORTEX_MIXER_RTBASE + (MIXER_RTBASE_SIZE - 1) * 4;
+	for (x = (MIXER_RTBASE_SIZE - 1); x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0x0);
+		addr -= 4;
+	}
+	hwwrite(vortex->mmio, VORTEX_MIXER_SR, 0);
+
+	/* Set clipping ceiling (this may be all wrong). */
+	/*
+	for (x = 0; x > 0x80; x++) {
+		hwwrite(vortex->mmio, VORTEX_MIXER_CLIP + (x << 2), 0x3ffff);
+	}
+	*/
+	/*
+	   call CAsp4Mix__Initialize_CAsp4HwIO____CAsp4Mixer____
+	   Register ISR callback for volume smooth fade out.
+	   Maybe this avoids clicks when press "stop" ?
+	 */
+}
+
+/*  SRC (CAsp4Src.s and CAsp4SrcBlock) */
+
+static void vortex_src_en_sr(vortex_t * vortex, int channel)
+{
+	hwwrite(vortex->mmio, VORTEX_SRCBLOCK_SR,
+		hwread(vortex->mmio, VORTEX_SRCBLOCK_SR) | (0x1 << channel));
+}
+
+static void vortex_src_dis_sr(vortex_t * vortex, int channel)
+{
+	hwwrite(vortex->mmio, VORTEX_SRCBLOCK_SR,
+		hwread(vortex->mmio, VORTEX_SRCBLOCK_SR) & ~(0x1 << channel));
+}
+
+static void vortex_src_flushbuffers(vortex_t * vortex, unsigned char src)
+{
+	int i;
+
+	for (i = 0x1f; i >= 0; i--)
+		hwwrite(vortex->mmio,
+			VORTEX_SRC_DATA0 + (src << 7) + (i << 2), 0);
+	hwwrite(vortex->mmio, VORTEX_SRC_DATA + (src << 3), 0);
+	hwwrite(vortex->mmio, VORTEX_SRC_DATA + (src << 3) + 4, 0);
+}
+
+static void vortex_src_cleardrift(vortex_t * vortex, unsigned char src)
+{
+	hwwrite(vortex->mmio, VORTEX_SRC_DRIFT0 + (src << 2), 0);
+	hwwrite(vortex->mmio, VORTEX_SRC_DRIFT1 + (src << 2), 0);
+	hwwrite(vortex->mmio, VORTEX_SRC_DRIFT2 + (src << 2), 1);
+}
+
+static void
+vortex_src_set_throttlesource(vortex_t * vortex, unsigned char src, int en)
+{
+	int temp;
+
+	temp = hwread(vortex->mmio, VORTEX_SRC_SOURCE);
+	if (en)
+		temp |= 1 << src;
+	else
+		temp &= ~(1 << src);
+	hwwrite(vortex->mmio, VORTEX_SRC_SOURCE, temp);
+}
+
+static int
+vortex_src_persist_convratio(vortex_t * vortex, unsigned char src, int ratio)
+{
+	int temp, lifeboat = 0;
+
+	do {
+		hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), ratio);
+		temp = hwread(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2));
+		if ((++lifeboat) > 0x9) {
+			printk(KERN_ERR "Vortex: Src cvr fail\n");
+			break;
+		}
+	}
+	while (temp != ratio);
+	return temp;
+}
+
+#if 0
+static void vortex_src_slowlock(vortex_t * vortex, unsigned char src)
+{
+	int temp;
+
+	hwwrite(vortex->mmio, VORTEX_SRC_DRIFT2 + (src << 2), 1);
+	hwwrite(vortex->mmio, VORTEX_SRC_DRIFT0 + (src << 2), 0);
+	temp = hwread(vortex->mmio, VORTEX_SRC_U0 + (src << 2));
+	if (temp & 0x200)
+		hwwrite(vortex->mmio, VORTEX_SRC_U0 + (src << 2),
+			temp & ~0x200L);
+}
+
+static void
+vortex_src_change_convratio(vortex_t * vortex, unsigned char src, int ratio)
+{
+	int temp, a;
+
+	if ((ratio & 0x10000) && (ratio != 0x10000)) {
+		if (ratio & 0x3fff)
+			a = (0x11 - ((ratio >> 0xe) & 0x3)) - 1;
+		else
+			a = (0x11 - ((ratio >> 0xe) & 0x3)) - 2;
+	} else
+		a = 0xc;
+	temp = hwread(vortex->mmio, VORTEX_SRC_U0 + (src << 2));
+	if (((temp >> 4) & 0xf) != a)
+		hwwrite(vortex->mmio, VORTEX_SRC_U0 + (src << 2),
+			(temp & 0xf) | ((a & 0xf) << 4));
+
+	vortex_src_persist_convratio(vortex, src, ratio);
+}
+
+static int
+vortex_src_checkratio(vortex_t * vortex, unsigned char src,
+		      unsigned int desired_ratio)
+{
+	int hw_ratio, lifeboat = 0;
+
+	hw_ratio = hwread(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2));
+
+	while (hw_ratio != desired_ratio) {
+		hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), desired_ratio);
+
+		if ((lifeboat++) > 15) {
+			printk(KERN_ERR "Vortex: could not set src-%d from %d to %d\n",
+			       src, hw_ratio, desired_ratio);
+			break;
+		}
+	}
+
+	return hw_ratio;
+}
+
+#endif
+/*
+ Objective: Set samplerate for given SRC module.
+ Arguments:
+	card:	pointer to vortex_t strcut.
+	src:	Integer index of the SRC module.
+	cr:		Current sample rate conversion factor.
+	b:		unknown 16 bit value.
+	sweep:	Enable Samplerate fade from cr toward tr flag.
+	dirplay: 1: playback, 0: recording.
+	sl:		Slow Lock flag.
+	tr:		Target samplerate conversion.
+	thsource: Throttle source flag (no idea what that means).
+*/
+static void vortex_src_setupchannel(vortex_t * card, unsigned char src,
+			unsigned int cr, unsigned int b, int sweep, int d,
+			int dirplay, int sl, unsigned int tr, int thsource)
+{
+	// noplayback: d=2,4,7,0xa,0xb when using first 2 src's.
+	// c: enables pitch sweep.
+	// looks like g is c related. Maybe g is a sweep parameter ?
+	// g = cvr
+	// dirplay: 0 = recording, 1 = playback
+	// d = src hw index.
+
+	int esi, ebp = 0, esp10;
+
+	vortex_src_flushbuffers(card, src);
+
+	if (sweep) {
+		if ((tr & 0x10000) && (tr != 0x10000)) {
+			tr = 0;
+			esi = 0x7;
+		} else {
+			if ((((short)tr) < 0) && (tr != 0x8000)) {
+				tr = 0;
+				esi = 0x8;
+			} else {
+				tr = 1;
+				esi = 0xc;
+			}
+		}
+	} else {
+		if ((cr & 0x10000) && (cr != 0x10000)) {
+			tr = 0;	/*ebx = 0 */
+			esi = 0x11 - ((cr >> 0xe) & 7);
+			if (cr & 0x3fff)
+				esi -= 1;
+			else
+				esi -= 2;
+		} else {
+			tr = 1;
+			esi = 0xc;
+		}
+	}
+	vortex_src_cleardrift(card, src);
+	vortex_src_set_throttlesource(card, src, thsource);
+
+	if ((dirplay == 0) && (sweep == 0)) {
+		if (tr)
+			esp10 = 0xf;
+		else
+			esp10 = 0xc;
+		ebp = 0;
+	} else {
+		if (tr)
+			ebp = 0xf;
+		else
+			ebp = 0xc;
+		esp10 = 0;
+	}
+	hwwrite(card->mmio, VORTEX_SRC_U0 + (src << 2),
+		(sl << 0x9) | (sweep << 0x8) | ((esi & 0xf) << 4) | d);
+	/* 0xc0   esi=0xc c=f=0 d=0 */
+	vortex_src_persist_convratio(card, src, cr);
+	hwwrite(card->mmio, VORTEX_SRC_U1 + (src << 2), b & 0xffff);
+	/* 0   b=0 */
+	hwwrite(card->mmio, VORTEX_SRC_U2 + (src << 2),
+		(tr << 0x11) | (dirplay << 0x10) | (ebp << 0x8) | esp10);
+	/* 0x30f00 e=g=1 esp10=0 ebp=f */
+	//printk(KERN_INFO "vortex: SRC %d, d=0x%x, esi=0x%x, esp10=0x%x, ebp=0x%x\n", src, d, esi, esp10, ebp);
+}
+
+static void vortex_srcblock_init(vortex_t * vortex)
+{
+	unsigned long addr;
+	int x;
+	hwwrite(vortex->mmio, VORTEX_SRC_SOURCESIZE, 0x1ff);
+	/*
+	   for (x=0; x<0x10; x++) {
+	   vortex_src_init(&vortex_src[x], x);
+	   }
+	 */
+	//addr = 0xcc3c;
+	//addr = 0x26c3c;
+	addr = VORTEX_SRC_RTBASE + 0x3c;
+	for (x = 0xf; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0);
+		addr -= 4;
+	}
+	//addr = 0xcc94;
+	//addr = 0x26c94;
+	addr = VORTEX_SRC_CHNBASE + 0x54;
+	for (x = 0x15; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0);
+		addr -= 4;
+	}
+}
+
+static int
+vortex_src_addWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
+{
+	int temp, lifeboat = 0, prev;
+	// esp13 = src
+
+	temp = hwread(vortex->mmio, VORTEX_SRCBLOCK_SR);
+	if ((temp & (1 << ch)) == 0) {
+		hwwrite(vortex->mmio, VORTEX_SRC_CHNBASE + (ch << 2), src);
+		vortex_src_en_sr(vortex, ch);
+		return 1;
+	}
+	prev = VORTEX_SRC_CHNBASE + (ch << 2);	/*ebp */
+	temp = hwread(vortex->mmio, prev);
+	//while (temp & NR_SRC) {
+	while (temp & 0x10) {
+		prev = VORTEX_SRC_RTBASE + ((temp & 0xf) << 2);	/*esp12 */
+		//prev = VORTEX_SRC_RTBASE + ((temp & (NR_SRC-1)) << 2); /*esp12*/
+		temp = hwread(vortex->mmio, prev);
+		//printk(KERN_INFO "vortex: srcAddWTD: while addr=%x, val=%x\n", prev, temp);
+		if ((++lifeboat) > 0xf) {
+			printk(KERN_ERR
+			       "vortex_src_addWTD: lifeboat overflow\n");
+			return 0;
+		}
+	}
+	hwwrite(vortex->mmio, VORTEX_SRC_RTBASE + ((temp & 0xf) << 2), src);
+	//hwwrite(vortex->mmio, prev, (temp & (NR_SRC-1)) | NR_SRC);
+	hwwrite(vortex->mmio, prev, (temp & 0xf) | 0x10);
+	return 1;
+}
+
+static int
+vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
+{
+	int esp14 = -1, esp18, eax, ebx, edx, ebp, esi = 0;
+	//int esp1f=edi(while)=src, esp10=ch;
+
+	eax = hwread(vortex->mmio, VORTEX_SRCBLOCK_SR);
+	if (((1 << ch) & eax) == 0) {
+		printk(KERN_ERR "src alarm\n");
+		return 0;
+	}
+	ebp = VORTEX_SRC_CHNBASE + (ch << 2);
+	esp18 = hwread(vortex->mmio, ebp);
+	if (esp18 & 0x10) {
+		ebx = (esp18 & 0xf);
+		if (src == ebx) {
+			ebx = VORTEX_SRC_RTBASE + (src << 2);
+			edx = hwread(vortex->mmio, ebx);
+			//7b60
+			hwwrite(vortex->mmio, ebp, edx);
+			hwwrite(vortex->mmio, ebx, 0);
+		} else {
+			//7ad3
+			edx =
+			    hwread(vortex->mmio,
+				   VORTEX_SRC_RTBASE + (ebx << 2));
+			//printk(KERN_INFO "vortex: srcdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src);
+			while ((edx & 0xf) != src) {
+				if ((esi) > 0xf) {
+					printk
+					    ("vortex: srcdelWTD: error, lifeboat overflow\n");
+					return 0;
+				}
+				esp14 = ebx;
+				ebx = edx & 0xf;
+				ebp = ebx << 2;
+				edx =
+				    hwread(vortex->mmio,
+					   VORTEX_SRC_RTBASE + ebp);
+				//printk(KERN_INFO "vortex: srcdelWTD: while addr=%x, val=%x\n", ebp, edx);
+				esi++;
+			}
+			//7b30
+			ebp = ebx << 2;
+			if (edx & 0x10) {	/* Delete entry in between others */
+				ebx = VORTEX_SRC_RTBASE + ((edx & 0xf) << 2);
+				edx = hwread(vortex->mmio, ebx);
+				//7b60
+				hwwrite(vortex->mmio,
+					VORTEX_SRC_RTBASE + ebp, edx);
+				hwwrite(vortex->mmio, ebx, 0);
+				//printk(KERN_INFO "vortex srcdelWTD between addr= 0x%x, val= 0x%x\n", ebp, edx);
+			} else {	/* Delete last entry */
+				//7b83
+				if (esp14 == -1)
+					hwwrite(vortex->mmio,
+						VORTEX_SRC_CHNBASE +
+						(ch << 2), esp18 & 0xef);
+				else {
+					ebx = (0xffffffe0 & edx) | (0xf & ebx);
+					hwwrite(vortex->mmio,
+						VORTEX_SRC_RTBASE +
+						(esp14 << 2), ebx);
+					//printk(KERN_INFO"vortex srcdelWTD last addr= 0x%x, val= 0x%x\n", esp14, ebx);
+				}
+				hwwrite(vortex->mmio,
+					VORTEX_SRC_RTBASE + ebp, 0);
+				return 1;
+			}
+		}
+	} else {
+		//7be0
+		vortex_src_dis_sr(vortex, ch);
+		hwwrite(vortex->mmio, ebp, 0);
+	}
+	return 1;
+}
+
+ /*FIFO*/ 
+
+static void
+vortex_fifo_clearadbdata(vortex_t * vortex, int fifo, int x)
+{
+	for (x--; x >= 0; x--)
+		hwwrite(vortex->mmio,
+			VORTEX_FIFO_ADBDATA +
+			(((fifo << FIFO_SIZE_BITS) + x) << 2), 0);
+}
+
+#if 0
+static void vortex_fifo_adbinitialize(vortex_t * vortex, int fifo, int j)
+{
+	vortex_fifo_clearadbdata(vortex, fifo, FIFO_SIZE);
+#ifdef CHIP_AU8820
+	hwwrite(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2),
+		(FIFO_U1 | ((j & FIFO_MASK) << 0xb)));
+#else
+	hwwrite(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2),
+		(FIFO_U1 | ((j & FIFO_MASK) << 0xc)));
+#endif
+}
+#endif
+static void vortex_fifo_setadbvalid(vortex_t * vortex, int fifo, int en)
+{
+	hwwrite(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2),
+		(hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2)) &
+		 0xffffffef) | ((1 & en) << 4) | FIFO_U1);
+}
+
+static void
+vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int b, int priority,
+		       int empty, int valid, int f)
+{
+	int temp, lifeboat = 0;
+	//int this_8[NR_ADB] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; /* position */
+	int this_4 = 0x2;
+	/* f seems priority related.
+	 * CAsp4AdbDma::SetPriority is the only place that calls SetAdbCtrl with f set to 1
+	 * every where else it is set to 0. It seems, however, that CAsp4AdbDma::SetPriority
+	 * is never called, thus the f related bits remain a mystery for now.
+	 */
+	do {
+		temp = hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2));
+		if (lifeboat++ > 0xbb8) {
+			printk(KERN_ERR
+			       "Vortex: vortex_fifo_setadbctrl fail\n");
+			break;
+		}
+	}
+	while (temp & FIFO_RDONLY);
+
+	// AU8830 semes to take some special care about fifo content (data).
+	// But i'm just to lazy to translate that :)
+	if (valid) {
+		if ((temp & FIFO_VALID) == 0) {
+			//this_8[fifo] = 0;
+			vortex_fifo_clearadbdata(vortex, fifo, FIFO_SIZE);	// this_4
+#ifdef CHIP_AU8820
+			temp = (this_4 & 0x1f) << 0xb;
+#else
+			temp = (this_4 & 0x3f) << 0xc;
+#endif
+			temp = (temp & 0xfffffffd) | ((b & 1) << 1);
+			temp = (temp & 0xfffffff3) | ((priority & 3) << 2);
+			temp = (temp & 0xffffffef) | ((valid & 1) << 4);
+			temp |= FIFO_U1;
+			temp = (temp & 0xffffffdf) | ((empty & 1) << 5);
+#ifdef CHIP_AU8820
+			temp = (temp & 0xfffbffff) | ((f & 1) << 0x12);
+#endif
+#ifdef CHIP_AU8830
+			temp = (temp & 0xf7ffffff) | ((f & 1) << 0x1b);
+			temp = (temp & 0xefffffff) | ((f & 1) << 0x1c);
+#endif
+#ifdef CHIP_AU8810
+			temp = (temp & 0xfeffffff) | ((f & 1) << 0x18);
+			temp = (temp & 0xfdffffff) | ((f & 1) << 0x19);
+#endif
+		}
+	} else {
+		if (temp & FIFO_VALID) {
+#ifdef CHIP_AU8820
+			temp = ((f & 1) << 0x12) | (temp & 0xfffbffef);
+#endif
+#ifdef CHIP_AU8830
+			temp =
+			    ((f & 1) << 0x1b) | (temp & 0xe7ffffef) | FIFO_BITS;
+#endif
+#ifdef CHIP_AU8810
+			temp =
+			    ((f & 1) << 0x18) | (temp & 0xfcffffef) | FIFO_BITS;
+#endif
+		} else
+			/*if (this_8[fifo]) */
+			vortex_fifo_clearadbdata(vortex, fifo, FIFO_SIZE);
+	}
+	hwwrite(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2), temp);
+	hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2));
+}
+
+#ifndef CHIP_AU8810
+static void vortex_fifo_clearwtdata(vortex_t * vortex, int fifo, int x)
+{
+	if (x < 1)
+		return;
+	for (x--; x >= 0; x--)
+		hwwrite(vortex->mmio,
+			VORTEX_FIFO_WTDATA +
+			(((fifo << FIFO_SIZE_BITS) + x) << 2), 0);
+}
+
+static void vortex_fifo_wtinitialize(vortex_t * vortex, int fifo, int j)
+{
+	vortex_fifo_clearwtdata(vortex, fifo, FIFO_SIZE);
+#ifdef CHIP_AU8820
+	hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2),
+		(FIFO_U1 | ((j & FIFO_MASK) << 0xb)));
+#else
+	hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2),
+		(FIFO_U1 | ((j & FIFO_MASK) << 0xc)));
+#endif
+}
+
+static void vortex_fifo_setwtvalid(vortex_t * vortex, int fifo, int en)
+{
+	hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2),
+		(hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2)) &
+		 0xffffffef) | ((en & 1) << 4) | FIFO_U1);
+}
+
+static void
+vortex_fifo_setwtctrl(vortex_t * vortex, int fifo, int ctrl, int priority,
+		      int empty, int valid, int f)
+{
+	int temp = 0, lifeboat = 0;
+	int this_4 = 2;
+
+	do {
+		temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
+		if (lifeboat++ > 0xbb8) {
+			printk(KERN_ERR "Vortex: vortex_fifo_setwtctrl fail\n");
+			break;
+		}
+	}
+	while (temp & FIFO_RDONLY);
+
+	if (valid) {
+		if ((temp & FIFO_VALID) == 0) {
+			vortex_fifo_clearwtdata(vortex, fifo, FIFO_SIZE);	// this_4
+#ifdef CHIP_AU8820
+			temp = (this_4 & 0x1f) << 0xb;
+#else
+			temp = (this_4 & 0x3f) << 0xc;
+#endif
+			temp = (temp & 0xfffffffd) | ((ctrl & 1) << 1);
+			temp = (temp & 0xfffffff3) | ((priority & 3) << 2);
+			temp = (temp & 0xffffffef) | ((valid & 1) << 4);
+			temp |= FIFO_U1;
+			temp = (temp & 0xffffffdf) | ((empty & 1) << 5);
+#ifdef CHIP_AU8820
+			temp = (temp & 0xfffbffff) | ((f & 1) << 0x12);
+#endif
+#ifdef CHIP_AU8830
+			temp = (temp & 0xf7ffffff) | ((f & 1) << 0x1b);
+			temp = (temp & 0xefffffff) | ((f & 1) << 0x1c);
+#endif
+#ifdef CHIP_AU8810
+			temp = (temp & 0xfeffffff) | ((f & 1) << 0x18);
+			temp = (temp & 0xfdffffff) | ((f & 1) << 0x19);
+#endif
+		}
+	} else {
+		if (temp & FIFO_VALID) {
+#ifdef CHIP_AU8820
+			temp = ((f & 1) << 0x12) | (temp & 0xfffbffef);
+#endif
+#ifdef CHIP_AU8830
+			temp =
+			    ((f & 1) << 0x1b) | (temp & 0xe7ffffef) | FIFO_BITS;
+#endif
+#ifdef CHIP_AU8810
+			temp =
+			    ((f & 1) << 0x18) | (temp & 0xfcffffef) | FIFO_BITS;
+#endif
+		} else
+			/*if (this_8[fifo]) */
+			vortex_fifo_clearwtdata(vortex, fifo, FIFO_SIZE);
+	}
+	hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2), temp);
+	hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
+
+/*	
+    do {
+		temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
+		if (lifeboat++ > 0xbb8) {
+			printk(KERN_ERR "Vortex: vortex_fifo_setwtctrl fail (hanging)\n");
+			break;
+		}
+    } while ((temp & FIFO_RDONLY)&&(temp & FIFO_VALID)&&(temp != 0xFFFFFFFF));
+	
+	
+	if (valid) {
+		if (temp & FIFO_VALID) {
+			temp = 0x40000;
+			//temp |= 0x08000000;
+			//temp |= 0x10000000;
+			//temp |= 0x04000000;
+			//temp |= 0x00400000;
+			temp |= 0x1c400000;
+			temp &= 0xFFFFFFF3;
+			temp &= 0xFFFFFFEF;
+			temp |= (valid & 1) << 4;
+			hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2), temp);
+			return;
+		} else {
+			vortex_fifo_clearwtdata(vortex, fifo, FIFO_SIZE);
+			return;
+		}
+	} else {
+		temp &= 0xffffffef;
+		temp |= 0x08000000;
+		temp |= 0x10000000;
+		temp |= 0x04000000;
+		temp |= 0x00400000;
+		hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2), temp);
+		temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
+		//((temp >> 6) & 0x3f) 
+		
+		priority = 0;
+		if (((temp & 0x0fc0) ^ ((temp >> 6) & 0x0fc0)) & 0FFFFFFC0)
+			vortex_fifo_clearwtdata(vortex, fifo, FIFO_SIZE);
+		valid = 0xfb;
+		temp = (temp & 0xfffffffd) | ((ctrl & 1) << 1);
+		temp = (temp & 0xfffdffff) | ((f & 1) << 0x11);
+		temp = (temp & 0xfffffff3) | ((priority & 3) << 2);
+		temp = (temp & 0xffffffef) | ((valid & 1) << 4);
+		temp = (temp & 0xffffffdf) | ((empty & 1) << 5);
+		hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2), temp);
+	}
+	
+	*/
+
+	/*
+	   temp = (temp & 0xfffffffd) | ((ctrl & 1) << 1);
+	   temp = (temp & 0xfffdffff) | ((f & 1) << 0x11);
+	   temp = (temp & 0xfffffff3) | ((priority & 3) << 2);
+	   temp = (temp & 0xffffffef) | ((valid & 1) << 4);
+	   temp = (temp & 0xffffffdf) | ((empty & 1) << 5);
+	   #ifdef FIFO_BITS
+	   temp = temp | FIFO_BITS | 40000;
+	   #endif
+	   // 0x1c440010, 0x1c400000
+	   hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2), temp);
+	 */
+}
+
+#endif
+static void vortex_fifo_init(vortex_t * vortex)
+{
+	int x;
+	unsigned long addr;
+
+	/* ADB DMA channels fifos. */
+	addr = VORTEX_FIFO_ADBCTRL + ((NR_ADB - 1) * 4);
+	for (x = NR_ADB - 1; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, (FIFO_U0 | FIFO_U1));
+		if (hwread(vortex->mmio, addr) != (FIFO_U0 | FIFO_U1))
+			printk(KERN_ERR "bad adb fifo reset!");
+		vortex_fifo_clearadbdata(vortex, x, FIFO_SIZE);
+		addr -= 4;
+	}
+
+#ifndef CHIP_AU8810
+	/* WT DMA channels fifos. */
+	addr = VORTEX_FIFO_WTCTRL + ((NR_WT - 1) * 4);
+	for (x = NR_WT - 1; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, FIFO_U0);
+		if (hwread(vortex->mmio, addr) != FIFO_U0)
+			printk(KERN_ERR
+			       "bad wt fifo reset (0x%08lx, 0x%08x)!\n",
+			       addr, hwread(vortex->mmio, addr));
+		vortex_fifo_clearwtdata(vortex, x, FIFO_SIZE);
+		addr -= 4;
+	}
+#endif
+	/* trigger... */
+#ifdef CHIP_AU8820
+	hwwrite(vortex->mmio, 0xf8c0, 0xd03);	//0x0843 0xd6b
+#else
+#ifdef CHIP_AU8830
+	hwwrite(vortex->mmio, 0x17000, 0x61);	/* wt a */
+	hwwrite(vortex->mmio, 0x17004, 0x61);	/* wt b */
+#endif
+	hwwrite(vortex->mmio, 0x17008, 0x61);	/* adb */
+#endif
+}
+
+/* ADBDMA */
+
+static void vortex_adbdma_init(vortex_t * vortex)
+{
+}
+
+static void vortex_adbdma_setfirstbuffer(vortex_t * vortex, int adbdma)
+{
+	stream_t *dma = &vortex->dma_adb[adbdma];
+
+	hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2),
+		dma->dma_ctrl);
+}
+
+static void vortex_adbdma_setstartbuffer(vortex_t * vortex, int adbdma, int sb)
+{
+	stream_t *dma = &vortex->dma_adb[adbdma];
+	//hwwrite(vortex->mmio, VORTEX_ADBDMA_START + (adbdma << 2), sb << (((NR_ADB-1)-((adbdma&0xf)*2))));
+	hwwrite(vortex->mmio, VORTEX_ADBDMA_START + (adbdma << 2),
+		sb << ((0xf - (adbdma & 0xf)) * 2));
+	dma->period_real = dma->period_virt = sb;
+}
+
+static void
+vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
+			 snd_pcm_sgbuf_t * sgbuf, int psize, int count)
+{
+	stream_t *dma = &vortex->dma_adb[adbdma];
+
+	if (sgbuf == NULL) {
+		printk(KERN_INFO "vortex: FATAL: sgbuf is NULL!\n");
+		return;
+	}
+	//printk(KERN_INFO "vortex: page count = %d, tblcount = %d\n", count, sgbuf->tblsize);
+
+	dma->period_bytes = psize;
+	dma->nr_periods = count;
+	dma->sgbuf = sgbuf;
+
+	dma->cfg0 = 0;
+	dma->cfg1 = 0;
+	switch (count) {
+		/* Four or more pages */
+	default:
+	case 4:
+		dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | (psize - 1);
+		hwwrite(vortex->mmio,
+			VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0xc,
+			snd_sgbuf_get_addr(sgbuf, psize * 3));
+		/* 3 pages */
+	case 3:
+		dma->cfg0 |= 0x12000000;
+		dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc);
+		hwwrite(vortex->mmio,
+			VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x8,
+			snd_sgbuf_get_addr(sgbuf, psize * 2));
+		/* 2 pages */
+	case 2:
+		dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize - 1);
+		hwwrite(vortex->mmio,
+			VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x4,
+			snd_sgbuf_get_addr(sgbuf, psize));
+		/* 1 page */
+	case 1:
+		dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc);
+		hwwrite(vortex->mmio,
+			VORTEX_ADBDMA_BUFBASE + (adbdma << 4),
+			snd_sgbuf_get_addr(sgbuf, 0));
+		break;
+	}
+	//printk("vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n", dma->cfg0, dma->cfg1);
+	hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFCFG0 + (adbdma << 3), dma->cfg0);
+	hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFCFG1 + (adbdma << 3), dma->cfg1);
+
+	vortex_adbdma_setfirstbuffer(vortex, adbdma);
+	vortex_adbdma_setstartbuffer(vortex, adbdma, 0);
+}
+
+static void
+vortex_adbdma_setmode(vortex_t * vortex, int adbdma, int ie, int dir,
+		      int fmt, int d, unsigned long offset)
+{
+	stream_t *dma = &vortex->dma_adb[adbdma];
+
+	dma->dma_unknown = d;
+	dma->dma_ctrl =
+	    ((offset & OFFSET_MASK) | (dma->dma_ctrl & ~OFFSET_MASK));
+	/* Enable PCMOUT interrupts. */
+	dma->dma_ctrl =
+	    (dma->dma_ctrl & ~IE_MASK) | ((ie << IE_SHIFT) & IE_MASK);
+
+	dma->dma_ctrl =
+	    (dma->dma_ctrl & ~DIR_MASK) | ((dir << DIR_SHIFT) & DIR_MASK);
+	dma->dma_ctrl =
+	    (dma->dma_ctrl & ~FMT_MASK) | ((fmt << FMT_SHIFT) & FMT_MASK);
+
+	hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2),
+		dma->dma_ctrl);
+	hwread(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2));
+}
+
+static int vortex_adbdma_bufshift(vortex_t * vortex, int adbdma)
+{
+	stream_t *dma = &vortex->dma_adb[adbdma];
+	int page, p, pp, delta, i;
+
+	page =
+	    (hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2)) &
+	     ADB_SUBBUF_MASK) >> ADB_SUBBUF_SHIFT;
+	if (dma->nr_periods >= 4)
+		delta = (page - dma->period_real) & 3;
+	else {
+		delta = (page - dma->period_real);
+		if (delta < 0)
+			delta += dma->nr_periods;
+	}
+	if (delta == 0)
+		return 0;
+
+	/* refresh hw page table */
+	if (dma->nr_periods > 4) {
+		for (i = 0; i < delta; i++) {
+			/* p: audio buffer page index */
+			p = dma->period_virt + i + 4;
+			if (p >= dma->nr_periods)
+				p -= dma->nr_periods;
+			/* pp: hardware DMA page index. */
+			pp = dma->period_real + i;
+			if (pp >= 4)
+				pp -= 4;
+			//hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE+(((adbdma << 2)+pp) << 2), dma->table[p].addr);
+			hwwrite(vortex->mmio,
+				VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2),
+				snd_sgbuf_get_addr(dma->sgbuf,
+				dma->period_bytes * p));
+			/* Force write thru cache. */
+			hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE +
+			       (((adbdma << 2) + pp) << 2));
+		}
+	}
+	dma->period_virt += delta;
+	dma->period_real = page;
+	if (dma->period_virt >= dma->nr_periods)
+		dma->period_virt -= dma->nr_periods;
+	if (delta != 1)
+		printk(KERN_INFO "vortex: %d virt=%d, real=%d, delta=%d\n",
+		       adbdma, dma->period_virt, dma->period_real, delta);
+
+	return delta;
+}
+
+
+static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma) {
+	stream_t *dma = &vortex->dma_adb[adbdma];
+	int p, pp, i;
+
+	/* refresh hw page table */
+	for (i=0 ; i < 4 && i < dma->nr_periods; i++) {
+		/* p: audio buffer page index */
+		p = dma->period_virt + i;
+		if (p >= dma->nr_periods)
+			p -= dma->nr_periods;
+		/* pp: hardware DMA page index. */
+		pp = dma->period_real + i;
+		if (dma->nr_periods < 4) {
+			if (pp >= dma->nr_periods)
+				pp -= dma->nr_periods;
+		}
+		else {
+			if (pp >= 4)
+				pp -= 4;
+		}
+		hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE+(((adbdma << 2)+pp) << 2), snd_sgbuf_get_addr(dma->sgbuf, dma->period_bytes * p));
+		/* Force write thru cache. */
+		hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (((adbdma << 2)+pp) << 2));
+	}
+}
+
+static int inline vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma)
+{
+	stream_t *dma = &vortex->dma_adb[adbdma];
+	int temp;
+
+	temp = hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2));
+	temp = (dma->period_virt * dma->period_bytes) + (temp & POS_MASK);
+	return (temp);
+}
+
+static void vortex_adbdma_startfifo(vortex_t * vortex, int adbdma)
+{
+	int this_8 = 0 /*empty */ , this_4 = 0 /*priority */ ;
+	stream_t *dma = &vortex->dma_adb[adbdma];
+
+	switch (dma->fifo_status) {
+	case FIFO_START:
+		vortex_fifo_setadbvalid(vortex, adbdma,
+					dma->fifo_enabled ? 1 : 0);
+		break;
+	case FIFO_STOP:
+		this_8 = 1;
+		hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2),
+			dma->dma_ctrl);
+		vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown,
+				       this_4, this_8,
+				       dma->fifo_enabled ? 1 : 0, 0);
+		break;
+	case FIFO_PAUSE:
+		vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown,
+				       this_4, this_8,
+				       dma->fifo_enabled ? 1 : 0, 0);
+		break;
+	}
+	dma->fifo_status = FIFO_START;
+}
+
+static void vortex_adbdma_resumefifo(vortex_t * vortex, int adbdma)
+{
+	stream_t *dma = &vortex->dma_adb[adbdma];
+
+	int this_8 = 1, this_4 = 0;
+	switch (dma->fifo_status) {
+	case FIFO_STOP:
+		hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2),
+			dma->dma_ctrl);
+		vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown,
+				       this_4, this_8,
+				       dma->fifo_enabled ? 1 : 0, 0);
+		break;
+	case FIFO_PAUSE:
+		vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown,
+				       this_4, this_8,
+				       dma->fifo_enabled ? 1 : 0, 0);
+		break;
+	}
+	dma->fifo_status = FIFO_START;
+}
+
+static void vortex_adbdma_pausefifo(vortex_t * vortex, int adbdma)
+{
+	stream_t *dma = &vortex->dma_adb[adbdma];
+
+	int this_8 = 0, this_4 = 0;
+	switch (dma->fifo_status) {
+	case FIFO_START:
+		vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown,
+				       this_4, this_8, 0, 0);
+		break;
+	case FIFO_STOP:
+		hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2),
+			dma->dma_ctrl);
+		vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown,
+				       this_4, this_8, 0, 0);
+		break;
+	}
+	dma->fifo_status = FIFO_PAUSE;
+}
+
+#if 0				// Using pause instead
+static void vortex_adbdma_stopfifo(vortex_t * vortex, int adbdma)
+{
+	stream_t *dma = &vortex->dma_adb[adbdma];
+
+	int this_4 = 0, this_8 = 0;
+	if (dma->fifo_status == FIFO_START)
+		vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown,
+				       this_4, this_8, 0, 0);
+	else if (dma->fifo_status == FIFO_STOP)
+		return;
+	dma->fifo_status = FIFO_STOP;
+	dma->fifo_enabled = 0;
+}
+
+#endif
+/* WTDMA */
+
+#ifndef CHIP_AU8810
+static void vortex_wtdma_setfirstbuffer(vortex_t * vortex, int wtdma)
+{
+	//int this_7c=dma_ctrl;
+	stream_t *dma = &vortex->dma_wt[wtdma];
+
+	hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2), dma->dma_ctrl);
+}
+
+static void vortex_wtdma_setstartbuffer(vortex_t * vortex, int wtdma, int sb)
+{
+	stream_t *dma = &vortex->dma_wt[wtdma];
+	//hwwrite(vortex->mmio, VORTEX_WTDMA_START + (wtdma << 2), sb << ((0x1f-(wtdma&0xf)*2)));
+	hwwrite(vortex->mmio, VORTEX_WTDMA_START + (wtdma << 2),
+		sb << ((0xf - (wtdma & 0xf)) * 2));
+	dma->period_real = dma->period_virt = sb;
+}
+
+static void
+vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma,
+			snd_pcm_sgbuf_t * sgbuf, int psize, int count)
+{
+	stream_t *dma = &vortex->dma_wt[wtdma];
+
+	dma->period_bytes = psize;
+	dma->nr_periods = count;
+	dma->sgbuf = sgbuf;
+
+	dma->cfg0 = 0;
+	dma->cfg1 = 0;
+	switch (count) {
+		/* Four or more pages */
+	default:
+	case 4:
+		dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | (psize-1);
+		hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0xc,
+			snd_sgbuf_get_addr(sgbuf, psize * 3));
+		/* 3 pages */
+	case 3:
+		dma->cfg0 |= 0x12000000;
+		dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);
+		hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4)  + 0x8,
+			snd_sgbuf_get_addr(sgbuf, psize * 2));
+		/* 2 pages */
+	case 2:
+		dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize-1);
+		hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x4,
+			snd_sgbuf_get_addr(sgbuf, psize));
+		/* 1 page */
+	case 1:
+		dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);
+		hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4),
+			snd_sgbuf_get_addr(sgbuf, 0));
+		break;
+	}
+	hwwrite(vortex->mmio, VORTEX_WTDMA_BUFCFG0 + (wtdma << 3), dma->cfg0);
+	hwwrite(vortex->mmio, VORTEX_WTDMA_BUFCFG1 + (wtdma << 3), dma->cfg1);
+
+	vortex_wtdma_setfirstbuffer(vortex, wtdma);
+	vortex_wtdma_setstartbuffer(vortex, wtdma, 0);
+}
+
+static void
+vortex_wtdma_setmode(vortex_t * vortex, int wtdma, int ie, int fmt, int d,
+		     /*int e, */ unsigned long offset)
+{
+	stream_t *dma = &vortex->dma_wt[wtdma];
+
+	//dma->this_08 = e;
+	dma->dma_unknown = d;
+	dma->dma_ctrl = 0;
+	dma->dma_ctrl =
+	    ((offset & OFFSET_MASK) | (dma->dma_ctrl & ~OFFSET_MASK));
+	/* PCMOUT interrupt */
+	dma->dma_ctrl =
+	    (dma->dma_ctrl & ~IE_MASK) | ((ie << IE_SHIFT) & IE_MASK);
+	/* Always playback. */
+	dma->dma_ctrl |= (1 << DIR_SHIFT);
+	/* Audio Format */
+	dma->dma_ctrl =
+	    (dma->dma_ctrl & FMT_MASK) | ((fmt << FMT_SHIFT) & FMT_MASK);
+	/* Write into hardware */
+	hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2), dma->dma_ctrl);
+}
+
+static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma)
+{
+	stream_t *dma = &vortex->dma_wt[wtdma];
+	int page, p, pp, delta, i;
+
+	page =
+	    (hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2)) &
+	     WT_SUBBUF_MASK)
+	    >> WT_SUBBUF_SHIFT;
+	if (dma->nr_periods >= 4)
+		delta = (page - dma->period_real) & 3;
+	else {
+		delta = (page - dma->period_real);
+		if (delta < 0)
+			delta += dma->nr_periods;
+	}
+	if (delta == 0)
+		return 0;
+
+	/* refresh hw page table */
+	if (dma->nr_periods > 4) {
+		for (i = 0; i < delta; i++) {
+			/* p: audio buffer page index */
+			p = dma->period_virt + i + 4;
+			if (p >= dma->nr_periods)
+				p -= dma->nr_periods;
+			/* pp: hardware DMA page index. */
+			pp = dma->period_real + i;
+			if (pp >= 4)
+				pp -= 4;
+			hwwrite(vortex->mmio,
+				VORTEX_WTDMA_BUFBASE +
+				(((wtdma << 2) + pp) << 2),
+				snd_sgbuf_get_addr(dma->sgbuf, dma->period_bytes * p));
+			/* Force write thru cache. */
+			hwread(vortex->mmio, VORTEX_WTDMA_BUFBASE +
+			       (((wtdma << 2) + pp) << 2));
+		}
+	}
+	dma->period_virt += delta;
+	if (dma->period_virt >= dma->nr_periods)
+		dma->period_virt -= dma->nr_periods;
+	dma->period_real = page;
+
+	if (delta != 1)
+		printk(KERN_WARNING "vortex: wt virt = %d, delta = %d\n",
+		       dma->period_virt, delta);
+
+	return delta;
+}
+
+#if 0
+static void
+vortex_wtdma_getposition(vortex_t * vortex, int wtdma, int *subbuf, int *pos)
+{
+	int temp;
+	temp = hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2));
+	*subbuf = (temp >> WT_SUBBUF_SHIFT) & WT_SUBBUF_MASK;
+	*pos = temp & POS_MASK;
+}
+
+static int vortex_wtdma_getcursubuffer(vortex_t * vortex, int wtdma)
+{
+	return ((hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2)) >>
+		 POS_SHIFT) & POS_MASK);
+}
+#endif
+static int inline vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma)
+{
+	stream_t *dma = &vortex->dma_wt[wtdma];
+	int temp;
+
+	temp = hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2));
+	//temp = (temp & POS_MASK) + (((temp>>WT_SUBBUF_SHIFT) & WT_SUBBUF_MASK)*(dma->cfg0&POS_MASK));
+	temp = (temp & POS_MASK) + ((dma->period_virt) * (dma->period_bytes));
+	return temp;
+}
+
+static void vortex_wtdma_startfifo(vortex_t * vortex, int wtdma)
+{
+	stream_t *dma = &vortex->dma_wt[wtdma];
+	int this_8 = 0, this_4 = 0;
+
+	switch (dma->fifo_status) {
+	case FIFO_START:
+		vortex_fifo_setwtvalid(vortex, wtdma,
+				       dma->fifo_enabled ? 1 : 0);
+		break;
+	case FIFO_STOP:
+		this_8 = 1;
+		hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2),
+			dma->dma_ctrl);
+		vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+				      this_4, this_8,
+				      dma->fifo_enabled ? 1 : 0, 0);
+		break;
+	case FIFO_PAUSE:
+		vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+				      this_4, this_8,
+				      dma->fifo_enabled ? 1 : 0, 0);
+		break;
+	}
+	dma->fifo_status = FIFO_START;
+}
+
+static void vortex_wtdma_resumefifo(vortex_t * vortex, int wtdma)
+{
+	stream_t *dma = &vortex->dma_wt[wtdma];
+
+	int this_8 = 0, this_4 = 0;
+	switch (dma->fifo_status) {
+	case FIFO_STOP:
+		hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2),
+			dma->dma_ctrl);
+		vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+				      this_4, this_8,
+				      dma->fifo_enabled ? 1 : 0, 0);
+		break;
+	case FIFO_PAUSE:
+		vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+				      this_4, this_8,
+				      dma->fifo_enabled ? 1 : 0, 0);
+		break;
+	}
+	dma->fifo_status = FIFO_START;
+}
+
+static void vortex_wtdma_pausefifo(vortex_t * vortex, int wtdma)
+{
+	stream_t *dma = &vortex->dma_wt[wtdma];
+
+	int this_8 = 0, this_4 = 0;
+	switch (dma->fifo_status) {
+	case FIFO_START:
+		vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+				      this_4, this_8, 0, 0);
+		break;
+	case FIFO_STOP:
+		hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2),
+			dma->dma_ctrl);
+		vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+				      this_4, this_8, 0, 0);
+		break;
+	}
+	dma->fifo_status = FIFO_PAUSE;
+}
+
+static void vortex_wtdma_stopfifo(vortex_t * vortex, int wtdma)
+{
+	stream_t *dma = &vortex->dma_wt[wtdma];
+
+	int this_4 = 0, this_8 = 0;
+	if (dma->fifo_status == FIFO_START)
+		vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+				      this_4, this_8, 0, 0);
+	else if (dma->fifo_status == FIFO_STOP)
+		return;
+	dma->fifo_status = FIFO_STOP;
+	dma->fifo_enabled = 0;
+}
+
+#endif
+/* ADB Routes */
+
+typedef int ADBRamLink;
+static void vortex_adb_init(vortex_t * vortex)
+{
+	int i;
+	/* it looks like we are writing more than we need to...
+	 * if we write what we are supposed to it breaks things... */
+	hwwrite(vortex->mmio, VORTEX_ADB_SR, 0);
+	for (i = 0; i < VORTEX_ADB_RTBASE_COUNT; i++)
+		hwwrite(vortex->mmio, VORTEX_ADB_RTBASE + (i << 2),
+			hwread(vortex->mmio,
+			       VORTEX_ADB_RTBASE + (i << 2)) | ROUTE_MASK);
+	for (i = 0; i < VORTEX_ADB_CHNBASE_COUNT; i++) {
+		hwwrite(vortex->mmio, VORTEX_ADB_CHNBASE + (i << 2),
+			hwread(vortex->mmio,
+			       VORTEX_ADB_CHNBASE + (i << 2)) | ROUTE_MASK);
+	}
+}
+
+static void vortex_adb_en_sr(vortex_t * vortex, int channel)
+{
+	hwwrite(vortex->mmio, VORTEX_ADB_SR,
+		hwread(vortex->mmio, VORTEX_ADB_SR) | (0x1 << channel));
+}
+
+static void vortex_adb_dis_sr(vortex_t * vortex, int channel)
+{
+	hwwrite(vortex->mmio, VORTEX_ADB_SR,
+		hwread(vortex->mmio, VORTEX_ADB_SR) & ~(0x1 << channel));
+}
+
+static void
+vortex_adb_addroutes(vortex_t * vortex, unsigned char channel,
+		     ADBRamLink * route, int rnum)
+{
+	int temp, prev, lifeboat = 0;
+
+	if ((rnum <= 0) || (route == NULL))
+		return;
+	/* Write last routes. */
+	rnum--;
+	hwwrite(vortex->mmio,
+		VORTEX_ADB_RTBASE + ((route[rnum] & ADB_MASK) << 2),
+		ROUTE_MASK);
+	while (rnum > 0) {
+		hwwrite(vortex->mmio,
+			VORTEX_ADB_RTBASE +
+			((route[rnum - 1] & ADB_MASK) << 2), route[rnum]);
+		rnum--;
+	}
+	/* Write first route. */
+	temp =
+	    hwread(vortex->mmio,
+		   VORTEX_ADB_CHNBASE + (channel << 2)) & ADB_MASK;
+	if (temp == ADB_MASK) {
+		/* First entry on this channel. */
+		hwwrite(vortex->mmio, VORTEX_ADB_CHNBASE + (channel << 2),
+			route[0]);
+		vortex_adb_en_sr(vortex, channel);
+		return;
+	}
+	/* Not first entry on this channel. Need to link. */
+	do {
+		prev = temp;
+		temp =
+		    hwread(vortex->mmio,
+			   VORTEX_ADB_RTBASE + (temp << 2)) & ADB_MASK;
+		if ((lifeboat++) > ADB_MASK) {
+			printk(KERN_ERR
+			       "vortex_adb_addroutes: unending route! 0x%x\n",
+			       *route);
+			return;
+		}
+	}
+	while (temp != ADB_MASK);
+	hwwrite(vortex->mmio, VORTEX_ADB_RTBASE + (prev << 2), route[0]);
+}
+
+static void
+vortex_adb_delroutes(vortex_t * vortex, unsigned char channel,
+		     ADBRamLink route0, ADBRamLink route1)
+{
+	int temp, lifeboat = 0, prev;
+
+	/* Find route. */
+	temp =
+	    hwread(vortex->mmio,
+		   VORTEX_ADB_CHNBASE + (channel << 2)) & ADB_MASK;
+	if (temp == (route0 & ADB_MASK)) {
+		temp =
+		    hwread(vortex->mmio,
+			   VORTEX_ADB_RTBASE + ((route1 & ADB_MASK) << 2));
+		if ((temp & ADB_MASK) == ADB_MASK)
+			vortex_adb_dis_sr(vortex, channel);
+		hwwrite(vortex->mmio, VORTEX_ADB_CHNBASE + (channel << 2),
+			temp);
+		return;
+	}
+	do {
+		prev = temp;
+		temp =
+		    hwread(vortex->mmio,
+			   VORTEX_ADB_RTBASE + (prev << 2)) & ADB_MASK;
+		if (((lifeboat++) > ADB_MASK) || (temp == ADB_MASK)) {
+			printk(KERN_ERR
+			       "vortex_adb_delroutes: route not found! 0x%x\n",
+			       route0);
+			return;
+		}
+	}
+	while (temp != (route0 & ADB_MASK));
+	temp = hwread(vortex->mmio, VORTEX_ADB_RTBASE + (temp << 2));
+	if ((temp & ADB_MASK) == route1)
+		temp = hwread(vortex->mmio, VORTEX_ADB_RTBASE + (temp << 2));
+	/* Make bridge over deleted route. */
+	hwwrite(vortex->mmio, VORTEX_ADB_RTBASE + (prev << 2), temp);
+}
+
+static void
+vortex_route(vortex_t * vortex, int en, unsigned char channel,
+	     unsigned char source, unsigned char dest)
+{
+	ADBRamLink route;
+
+	route = ((source & ADB_MASK) << ADB_SHIFT) | (dest & ADB_MASK);
+	if (en) {
+		vortex_adb_addroutes(vortex, channel, &route, 1);
+		if ((source < (OFFSET_SRCOUT + NR_SRC))
+		    && (source >= OFFSET_SRCOUT))
+			vortex_src_addWTD(vortex, (source - OFFSET_SRCOUT),
+					  channel);
+		else if ((source < (OFFSET_MIXOUT + NR_MIXOUT))
+			 && (source >= OFFSET_MIXOUT))
+			vortex_mixer_addWTD(vortex,
+					    (source - OFFSET_MIXOUT), channel);
+	} else {
+		vortex_adb_delroutes(vortex, channel, route, route);
+		if ((source < (OFFSET_SRCOUT + NR_SRC))
+		    && (source >= OFFSET_SRCOUT))
+			vortex_src_delWTD(vortex, (source - OFFSET_SRCOUT),
+					  channel);
+		else if ((source < (OFFSET_MIXOUT + NR_MIXOUT))
+			 && (source >= OFFSET_MIXOUT))
+			vortex_mixer_delWTD(vortex,
+					    (source - OFFSET_MIXOUT), channel);
+	}
+}
+
+#if 0
+static void
+vortex_routes(vortex_t * vortex, int en, unsigned char channel,
+	      unsigned char source, unsigned char dest0, unsigned char dest1)
+{
+	ADBRamLink route[2];
+
+	route[0] = ((source & ADB_MASK) << ADB_SHIFT) | (dest0 & ADB_MASK);
+	route[1] = ((source & ADB_MASK) << ADB_SHIFT) | (dest1 & ADB_MASK);
+
+	if (en) {
+		vortex_adb_addroutes(vortex, channel, route, 2);
+		if ((source < (OFFSET_SRCOUT + NR_SRC))
+		    && (source >= (OFFSET_SRCOUT)))
+			vortex_src_addWTD(vortex, (source - OFFSET_SRCOUT),
+					  channel);
+		else if ((source < (OFFSET_MIXOUT + NR_MIXOUT))
+			 && (source >= (OFFSET_MIXOUT)))
+			vortex_mixer_addWTD(vortex,
+					    (source - OFFSET_MIXOUT), channel);
+	} else {
+		vortex_adb_delroutes(vortex, channel, route[0], route[1]);
+		if ((source < (OFFSET_SRCOUT + NR_SRC))
+		    && (source >= (OFFSET_SRCOUT)))
+			vortex_src_delWTD(vortex, (source - OFFSET_SRCOUT),
+					  channel);
+		else if ((source < (OFFSET_MIXOUT + NR_MIXOUT))
+			 && (source >= (OFFSET_MIXOUT)))
+			vortex_mixer_delWTD(vortex,
+					    (source - OFFSET_MIXOUT), channel);
+	}
+}
+
+#endif
+/* Route two sources to same target. Sources must be of same class !!! */
+static void
+vortex_routeLRT(vortex_t * vortex, int en, unsigned char ch,
+		unsigned char source0, unsigned char source1,
+		unsigned char dest)
+{
+	ADBRamLink route[2];
+
+	route[0] = ((source0 & ADB_MASK) << ADB_SHIFT) | (dest & ADB_MASK);
+	route[1] = ((source1 & ADB_MASK) << ADB_SHIFT) | (dest & ADB_MASK);
+
+	if (dest < 0x10)
+		route[1] = (route[1] & ~ADB_MASK) | (dest + 0x20);	/* fifo A */
+
+	if (en) {
+		vortex_adb_addroutes(vortex, ch, route, 2);
+		if ((source0 < (OFFSET_SRCOUT + NR_SRC))
+		    && (source0 >= OFFSET_SRCOUT)) {
+			vortex_src_addWTD(vortex,
+					  (source0 - OFFSET_SRCOUT), ch);
+			vortex_src_addWTD(vortex,
+					  (source1 - OFFSET_SRCOUT), ch);
+		} else if ((source0 < (OFFSET_MIXOUT + NR_MIXOUT))
+			   && (source0 >= OFFSET_MIXOUT)) {
+			vortex_mixer_addWTD(vortex,
+					    (source0 - OFFSET_MIXOUT), ch);
+			vortex_mixer_addWTD(vortex,
+					    (source1 - OFFSET_MIXOUT), ch);
+		}
+	} else {
+		vortex_adb_delroutes(vortex, ch, route[0], route[1]);
+		if ((source0 < (OFFSET_SRCOUT + NR_SRC))
+		    && (source0 >= OFFSET_SRCOUT)) {
+			vortex_src_delWTD(vortex,
+					  (source0 - OFFSET_SRCOUT), ch);
+			vortex_src_delWTD(vortex,
+					  (source1 - OFFSET_SRCOUT), ch);
+		} else if ((source0 < (OFFSET_MIXOUT + NR_MIXOUT))
+			   && (source0 >= OFFSET_MIXOUT)) {
+			vortex_mixer_delWTD(vortex,
+					    (source0 - OFFSET_MIXOUT), ch);
+			vortex_mixer_delWTD(vortex,
+					    (source1 - OFFSET_MIXOUT), ch);
+		}
+	}
+}
+
+/* Connection stuff */
+
+// Connect adbdma to src('s).
+static void
+vortex_connection_adbdma_src(vortex_t * vortex, int en, unsigned char ch,
+			     unsigned char adbdma, unsigned char src)
+{
+	vortex_route(vortex, en, ch, ADB_DMA(adbdma), ADB_SRCIN(src));
+}
+
+// Connect SRC to mixin.
+static void
+vortex_connection_src_mixin(vortex_t * vortex, int en,
+			    unsigned char channel, unsigned char src,
+			    unsigned char mixin)
+{
+	vortex_route(vortex, en, channel, ADB_SRCOUT(src), ADB_MIXIN(mixin));
+}
+
+// Connect mixin with mix output.
+static void
+vortex_connection_mixin_mix(vortex_t * vortex, int en, unsigned char mixin,
+			    unsigned char mix, int a)
+{
+	if (en) {
+		vortex_mix_enableinput(vortex, mix, mixin);
+		vortex_mix_setinputvolumebyte(vortex, mix, mixin, MIX_DEFIGAIN);	// added to original code.
+	} else
+		vortex_mix_disableinput(vortex, mix, mixin, a);
+}
+
+// Connect absolut address to mixin.
+static void
+vortex_connection_adb_mixin(vortex_t * vortex, int en,
+			    unsigned char channel, unsigned char source,
+			    unsigned char mixin)
+{
+	vortex_route(vortex, en, channel, source, ADB_MIXIN(mixin));
+}
+
+static void
+vortex_connection_src_adbdma(vortex_t * vortex, int en, unsigned char ch,
+			     unsigned char src, unsigned char adbdma)
+{
+	vortex_route(vortex, en, ch, ADB_SRCOUT(src), ADB_DMA(adbdma));
+}
+
+static void
+vortex_connection_src_src_adbdma(vortex_t * vortex, int en,
+				 unsigned char ch, unsigned char src0,
+				 unsigned char src1, unsigned char adbdma)
+{
+
+	vortex_routeLRT(vortex, en, ch, ADB_SRCOUT(src0), ADB_SRCOUT(src1),
+			ADB_DMA(adbdma));
+}
+
+// mix to absolut address.
+static void
+vortex_connection_mix_adb(vortex_t * vortex, int en, unsigned char ch,
+			  unsigned char mix, unsigned char dest)
+{
+	vortex_route(vortex, en, ch, ADB_MIXOUT(mix), dest);
+	vortex_mix_setvolumebyte(vortex, mix, MIX_DEFOGAIN);	// added to original code.
+}
+
+// mixer to src.
+static void
+vortex_connection_mix_src(vortex_t * vortex, int en, unsigned char ch,
+			  unsigned char mix, unsigned char src)
+{
+	vortex_route(vortex, en, ch, ADB_MIXOUT(mix), ADB_SRCIN(src));
+	vortex_mix_setvolumebyte(vortex, mix, MIX_DEFOGAIN);	// added to original code.
+}
+
+#if 0
+static void
+vortex_connection_adbdma_src_src(vortex_t * vortex, int en,
+				 unsigned char channel,
+				 unsigned char adbdma, unsigned char src0,
+				 unsigned char src1)
+{
+	vortex_routes(vortex, en, channel, ADB_DMA(adbdma),
+		      ADB_SRCIN(src0), ADB_SRCIN(src1));
+}
+
+// Connect two mix to AdbDma.
+static void
+vortex_connection_mix_mix_adbdma(vortex_t * vortex, int en,
+				 unsigned char ch, unsigned char mix0,
+				 unsigned char mix1, unsigned char adbdma)
+{
+
+	ADBRamLink routes[2];
+	routes[0] =
+	    (((mix0 +
+	       OFFSET_MIXOUT) & ADB_MASK) << ADB_SHIFT) | (adbdma & ADB_MASK);
+	routes[1] =
+	    (((mix1 + OFFSET_MIXOUT) & ADB_MASK) << ADB_SHIFT) | ((adbdma +
+								   0x20) &
+								  ADB_MASK);
+	if (en) {
+		vortex_adb_addroutes(vortex, ch, routes, 0x2);
+		vortex_mixer_addWTD(vortex, mix0, ch);
+		vortex_mixer_addWTD(vortex, mix1, ch);
+	} else {
+		vortex_adb_delroutes(vortex, ch, routes[0], routes[1]);
+		vortex_mixer_delWTD(vortex, mix0, ch);
+		vortex_mixer_delWTD(vortex, mix1, ch);
+	}
+}
+#endif
+
+/* CODEC connect. */
+
+static void
+vortex_connect_codecplay(vortex_t * vortex, int en, unsigned char mixers[])
+{
+#ifdef CHIP_AU8820
+	vortex_connection_mix_adb(vortex, en, 0x11, mixers[0], ADB_CODECOUT(0));
+	vortex_connection_mix_adb(vortex, en, 0x11, mixers[1], ADB_CODECOUT(1));
+#else
+#if 1
+	// Connect front channels through EQ.
+	vortex_connection_mix_adb(vortex, en, 0x11, mixers[0], ADB_EQIN(0));
+	vortex_connection_mix_adb(vortex, en, 0x11, mixers[1], ADB_EQIN(1));
+	/* Lower volume, since EQ has some gain. */
+	vortex_mix_setvolumebyte(vortex, mixers[0], 0);
+	vortex_mix_setvolumebyte(vortex, mixers[1], 0);
+	vortex_route(vortex, en, 0x11, ADB_EQOUT(0), ADB_CODECOUT(0));
+	vortex_route(vortex, en, 0x11, ADB_EQOUT(1), ADB_CODECOUT(1));
+
+	/* Check if reg 0x28 has SDAC bit set. */
+	if (VORTEX_IS_QUAD(vortex)) {
+		/* Rear channel. Note: ADB_CODECOUT(0+2) and (1+2) is for AC97 modem */
+		vortex_connection_mix_adb(vortex, en, 0x11, mixers[2],
+					  ADB_CODECOUT(0 + 4));
+		vortex_connection_mix_adb(vortex, en, 0x11, mixers[3],
+					  ADB_CODECOUT(1 + 4));
+		//printk("SDAC detected ");
+	}
+#else
+	// Use plain direct output to codec.
+	vortex_connection_mix_adb(vortex, en, 0x11, mixers[0], ADB_CODECOUT(0));
+	vortex_connection_mix_adb(vortex, en, 0x11, mixers[1], ADB_CODECOUT(1));
+#endif
+#endif
+}
+
+static void
+vortex_connect_codecrec(vortex_t * vortex, int en, unsigned char mixin0,
+			unsigned char mixin1)
+{
+	/*
+	   Enable: 0x1, 0x1
+	   Channel: 0x11, 0x11
+	   ADB Source address: 0x48, 0x49
+	   Destination Asp4Topology_0x9c,0x98
+	 */
+	vortex_connection_adb_mixin(vortex, en, 0x11, ADB_CODECIN(0), mixin0);
+	vortex_connection_adb_mixin(vortex, en, 0x11, ADB_CODECIN(1), mixin1);
+}
+
+// Higher level ADB audio path (de)allocator.
+
+/* Resource manager */
+static int resnum[VORTEX_RESOURCE_LAST] =
+    { NR_ADB, NR_SRC, NR_MIXIN, NR_MIXOUT, NR_A3D };
+/*
+ Checkout/Checkin resource of given type. 
+ resmap: resource map to be used. If NULL means that we want to allocate
+ a DMA resource (root of all other resources of a dma channel).
+ out: Mean checkout if != 0. Else mean Checkin resource.
+ restype: Indicates type of resource to be checked in or out.
+*/
+static char
+vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
+{
+	int i, qty = resnum[restype], resinuse = 0;
+
+	if (out) {
+		/* Gather used resources by all streams. */
+		for (i = 0; i < NR_ADB; i++) {
+			resinuse |= vortex->dma_adb[i].resources[restype];
+		}
+		resinuse |= vortex->fixed_res[restype];
+		/* Find and take free resource. */
+		for (i = 0; i < qty; i++) {
+			if ((resinuse & (1 << i)) == 0) {
+				if (resmap != NULL)
+					resmap[restype] |= (1 << i);
+				else
+					vortex->dma_adb[i].resources[restype] |= (1 << i);
+				//printk("vortex: ResManager: type %d out %d\n", restype, i);
+				return i;
+			}
+		}
+	} else {
+		if (resmap == NULL)
+			return -EINVAL;
+		/* Checkin first resource of type restype. */
+		for (i = 0; i < qty; i++) {
+			if (resmap[restype] & (1 << i)) {
+				resmap[restype] &= ~(1 << i);
+				//printk("vortex: ResManager: type %d in %d\n",restype, i);
+				return i;
+			}
+		}
+	}
+	printk("vortex: FATAL: ResManager: resource type %d exhausted.\n", restype);
+	return -ENOMEM;
+}
+
+/* Default Connections  */
+static int
+vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type);
+
+static void vortex_connect_default(vortex_t * vortex, int en)
+{
+	// Connect AC97 codec.
+	vortex->mixplayb[0] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+				  VORTEX_RESOURCE_MIXOUT);
+	vortex->mixplayb[1] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+				  VORTEX_RESOURCE_MIXOUT);
+	if (VORTEX_IS_QUAD(vortex)) {
+		vortex->mixplayb[2] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+					  VORTEX_RESOURCE_MIXOUT);
+		vortex->mixplayb[3] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+					  VORTEX_RESOURCE_MIXOUT);
+	}
+	vortex_connect_codecplay(vortex, en, vortex->mixplayb);
+
+	vortex->mixcapt[0] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+				  VORTEX_RESOURCE_MIXIN);
+	vortex->mixcapt[1] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+				  VORTEX_RESOURCE_MIXIN);
+	vortex_connect_codecrec(vortex, en, MIX_CAPT(0), MIX_CAPT(1));
+
+	// Connect SPDIF
+#ifndef CHIP_AU8820
+	vortex->mixspdif[0] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+				  VORTEX_RESOURCE_MIXOUT);
+	vortex->mixspdif[1] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+				  VORTEX_RESOURCE_MIXOUT);
+	vortex_connection_mix_adb(vortex, en, 0x14, vortex->mixspdif[0],
+				  ADB_SPDIFOUT(0));
+	vortex_connection_mix_adb(vortex, en, 0x14, vortex->mixspdif[1],
+				  ADB_SPDIFOUT(1));
+#endif
+	// Connect WT
+#ifndef CHIP_AU8810
+	vortex_wt_connect(vortex, en);
+#endif
+	// A3D (crosstalk canceler and A3D slices). AU8810 disabled for now.
+#ifndef CHIP_AU8820
+	vortex_Vort3D_connect(vortex, en);
+#endif
+	// Connect I2S
+
+	// Connect DSP interface for SQ3500 turbo (not here i think...)
+
+	// Connect AC98 modem codec
+	
+}
+
+/*
+  Allocate nr_ch pcm audio routes if dma < 0. If dma >= 0, existing routes
+  are deallocated.
+  dma: DMA engine routes to be deallocated when dma >= 0.
+  nr_ch: Number of channels to be de/allocated.
+  dir: direction of stream. Uses same values as substream->stream.
+  type: Type of audio output/source (codec, spdif, i2s, dsp, etc)
+  Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0.
+*/
+static int
+vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
+{
+	stream_t *stream;
+	int i, en;
+	
+	if ((nr_ch == 3)
+	    || ((dir == SNDRV_PCM_STREAM_CAPTURE) && (nr_ch > 2)))
+		return -EBUSY;
+
+	if (dma >= 0) {
+		en = 0;
+		vortex_adb_checkinout(vortex,
+				      vortex->dma_adb[dma].resources, en,
+				      VORTEX_RESOURCE_DMA);
+	} else {
+		en = 1;
+		if ((dma =
+		     vortex_adb_checkinout(vortex, NULL, en,
+					   VORTEX_RESOURCE_DMA)) < 0)
+			return -EBUSY;
+	}
+
+	stream = &vortex->dma_adb[dma];
+	stream->dma = dma;
+	stream->dir = dir;
+	stream->type = type;
+
+	/* PLAYBACK ROUTES. */
+	if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+		int src[4], mix[4], ch_top;
+#ifndef CHIP_AU8820
+		int a3d = 0;
+#endif
+		/* Get SRC and MIXER hardware resources. */
+		if (stream->type != VORTEX_PCM_SPDIF) {
+			for (i = 0; i < nr_ch; i++) {
+				if ((src[i] = vortex_adb_checkinout(vortex,
+							   stream->resources, en,
+							   VORTEX_RESOURCE_SRC)) < 0) {
+					memset(stream->resources, 0,
+					       sizeof(unsigned char) *
+					       VORTEX_RESOURCE_LAST);
+					return -EBUSY;
+				}
+				if (stream->type != VORTEX_PCM_A3D) {
+					if ((mix[i] = vortex_adb_checkinout(vortex,
+								   stream->resources,
+								   en,
+								   VORTEX_RESOURCE_MIXIN)) < 0) {
+						memset(stream->resources,
+						       0,
+						       sizeof(unsigned char) * VORTEX_RESOURCE_LAST);
+						return -EBUSY;
+					}
+				}
+			}
+		}
+#ifndef CHIP_AU8820
+		if (stream->type == VORTEX_PCM_A3D) {
+			if ((a3d =
+			     vortex_adb_checkinout(vortex,
+						   stream->resources, en,
+						   VORTEX_RESOURCE_A3D)) < 0) {
+				memset(stream->resources, 0,
+				       sizeof(unsigned char) *
+				       VORTEX_RESOURCE_LAST);
+				printk("vortex: out of A3D sources. Sorry\n");
+				return -EBUSY;
+			}
+			/* (De)Initialize A3D hardware source. */
+			vortex_Vort3D_InitializeSource(&(vortex->a3d[a3d]), en);
+		}
+		/* Make SPDIF out exclusive to "spdif" device when in use. */
+		if ((stream->type == VORTEX_PCM_SPDIF) && (en)) {
+			vortex_route(vortex, 0, 0x14,
+				     ADB_MIXOUT(vortex->mixspdif[0]),
+				     ADB_SPDIFOUT(0));
+			vortex_route(vortex, 0, 0x14,
+				     ADB_MIXOUT(vortex->mixspdif[1]),
+				     ADB_SPDIFOUT(1));
+		}
+#endif
+		/* Make playback routes. */
+		for (i = 0; i < nr_ch; i++) {
+			if (stream->type == VORTEX_PCM_ADB) {
+				vortex_connection_adbdma_src(vortex, en,
+							     src[nr_ch - 1],
+							     dma,
+							     src[i]);
+				vortex_connection_src_mixin(vortex, en,
+							    0x11, src[i],
+							    mix[i]);
+				vortex_connection_mixin_mix(vortex, en,
+							    mix[i],
+							    MIX_PLAYB(i), 0);
+#ifndef CHIP_AU8820
+				vortex_connection_mixin_mix(vortex, en,
+							    mix[i],
+							    MIX_SPDIF(i % 2), 0);
+				vortex_mix_setinputvolumebyte(vortex,
+							      MIX_SPDIF(i % 2),
+							      mix[i],
+							      MIX_DEFIGAIN);
+#endif
+			}
+#ifndef CHIP_AU8820
+			if (stream->type == VORTEX_PCM_A3D) {
+				vortex_connection_adbdma_src(vortex, en,
+							     src[nr_ch - 1], 
+								 dma,
+							     src[i]);
+				vortex_route(vortex, en, 0x11, ADB_SRCOUT(src[i]), ADB_A3DIN(a3d));
+				/* XTalk test. */
+				//vortex_route(vortex, en, 0x11, dma, ADB_XTALKIN(i?9:4));
+				//vortex_route(vortex, en, 0x11, ADB_SRCOUT(src[i]), ADB_XTALKIN(i?4:9));
+			}
+			if (stream->type == VORTEX_PCM_SPDIF)
+				vortex_route(vortex, en, 0x14,
+					     ADB_DMA(stream->dma),
+					     ADB_SPDIFOUT(i));
+#endif
+		}
+		if (stream->type != VORTEX_PCM_SPDIF && stream->type != VORTEX_PCM_A3D) {
+			ch_top = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+			for (i = nr_ch; i < ch_top; i++) {
+				vortex_connection_mixin_mix(vortex, en,
+							    mix[i % nr_ch],
+							    MIX_PLAYB(i), 0);
+#ifndef CHIP_AU8820
+				vortex_connection_mixin_mix(vortex, en,
+							    mix[i % nr_ch],
+							    MIX_SPDIF(i % 2),
+								0);
+				vortex_mix_setinputvolumebyte(vortex,
+							      MIX_SPDIF(i % 2),
+							      mix[i % nr_ch],
+							      MIX_DEFIGAIN);
+#endif
+			}
+		}
+#ifndef CHIP_AU8820
+		else {
+			if (nr_ch == 1 && stream->type == VORTEX_PCM_SPDIF)
+				vortex_route(vortex, en, 0x14,
+					     ADB_DMA(stream->dma),
+					     ADB_SPDIFOUT(1));
+		}
+		/* Reconnect SPDIF out when "spdif" device is down. */
+		if ((stream->type == VORTEX_PCM_SPDIF) && (!en)) {
+			vortex_route(vortex, 1, 0x14,
+				     ADB_MIXOUT(vortex->mixspdif[0]),
+				     ADB_SPDIFOUT(0));
+			vortex_route(vortex, 1, 0x14,
+				     ADB_MIXOUT(vortex->mixspdif[1]),
+				     ADB_SPDIFOUT(1));
+		}
+#endif
+	/* CAPTURE ROUTES. */
+	} else {
+		int src[2], mix[2];
+
+		/* Get SRC and MIXER hardware resources. */
+		for (i = 0; i < nr_ch; i++) {
+			if ((mix[i] =
+			     vortex_adb_checkinout(vortex,
+						   stream->resources, en,
+						   VORTEX_RESOURCE_MIXOUT))
+			    < 0) {
+				memset(stream->resources, 0,
+				       sizeof(unsigned char) *
+				       VORTEX_RESOURCE_LAST);
+				return -EBUSY;
+			}
+			if ((src[i] =
+			     vortex_adb_checkinout(vortex,
+						   stream->resources, en,
+						   VORTEX_RESOURCE_SRC)) < 0) {
+				memset(stream->resources, 0,
+				       sizeof(unsigned char) *
+				       VORTEX_RESOURCE_LAST);
+				return -EBUSY;
+			}
+		}
+
+		/* Make capture routes. */
+		vortex_connection_mixin_mix(vortex, en, MIX_CAPT(0), mix[0], 0);
+		vortex_connection_mix_src(vortex, en, 0x11, mix[0], src[0]);
+		if (nr_ch == 1) {
+			vortex_connection_mixin_mix(vortex, en,
+						    MIX_CAPT(1), mix[0], 0);
+			vortex_connection_src_adbdma(vortex, en,
+						     src[0],
+						     src[0], dma);
+		} else {
+			vortex_connection_mixin_mix(vortex, en,
+						    MIX_CAPT(1), mix[1], 0);
+			vortex_connection_mix_src(vortex, en, 0x11, mix[1],
+						  src[1]);
+			vortex_connection_src_src_adbdma(vortex, en,
+							 src[1], src[0],
+							 src[1], dma);
+		}
+	}
+	vortex->dma_adb[dma].nr_ch = nr_ch;
+
+#if 0
+	/* AC97 Codec channel setup. FIXME: this has no effect on some cards !! */
+	if (nr_ch < 4) {
+		/* Copy stereo to rear channel (surround) */
+		snd_ac97_write_cache(vortex->codec,
+				     AC97_SIGMATEL_DAC2INVERT,
+				     snd_ac97_read(vortex->codec,
+						   AC97_SIGMATEL_DAC2INVERT)
+				     | 4);
+	} else {
+		/* Allow separate front and rear channels. */
+		snd_ac97_write_cache(vortex->codec,
+				     AC97_SIGMATEL_DAC2INVERT,
+				     snd_ac97_read(vortex->codec,
+						   AC97_SIGMATEL_DAC2INVERT)
+				     & ~((u32)
+					 4));
+	}
+#endif
+	return dma;
+}
+
+/*
+ Set the SampleRate of the SRC's attached to the given DMA engine.
+ */
+static void
+vortex_adb_setsrc(vortex_t * vortex, int adbdma, unsigned int rate, int dir)
+{
+	stream_t *stream = &(vortex->dma_adb[adbdma]);
+	int i, cvrt;
+
+	/* dir=1:play ; dir=0:rec */
+	if (dir)
+		cvrt = SRC_RATIO(rate, 48000);
+	else
+		cvrt = SRC_RATIO(48000, rate);
+
+	/* Setup SRC's */
+	for (i = 0; i < NR_SRC; i++) {
+		if (stream->resources[VORTEX_RESOURCE_SRC] & (1 << i))
+			vortex_src_setupchannel(vortex, i, cvrt, 0, 0, i, dir, 1, cvrt, dir);
+	}
+}
+
+// Timer and ISR functions.
+
+static void vortex_settimer(vortex_t * vortex, int period)
+{
+	//set the timer period to <period> 48000ths of a second.
+	hwwrite(vortex->mmio, VORTEX_IRQ_STAT, period);
+}
+
+#if 0
+static void vortex_enable_timer_int(vortex_t * card)
+{
+	hwwrite(card->mmio, VORTEX_IRQ_CTRL,
+		hwread(card->mmio, VORTEX_IRQ_CTRL) | IRQ_TIMER | 0x60);
+}
+
+static void vortex_disable_timer_int(vortex_t * card)
+{
+	hwwrite(card->mmio, VORTEX_IRQ_CTRL,
+		hwread(card->mmio, VORTEX_IRQ_CTRL) & ~IRQ_TIMER);
+}
+
+#endif
+static void vortex_enable_int(vortex_t * card)
+{
+	// CAsp4ISR__EnableVortexInt_void_
+	hwwrite(card->mmio, VORTEX_CTRL,
+		hwread(card->mmio, VORTEX_CTRL) | CTRL_IRQ_ENABLE);
+	hwwrite(card->mmio, VORTEX_IRQ_CTRL,
+		(hwread(card->mmio, VORTEX_IRQ_CTRL) & 0xffffefc0) | 0x24);
+}
+
+static void vortex_disable_int(vortex_t * card)
+{
+	hwwrite(card->mmio, VORTEX_CTRL,
+		hwread(card->mmio, VORTEX_CTRL) & ~CTRL_IRQ_ENABLE);
+}
+
+static irqreturn_t vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	vortex_t *vortex = dev_id;
+	int i, handled;
+	u32 source;
+
+	//check if the interrupt is ours.
+	if (!(hwread(vortex->mmio, VORTEX_STAT) & 0x1))
+		return IRQ_NONE;
+
+	// This is the Interrrupt Enable flag we set before (consistency check).
+	if (!(hwread(vortex->mmio, VORTEX_CTRL) & CTRL_IRQ_ENABLE))
+		return IRQ_NONE;
+
+	source = hwread(vortex->mmio, VORTEX_IRQ_SOURCE);
+	// Reset IRQ flags.
+	hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, source);
+	hwread(vortex->mmio, VORTEX_IRQ_SOURCE);
+	// Is at least one IRQ flag set?
+	if (source == 0) {
+		printk(KERN_ERR "vortex: missing irq source\n");
+		return IRQ_NONE;
+	}
+
+	handled = 0;
+	// Attend every interrupt source.
+	if (unlikely(source & IRQ_ERR_MASK)) {
+		if (source & IRQ_FATAL) {
+			printk(KERN_ERR "vortex: IRQ fatal error\n");
+		}
+		if (source & IRQ_PARITY) {
+			printk(KERN_ERR "vortex: IRQ parity error\n");
+		}
+		if (source & IRQ_REG) {
+			printk(KERN_ERR "vortex: IRQ reg error\n");
+		}
+		if (source & IRQ_FIFO) {
+			printk(KERN_ERR "vortex: IRQ fifo error\n");
+		}
+		if (source & IRQ_DMA) {
+			printk(KERN_ERR "vortex: IRQ dma error\n");
+		}
+		handled = 1;
+	}
+	if (source & IRQ_PCMOUT) {
+		/* ALSA period acknowledge. */
+		spin_lock(&vortex->lock);
+		for (i = 0; i < NR_ADB; i++) {
+			if (vortex->dma_adb[i].fifo_status == FIFO_START) {
+				if (vortex_adbdma_bufshift(vortex, i)) ;
+				spin_unlock(&vortex->lock);
+				snd_pcm_period_elapsed(vortex->dma_adb[i].
+						       substream);
+				spin_lock(&vortex->lock);
+			}
+		}
+#ifndef CHIP_AU8810
+		for (i = 0; i < NR_WT; i++) {
+			if (vortex->dma_wt[i].fifo_status == FIFO_START) {
+				if (vortex_wtdma_bufshift(vortex, i)) ;
+				spin_unlock(&vortex->lock);
+				snd_pcm_period_elapsed(vortex->dma_wt[i].
+						       substream);
+				spin_lock(&vortex->lock);
+			}
+		}
+#endif
+		spin_unlock(&vortex->lock);
+		handled = 1;
+	}
+	//Acknowledge the Timer interrupt
+	if (source & IRQ_TIMER) {
+		hwread(vortex->mmio, VORTEX_IRQ_STAT);
+		handled = 1;
+	}
+	if (source & IRQ_MIDI) {
+		snd_mpu401_uart_interrupt(vortex->irq,
+					  vortex->rmidi->private_data, regs);
+		handled = 1;
+	}
+
+	if (!handled) {
+		printk(KERN_ERR "vortex: unknown irq source %x\n", source);
+	}
+	return IRQ_RETVAL(handled);
+}
+
+/* Codec */
+
+#define POLL_COUNT 1000
+static void vortex_codec_init(vortex_t * vortex)
+{
+	int i;
+
+	for (i = 0; i < 32; i++) {
+		/* the windows driver writes -i, so we write -i */
+		hwwrite(vortex->mmio, (VORTEX_CODEC_CHN + (i << 2)), -i);
+		msleep(2);
+	}
+	if (0) {
+		hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x8068);
+		msleep(1);
+		hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00e8);
+		msleep(1);
+	} else {
+		hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00a8);
+		msleep(2);
+		hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x80a8);
+		msleep(2);
+		hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x80e8);
+		msleep(2);
+		hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x80a8);
+		msleep(2);
+		hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00a8);
+		msleep(2);
+		hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00e8);
+	}
+	for (i = 0; i < 32; i++) {
+		hwwrite(vortex->mmio, (VORTEX_CODEC_CHN + (i << 2)), -i);
+		msleep(5);
+	}
+	hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0xe8);
+	msleep(1);
+	/* Enable codec channels 0 and 1. */
+	hwwrite(vortex->mmio, VORTEX_CODEC_EN,
+		hwread(vortex->mmio, VORTEX_CODEC_EN) | EN_CODEC);
+}
+
+static void
+vortex_codec_write(ac97_t * codec, unsigned short addr, unsigned short data)
+{
+
+	vortex_t *card = (vortex_t *) codec->private_data;
+	unsigned int lifeboat = 0;
+
+	/* wait for transactions to clear */
+	while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
+		udelay(100);
+		if (lifeboat++ > POLL_COUNT) {
+			printk(KERN_ERR "vortex: ac97 codec stuck busy\n");
+			return;
+		}
+	}
+	/* write register */
+	hwwrite(card->mmio, VORTEX_CODEC_IO,
+		((addr << VORTEX_CODEC_ADDSHIFT) & VORTEX_CODEC_ADDMASK) |
+		((data << VORTEX_CODEC_DATSHIFT) & VORTEX_CODEC_DATMASK) |
+		VORTEX_CODEC_WRITE);
+
+	/* Flush Caches. */
+	hwread(card->mmio, VORTEX_CODEC_IO);
+}
+
+static unsigned short vortex_codec_read(ac97_t * codec, unsigned short addr)
+{
+
+	vortex_t *card = (vortex_t *) codec->private_data;
+	u32 read_addr, data;
+	unsigned lifeboat = 0;
+
+	/* wait for transactions to clear */
+	while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
+		udelay(100);
+		if (lifeboat++ > POLL_COUNT) {
+			printk(KERN_ERR "vortex: ac97 codec stuck busy\n");
+			return 0xffff;
+		}
+	}
+	/* set up read address */
+	read_addr = ((addr << VORTEX_CODEC_ADDSHIFT) & VORTEX_CODEC_ADDMASK);
+	hwwrite(card->mmio, VORTEX_CODEC_IO, read_addr);
+
+	/* wait for address */
+	do {
+		udelay(100);
+		data = hwread(card->mmio, VORTEX_CODEC_IO);
+		if (lifeboat++ > POLL_COUNT) {
+			printk(KERN_ERR "vortex: ac97 address never arrived\n");
+			return 0xffff;
+		}
+	} while ((data & VORTEX_CODEC_ADDMASK) !=
+		 (addr << VORTEX_CODEC_ADDSHIFT));
+
+	/* return data. */
+	return (u16) (data & VORTEX_CODEC_DATMASK);
+}
+
+/* SPDIF support  */
+
+static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode)
+{
+	int i, this_38 = 0, this_04 = 0, this_08 = 0, this_0c = 0;
+
+	/* CAsp4Spdif::InitializeSpdifHardware(void) */
+	hwwrite(vortex->mmio, VORTEX_SPDIF_FLAGS,
+		hwread(vortex->mmio, VORTEX_SPDIF_FLAGS) & 0xfff3fffd);
+	//for (i=0x291D4; i<0x29200; i+=4)
+	for (i = 0; i < 11; i++)
+		hwwrite(vortex->mmio, VORTEX_SPDIF_CFG1 + (i << 2), 0);
+	//hwwrite(vortex->mmio, 0x29190, hwread(vortex->mmio, 0x29190) | 0xc0000);
+	hwwrite(vortex->mmio, VORTEX_CODEC_EN,
+		hwread(vortex->mmio, VORTEX_CODEC_EN) | EN_SPDIF);
+
+	/* CAsp4Spdif::ProgramSRCInHardware(enum  SPDIF_SR,enum  SPDIFMODE) */
+	if (this_04 && this_08) {
+		int edi;
+
+		i = (((0x5DC00000 / spdif_sr) + 1) >> 1);
+		if (i > 0x800) {
+			if (i < 0x1ffff)
+				edi = (i >> 1);
+			else
+				edi = 0x1ffff;
+		} else {
+			i = edi = 0x800;
+		}
+		/* this_04 and this_08 are the CASp4Src's (samplerate converters) */
+		vortex_src_setupchannel(vortex, this_04, edi, 0, 1,
+					this_0c, 1, 0, edi, 1);
+		vortex_src_setupchannel(vortex, this_08, edi, 0, 1,
+					this_0c, 1, 0, edi, 1);
+	}
+
+	i = spdif_sr;
+	spdif_sr |= 0x8c;
+	switch (i) {
+	case 32000:
+		this_38 &= 0xFFFFFFFE;
+		this_38 &= 0xFFFFFFFD;
+		this_38 &= 0xF3FFFFFF;
+		this_38 |= 0x03000000;	/* set 32khz samplerate */
+		this_38 &= 0xFFFFFF3F;
+		spdif_sr &= 0xFFFFFFFD;
+		spdif_sr |= 1;
+		break;
+	case 44100:
+		this_38 &= 0xFFFFFFFE;
+		this_38 &= 0xFFFFFFFD;
+		this_38 &= 0xF0FFFFFF;
+		this_38 |= 0x03000000;
+		this_38 &= 0xFFFFFF3F;
+		spdif_sr &= 0xFFFFFFFC;
+		break;
+	case 48000:
+		if (spdif_mode == 1) {
+			this_38 &= 0xFFFFFFFE;
+			this_38 &= 0xFFFFFFFD;
+			this_38 &= 0xF2FFFFFF;
+			this_38 |= 0x02000000;	/* set 48khz samplerate */
+			this_38 &= 0xFFFFFF3F;
+		} else {
+			/* J. Gordon Wolfe: I think this stuff is for AC3 */
+			this_38 |= 0x00000003;
+			this_38 &= 0xFFFFFFBF;
+			this_38 |= 0x80;
+		}
+		spdif_sr |= 2;
+		spdif_sr &= 0xFFFFFFFE;
+		break;
+
+	}
+	/* looks like the next 2 lines transfer a 16-bit value into 2 8-bit 
+	   registers. seems to be for the standard IEC/SPDIF initialization 
+	   stuff */
+	hwwrite(vortex->mmio, VORTEX_SPDIF_CFG0, this_38 & 0xffff);
+	hwwrite(vortex->mmio, VORTEX_SPDIF_CFG1, this_38 >> 0x10);
+	hwwrite(vortex->mmio, VORTEX_SPDIF_SMPRATE, spdif_sr);
+}
+
+/* Initialization */
+
+static int vortex_core_init(vortex_t * vortex)
+{
+
+	printk(KERN_INFO "Vortex: init.... ");
+	/* Hardware Init. */
+	hwwrite(vortex->mmio, VORTEX_CTRL, 0xffffffff);
+	msleep(5);
+	hwwrite(vortex->mmio, VORTEX_CTRL,
+		hwread(vortex->mmio, VORTEX_CTRL) & 0xffdfffff);
+	msleep(5);
+	/* Reset IRQ flags */
+	hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffffffff);
+	hwread(vortex->mmio, VORTEX_IRQ_STAT);
+
+	vortex_codec_init(vortex);
+
+#ifdef CHIP_AU8830
+	hwwrite(vortex->mmio, VORTEX_CTRL,
+		hwread(vortex->mmio, VORTEX_CTRL) | 0x1000000);
+#endif
+
+	/* Init audio engine. */
+	vortex_adbdma_init(vortex);
+	hwwrite(vortex->mmio, VORTEX_ENGINE_CTRL, 0x0);	//, 0xc83c7e58, 0xc5f93e58
+	vortex_adb_init(vortex);
+	/* Init processing blocks. */
+	vortex_fifo_init(vortex);
+	vortex_mixer_init(vortex);
+	vortex_srcblock_init(vortex);
+#ifndef CHIP_AU8820
+	vortex_eq_init(vortex);
+	vortex_spdif_init(vortex, 48000, 1);
+	vortex_Vort3D(vortex, 1);
+#endif
+#ifndef CHIP_AU8810
+	vortex_wt_init(vortex);
+#endif
+	// Moved to au88x0.c
+	//vortex_connect_default(vortex, 1);
+
+	vortex_settimer(vortex, 0x90);
+	// Enable Interrupts.
+	// vortex_enable_int() must be first !!
+	//  hwwrite(vortex->mmio, VORTEX_IRQ_CTRL, 0);
+	// vortex_enable_int(vortex);
+	//vortex_enable_timer_int(vortex);
+	//vortex_disable_timer_int(vortex);
+
+	printk(KERN_INFO "done.\n");
+	spin_lock_init(&vortex->lock);
+
+	return 0;
+}
+
+static int vortex_core_shutdown(vortex_t * vortex)
+{
+
+	printk(KERN_INFO "Vortex: shutdown...");
+#ifndef CHIP_AU8820
+	vortex_eq_free(vortex);
+	vortex_Vort3D(vortex, 0);
+#endif
+	//vortex_disable_timer_int(vortex);
+	vortex_disable_int(vortex);
+	vortex_connect_default(vortex, 0);
+	/* Reset all DMA fifos. */
+	vortex_fifo_init(vortex);
+	/* Erase all audio routes. */
+	vortex_adb_init(vortex);
+
+	/* Disable MPU401 */
+	//hwwrite(vortex->mmio, VORTEX_IRQ_CTRL, hwread(vortex->mmio, VORTEX_IRQ_CTRL) & ~IRQ_MIDI);
+	//hwwrite(vortex->mmio, VORTEX_CTRL, hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_EN);
+
+	hwwrite(vortex->mmio, VORTEX_IRQ_CTRL, 0);
+	hwwrite(vortex->mmio, VORTEX_CTRL, 0);
+	msleep(5);
+	hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffff);
+
+	printk(KERN_INFO "done.\n");
+	return 0;
+}
+
+/* Alsa support. */
+
+static int vortex_alsafmt_aspfmt(int alsafmt)
+{
+	int fmt;
+
+	switch (alsafmt) {
+	case SNDRV_PCM_FORMAT_U8:
+		fmt = 0x1;
+		break;
+	case SNDRV_PCM_FORMAT_MU_LAW:
+		fmt = 0x2;
+		break;
+	case SNDRV_PCM_FORMAT_A_LAW:
+		fmt = 0x3;
+		break;
+	case SNDRV_PCM_FORMAT_SPECIAL:
+		fmt = 0x4;	/* guess. */
+		break;
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		fmt = 0x5;	/* guess. */
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		fmt = 0x8;
+		break;
+	case SNDRV_PCM_FORMAT_S16_BE:
+		fmt = 0x9;	/* check this... */
+		break;
+	default:
+		fmt = 0x8;
+		printk(KERN_ERR "vortex: format unsupported %d\n", alsafmt);
+		break;
+	}
+	return fmt;
+}
+
+/* Some not yet useful translations. */
+#if 0
+typedef enum {
+	ASPFMTLINEAR16 = 0,	/* 0x8 */
+	ASPFMTLINEAR8,		/* 0x1 */
+	ASPFMTULAW,		/* 0x2 */
+	ASPFMTALAW,		/* 0x3 */
+	ASPFMTSPORT,		/* ? */
+	ASPFMTSPDIF,		/* ? */
+} ASPENCODING;
+
+static int
+vortex_translateformat(vortex_t * vortex, char bits, char nch, int encod)
+{
+	int a, this_194;
+
+	if ((bits != 8) || (bits != 16))
+		return -1;
+
+	switch (encod) {
+	case 0:
+		if (bits == 0x10)
+			a = 8;	// 16 bit
+		break;
+	case 1:
+		if (bits == 8)
+			a = 1;	// 8 bit
+		break;
+	case 2:
+		a = 2;		// U_LAW
+		break;
+	case 3:
+		a = 3;		// A_LAW
+		break;
+	}
+	switch (nch) {
+	case 1:
+		this_194 = 0;
+		break;
+	case 2:
+		this_194 = 1;
+		break;
+	case 4:
+		this_194 = 1;
+		break;
+	case 6:
+		this_194 = 1;
+		break;
+	}
+	return (a);
+}
+
+static void vortex_cdmacore_setformat(vortex_t * vortex, int bits, int nch)
+{
+	short int d, this_148;
+
+	d = ((bits >> 3) * nch);
+	this_148 = 0xbb80 / d;
+}
+#endif
diff --git a/sound/pci/au88x0/au88x0_eq.c b/sound/pci/au88x0/au88x0_eq.c
new file mode 100644
index 0000000..53b47a4
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_eq.c
@@ -0,0 +1,937 @@
+/***************************************************************************
+ *            au88x0_eq.c
+ *  Aureal Vortex Hardware EQ control/access.
+ *
+ *  Sun Jun  8 18:19:19 2003
+ *  2003  Manuel Jander (mjander@users.sourceforge.net)
+ *  
+ *  02 July 2003: First time something works :)
+ *  November 2003: A3D Bypass code completed but untested.
+ *
+ *  TODO:
+ *     - Debug (testing)
+ *     - Test peak visualization support.
+ *
+ ****************************************************************************/
+
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ The Aureal Hardware EQ is found on AU8810 and AU8830 chips only.
+ it has 4 inputs (2 for general mix, 2 for A3D) and 2 outputs (supposed 
+ to be routed to the codec).
+*/
+
+#include "au88x0.h"
+#include "au88x0_eq.h"
+#include "au88x0_eqdata.c"
+
+#define VORTEX_EQ_BASE	 0x2b000
+#define VORTEX_EQ_DEST   (VORTEX_EQ_BASE + 0x410)
+#define VORTEX_EQ_SOURCE (VORTEX_EQ_BASE + 0x430)
+#define VORTEX_EQ_CTRL   (VORTEX_EQ_BASE + 0x440)
+
+#define VORTEX_BAND_COEFF_SIZE 0x30
+
+/* CEqHw.s */
+static void vortex_EqHw_SetTimeConsts(vortex_t * vortex, u16 gain, u16 level)
+{
+	hwwrite(vortex->mmio, 0x2b3c4, gain);
+	hwwrite(vortex->mmio, 0x2b3c8, level);
+}
+
+static inline u16 sign_invert(u16 a)
+{
+	/* -(-32768) -> -32768 so we do -(-32768) -> 32767 to make the result positive */
+	if (a == (u16)-32768)
+		return 32767;
+	else
+		return -a;
+}
+
+static void vortex_EqHw_SetLeftCoefs(vortex_t * vortex, u16 coefs[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int i = 0, n /*esp2c */;
+
+	for (n = 0; n < eqhw->this04; n++) {
+		hwwrite(vortex->mmio, 0x2b000 + n * 0x30, coefs[i + 0]);
+		hwwrite(vortex->mmio, 0x2b004 + n * 0x30, coefs[i + 1]);
+
+		if (eqhw->this08 == 0) {
+			hwwrite(vortex->mmio, 0x2b008 + n * 0x30, coefs[i + 2]);
+			hwwrite(vortex->mmio, 0x2b00c + n * 0x30, coefs[i + 3]);
+			hwwrite(vortex->mmio, 0x2b010 + n * 0x30, coefs[i + 4]);
+		} else {
+			hwwrite(vortex->mmio, 0x2b008 + n * 0x30, sign_invert(coefs[2 + i]));
+			hwwrite(vortex->mmio, 0x2b00c + n * 0x30, sign_invert(coefs[3 + i]));
+		        hwwrite(vortex->mmio, 0x2b010 + n * 0x30, sign_invert(coefs[4 + i]));
+		}
+		i += 5;
+	}
+}
+
+static void vortex_EqHw_SetRightCoefs(vortex_t * vortex, u16 coefs[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int i = 0, n /*esp2c */;
+
+	for (n = 0; n < eqhw->this04; n++) {
+		hwwrite(vortex->mmio, 0x2b1e0 + n * 0x30, coefs[0 + i]);
+		hwwrite(vortex->mmio, 0x2b1e4 + n * 0x30, coefs[1 + i]);
+
+		if (eqhw->this08 == 0) {
+			hwwrite(vortex->mmio, 0x2b1e8 + n * 0x30, coefs[2 + i]);
+			hwwrite(vortex->mmio, 0x2b1ec + n * 0x30, coefs[3 + i]);
+			hwwrite(vortex->mmio, 0x2b1f0 + n * 0x30, coefs[4 + i]);
+		} else {
+			hwwrite(vortex->mmio, 0x2b1e8 + n * 0x30, sign_invert(coefs[2 + i]));
+			hwwrite(vortex->mmio, 0x2b1ec + n * 0x30, sign_invert(coefs[3 + i]));
+			hwwrite(vortex->mmio, 0x2b1f0 + n * 0x30, sign_invert(coefs[4 + i]));
+		}
+		i += 5;
+	}
+
+}
+
+static void vortex_EqHw_SetLeftStates(vortex_t * vortex, u16 a[], u16 b[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int i = 0, ebx;
+
+	hwwrite(vortex->mmio, 0x2b3fc, a[0]);
+	hwwrite(vortex->mmio, 0x2b400, a[1]);
+
+	for (ebx = 0; ebx < eqhw->this04; ebx++) {
+		hwwrite(vortex->mmio, 0x2b014 + (i * 0xc), b[i]);
+		hwwrite(vortex->mmio, 0x2b018 + (i * 0xc), b[1 + i]);
+		hwwrite(vortex->mmio, 0x2b01c + (i * 0xc), b[2 + i]);
+		hwwrite(vortex->mmio, 0x2b020 + (i * 0xc), b[3 + i]);
+		i += 4;
+	}
+}
+
+static void vortex_EqHw_SetRightStates(vortex_t * vortex, u16 a[], u16 b[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int i = 0, ebx;
+
+	hwwrite(vortex->mmio, 0x2b404, a[0]);
+	hwwrite(vortex->mmio, 0x2b408, a[1]);
+
+	for (ebx = 0; ebx < eqhw->this04; ebx++) {
+		hwwrite(vortex->mmio, 0x2b1f4 + (i * 0xc), b[i]);
+		hwwrite(vortex->mmio, 0x2b1f8 + (i * 0xc), b[1 + i]);
+		hwwrite(vortex->mmio, 0x2b1fc + (i * 0xc), b[2 + i]);
+		hwwrite(vortex->mmio, 0x2b200 + (i * 0xc), b[3 + i]);
+		i += 4;
+	}
+}
+
+#if 0
+static void vortex_EqHw_GetTimeConsts(vortex_t * vortex, u16 * a, u16 * b)
+{
+	*a = hwread(vortex->mmio, 0x2b3c4);
+	*b = hwread(vortex->mmio, 0x2b3c8);
+}
+
+static void vortex_EqHw_GetLeftCoefs(vortex_t * vortex, u16 a[])
+{
+
+}
+
+static void vortex_EqHw_GetRightCoefs(vortex_t * vortex, u16 a[])
+{
+
+}
+
+static void vortex_EqHw_GetLeftStates(vortex_t * vortex, u16 * a, u16 b[])
+{
+
+}
+
+static void vortex_EqHw_GetRightStates(vortex_t * vortex, u16 * a, u16 b[])
+{
+
+}
+
+#endif
+/* Mix Gains */
+static void vortex_EqHw_SetBypassGain(vortex_t * vortex, u16 a, u16 b)
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	if (eqhw->this08 == 0) {
+		hwwrite(vortex->mmio, 0x2b3d4, a);
+		hwwrite(vortex->mmio, 0x2b3ec, b);
+	} else {
+		hwwrite(vortex->mmio, 0x2b3d4, sign_invert(a));
+		hwwrite(vortex->mmio, 0x2b3ec, sign_invert(b));
+	}
+}
+
+static void vortex_EqHw_SetA3DBypassGain(vortex_t * vortex, u16 a, u16 b)
+{
+
+	hwwrite(vortex->mmio, 0x2b3e0, a);
+	hwwrite(vortex->mmio, 0x2b3f8, b);
+}
+
+#if 0
+static void vortex_EqHw_SetCurrBypassGain(vortex_t * vortex, u16 a, u16 b)
+{
+
+	hwwrite(vortex->mmio, 0x2b3d0, a);
+	hwwrite(vortex->mmio, 0x2b3e8, b);
+}
+
+static void vortex_EqHw_SetCurrA3DBypassGain(vortex_t * vortex, u16 a, u16 b)
+{
+
+	hwwrite(vortex->mmio, 0x2b3dc, a);
+	hwwrite(vortex->mmio, 0x2b3f4, b);
+}
+
+#endif
+static void
+vortex_EqHw_SetLeftGainsSingleTarget(vortex_t * vortex, u16 index, u16 b)
+{
+	hwwrite(vortex->mmio, 0x2b02c + (index * 0x30), b);
+}
+
+static void
+vortex_EqHw_SetRightGainsSingleTarget(vortex_t * vortex, u16 index, u16 b)
+{
+	hwwrite(vortex->mmio, 0x2b20c + (index * 0x30), b);
+}
+
+static void vortex_EqHw_SetLeftGainsTarget(vortex_t * vortex, u16 a[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx;
+
+	for (ebx = 0; ebx < eqhw->this04; ebx++) {
+		hwwrite(vortex->mmio, 0x2b02c + ebx * 0x30, a[ebx]);
+	}
+}
+
+static void vortex_EqHw_SetRightGainsTarget(vortex_t * vortex, u16 a[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx;
+
+	for (ebx = 0; ebx < eqhw->this04; ebx++) {
+		hwwrite(vortex->mmio, 0x2b20c + ebx * 0x30, a[ebx]);
+	}
+}
+
+static void vortex_EqHw_SetLeftGainsCurrent(vortex_t * vortex, u16 a[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx;
+
+	for (ebx = 0; ebx < eqhw->this04; ebx++) {
+		hwwrite(vortex->mmio, 0x2b028 + ebx * 0x30, a[ebx]);
+	}
+}
+
+static void vortex_EqHw_SetRightGainsCurrent(vortex_t * vortex, u16 a[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx;
+
+	for (ebx = 0; ebx < eqhw->this04; ebx++) {
+		hwwrite(vortex->mmio, 0x2b208 + ebx * 0x30, a[ebx]);
+	}
+}
+
+#if 0
+static void vortex_EqHw_GetLeftGainsTarget(vortex_t * vortex, u16 a[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx = 0;
+
+	if (eqhw->this04 < 0)
+		return;
+
+	do {
+		a[ebx] = hwread(vortex->mmio, 0x2b02c + ebx * 0x30);
+		ebx++;
+	}
+	while (ebx < eqhw->this04);
+}
+
+static void vortex_EqHw_GetRightGainsTarget(vortex_t * vortex, u16 a[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx = 0;
+
+	if (eqhw->this04 < 0)
+		return;
+
+	do {
+		a[ebx] = hwread(vortex->mmio, 0x2b20c + ebx * 0x30);
+		ebx++;
+	}
+	while (ebx < eqhw->this04);
+}
+
+static void vortex_EqHw_GetLeftGainsCurrent(vortex_t * vortex, u16 a[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx = 0;
+
+	if (eqhw->this04 < 0)
+		return;
+
+	do {
+		a[ebx] = hwread(vortex->mmio, 0x2b028 + ebx * 0x30);
+		ebx++;
+	}
+	while (ebx < eqhw->this04);
+}
+
+static void vortex_EqHw_GetRightGainsCurrent(vortex_t * vortex, u16 a[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx = 0;
+
+	if (eqhw->this04 < 0)
+		return;
+
+	do {
+		a[ebx] = hwread(vortex->mmio, 0x2b208 + ebx * 0x30);
+		ebx++;
+	}
+	while (ebx < eqhw->this04);
+}
+
+#endif
+/* EQ band levels settings */
+static void vortex_EqHw_SetLevels(vortex_t * vortex, u16 peaks[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int i;
+
+	/* set left peaks */
+	for (i = 0; i < eqhw->this04; i++) {
+		hwwrite(vortex->mmio, 0x2b024 + i * VORTEX_BAND_COEFF_SIZE, peaks[i]);
+	}
+
+	hwwrite(vortex->mmio, 0x2b3cc, peaks[eqhw->this04]);
+	hwwrite(vortex->mmio, 0x2b3d8, peaks[eqhw->this04 + 1]);
+
+	/* set right peaks */
+	for (i = 0; i < eqhw->this04; i++) {
+		hwwrite(vortex->mmio, 0x2b204 + i * VORTEX_BAND_COEFF_SIZE,
+			peaks[i + (eqhw->this04 + 2)]);
+	}
+
+	hwwrite(vortex->mmio, 0x2b3e4, peaks[2 + (eqhw->this04 * 2)]);
+	hwwrite(vortex->mmio, 0x2b3f0, peaks[3 + (eqhw->this04 * 2)]);
+}
+
+#if 0
+static void vortex_EqHw_GetLevels(vortex_t * vortex, u16 a[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx;
+
+	if (eqhw->this04 < 0)
+		return;
+
+	ebx = 0;
+	do {
+		a[ebx] = hwread(vortex->mmio, 0x2b024 + ebx * 0x30);
+		ebx++;
+	}
+	while (ebx < eqhw->this04);
+
+	a[eqhw->this04] = hwread(vortex->mmio, 0x2b3cc);
+	a[eqhw->this04 + 1] = hwread(vortex->mmio, 0x2b3d8);
+
+	ebx = 0;
+	do {
+		a[ebx + (eqhw->this04 + 2)] =
+		    hwread(vortex->mmio, 0x2b204 + ebx * 0x30);
+		ebx++;
+	}
+	while (ebx < eqhw->this04);
+
+	a[2 + (eqhw->this04 * 2)] = hwread(vortex->mmio, 0x2b3e4);
+	a[3 + (eqhw->this04 * 2)] = hwread(vortex->mmio, 0x2b3f0);
+}
+
+#endif
+/* Global Control */
+static void vortex_EqHw_SetControlReg(vortex_t * vortex, unsigned long reg)
+{
+	hwwrite(vortex->mmio, 0x2b440, reg);
+}
+
+static void vortex_EqHw_SetSampleRate(vortex_t * vortex, int sr)
+{
+	hwwrite(vortex->mmio, 0x2b440, ((sr & 0x1f) << 3) | 0xb800);
+}
+
+#if 0
+static void vortex_EqHw_GetControlReg(vortex_t * vortex, unsigned long *reg)
+{
+	*reg = hwread(vortex->mmio, 0x2b440);
+}
+
+static void vortex_EqHw_GetSampleRate(vortex_t * vortex, int *sr)
+{
+	*sr = (hwread(vortex->mmio, 0x2b440) >> 3) & 0x1f;
+}
+
+#endif
+static void vortex_EqHw_Enable(vortex_t * vortex)
+{
+	hwwrite(vortex->mmio, VORTEX_EQ_CTRL, 0xf001);
+}
+
+static void vortex_EqHw_Disable(vortex_t * vortex)
+{
+	hwwrite(vortex->mmio, VORTEX_EQ_CTRL, 0xf000);
+}
+
+/* Reset (zero) buffers */
+static void vortex_EqHw_ZeroIO(vortex_t * vortex)
+{
+	int i;
+	for (i = 0; i < 0x8; i++)
+		hwwrite(vortex->mmio, VORTEX_EQ_DEST + (i << 2), 0x0);
+	for (i = 0; i < 0x4; i++)
+		hwwrite(vortex->mmio, VORTEX_EQ_SOURCE + (i << 2), 0x0);
+}
+
+static void vortex_EqHw_ZeroA3DIO(vortex_t * vortex)
+{
+	int i;
+	for (i = 0; i < 0x4; i++)
+		hwwrite(vortex->mmio, VORTEX_EQ_DEST + (i << 2), 0x0);
+}
+
+static void vortex_EqHw_ZeroState(vortex_t * vortex)
+{
+
+	vortex_EqHw_SetControlReg(vortex, 0);
+	vortex_EqHw_ZeroIO(vortex);
+	hwwrite(vortex->mmio, 0x2b3c0, 0);
+
+	vortex_EqHw_SetTimeConsts(vortex, 0, 0);
+
+	vortex_EqHw_SetLeftCoefs(vortex, asEqCoefsZeros);
+	vortex_EqHw_SetRightCoefs(vortex, asEqCoefsZeros);
+
+	vortex_EqHw_SetLeftGainsCurrent(vortex, eq_gains_zero);
+	vortex_EqHw_SetRightGainsCurrent(vortex, eq_gains_zero);
+	vortex_EqHw_SetLeftGainsTarget(vortex, eq_gains_zero);
+	vortex_EqHw_SetRightGainsTarget(vortex, eq_gains_zero);
+
+	vortex_EqHw_SetBypassGain(vortex, 0, 0);
+	//vortex_EqHw_SetCurrBypassGain(vortex, 0, 0);
+	vortex_EqHw_SetA3DBypassGain(vortex, 0, 0);
+	//vortex_EqHw_SetCurrA3DBypassGain(vortex, 0, 0);
+	vortex_EqHw_SetLeftStates(vortex, eq_states_zero, asEqOutStateZeros);
+	vortex_EqHw_SetRightStates(vortex, eq_states_zero, asEqOutStateZeros);
+	vortex_EqHw_SetLevels(vortex, (u16 *) eq_levels);
+}
+
+/* Program coeficients as pass through */
+static void vortex_EqHw_ProgramPipe(vortex_t * vortex)
+{
+	vortex_EqHw_SetTimeConsts(vortex, 0, 0);
+
+	vortex_EqHw_SetLeftCoefs(vortex, asEqCoefsPipes);
+	vortex_EqHw_SetRightCoefs(vortex, asEqCoefsPipes);
+
+	vortex_EqHw_SetLeftGainsCurrent(vortex, eq_gains_current);
+	vortex_EqHw_SetRightGainsCurrent(vortex, eq_gains_current);
+	vortex_EqHw_SetLeftGainsTarget(vortex, eq_gains_current);
+	vortex_EqHw_SetRightGainsTarget(vortex, eq_gains_current);
+}
+
+/* Program EQ block as 10 band Equalizer */
+static void
+vortex_EqHw_Program10Band(vortex_t * vortex, auxxEqCoeffSet_t * coefset)
+{
+
+	vortex_EqHw_SetTimeConsts(vortex, 0xc, 0x7fe0);
+
+	vortex_EqHw_SetLeftCoefs(vortex, coefset->LeftCoefs);
+	vortex_EqHw_SetRightCoefs(vortex, coefset->RightCoefs);
+
+	vortex_EqHw_SetLeftGainsCurrent(vortex, coefset->LeftGains);
+
+	vortex_EqHw_SetRightGainsTarget(vortex, coefset->RightGains);
+	vortex_EqHw_SetLeftGainsTarget(vortex, coefset->LeftGains);
+
+	vortex_EqHw_SetRightGainsCurrent(vortex, coefset->RightGains);
+}
+
+/* Read all EQ peaks. (think VU meter) */
+static void vortex_EqHw_GetTenBandLevels(vortex_t * vortex, u16 peaks[])
+{
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int i;
+
+	if (eqhw->this04 <= 0)
+		return;
+
+	for (i = 0; i < eqhw->this04; i++)
+		peaks[i] = hwread(vortex->mmio, 0x2B024 + i * 0x30);
+	for (i = 0; i < eqhw->this04; i++)
+		peaks[i + eqhw->this04] =
+		    hwread(vortex->mmio, 0x2B204 + i * 0x30);
+}
+
+/* CEqlzr.s */
+
+static int vortex_Eqlzr_GetLeftGain(vortex_t * vortex, u16 index, u16 * gain)
+{
+	eqlzr_t *eq = &(vortex->eq);
+
+	if (eq->this28) {
+		*gain = eq->this130[index];
+		return 0;
+	}
+	return 1;
+}
+
+static void vortex_Eqlzr_SetLeftGain(vortex_t * vortex, u16 index, u16 gain)
+{
+	eqlzr_t *eq = &(vortex->eq);
+
+	if (eq->this28 == 0)
+		return;
+
+	eq->this130[index] = gain;
+	if (eq->this54)
+		return;
+
+	vortex_EqHw_SetLeftGainsSingleTarget(vortex, index, gain);
+}
+
+static int vortex_Eqlzr_GetRightGain(vortex_t * vortex, u16 index, u16 * gain)
+{
+	eqlzr_t *eq = &(vortex->eq);
+
+	if (eq->this28) {
+		*gain = eq->this130[index + eq->this10];
+		return 0;
+	}
+	return 1;
+}
+
+static void vortex_Eqlzr_SetRightGain(vortex_t * vortex, u16 index, u16 gain)
+{
+	eqlzr_t *eq = &(vortex->eq);
+
+	if (eq->this28 == 0)
+		return;
+
+	eq->this130[index + eq->this10] = gain;
+	if (eq->this54)
+		return;
+
+	vortex_EqHw_SetRightGainsSingleTarget(vortex, index, gain);
+}
+
+#if 0
+static int
+vortex_Eqlzr_GetAllBands(vortex_t * vortex, u16 * gains, unsigned long *cnt)
+{
+	eqlzr_t *eq = &(vortex->eq);
+	int si = 0;
+
+	if (eq->this10 == 0)
+		return 1;
+
+	{
+		if (vortex_Eqlzr_GetLeftGain(vortex, si, &gains[si]))
+			return 1;
+		if (vortex_Eqlzr_GetRightGain
+		    (vortex, si, &gains[si + eq->this10]))
+			return 1;
+		si++;
+	}
+	while (eq->this10 > si) ;
+	*cnt = si * 2;
+	return 0;
+}
+#endif
+static int vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex_t * vortex)
+{
+	eqlzr_t *eq = &(vortex->eq);
+
+	vortex_EqHw_SetLeftGainsTarget(vortex, eq->this130);
+	vortex_EqHw_SetRightGainsTarget(vortex, &(eq->this130[eq->this10]));
+
+	return 0;
+}
+
+static int
+vortex_Eqlzr_SetAllBands(vortex_t * vortex, u16 gains[], unsigned long count)
+{
+	eqlzr_t *eq = &(vortex->eq);
+	int i;
+
+	if (((eq->this10) * 2 != count) || (eq->this28 == 0))
+		return 1;
+
+	for (i = 0; i < count; i++) {
+		eq->this130[i] = gains[i];
+	}
+	
+	if (eq->this54)
+		return 0;
+	return vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex);
+}
+
+static void
+vortex_Eqlzr_SetA3dBypassGain(vortex_t * vortex, unsigned long a,
+			      unsigned long b)
+{
+	eqlzr_t *eq = &(vortex->eq);
+	int eax, ebx;
+
+	eq->this58 = a;
+	eq->this5c = b;
+	if (eq->this54)
+		eax = eq->this0e;
+	else
+		eax = eq->this0a;
+	ebx = (eax * eq->this58) >> 0x10;
+	eax = (eax * eq->this5c) >> 0x10;
+	vortex_EqHw_SetA3DBypassGain(vortex, ebx, eax);
+}
+
+static void vortex_Eqlzr_ProgramA3dBypassGain(vortex_t * vortex)
+{
+	eqlzr_t *eq = &(vortex->eq);
+	int eax, ebx;
+
+	if (eq->this54)
+		eax = eq->this0e;
+	else
+		eax = eq->this0a;
+	ebx = (eax * eq->this58) >> 0x10;
+	eax = (eax * eq->this5c) >> 0x10;
+	vortex_EqHw_SetA3DBypassGain(vortex, ebx, eax);
+}
+
+static void vortex_Eqlzr_ShutDownA3d(vortex_t * vortex)
+{
+	if (vortex != NULL)
+		vortex_EqHw_ZeroA3DIO(vortex);
+}
+
+static void vortex_Eqlzr_SetBypass(vortex_t * vortex, long bp)
+{
+	eqlzr_t *eq = &(vortex->eq);
+	
+	if ((eq->this28) && (bp == 0)) {
+		/* EQ enabled */
+		vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex);
+		vortex_EqHw_SetBypassGain(vortex, eq->this08, eq->this08);
+	} else {
+		/* EQ disabled. */
+		vortex_EqHw_SetLeftGainsTarget(vortex, (u16 *) (eq->this14));
+		vortex_EqHw_SetRightGainsTarget(vortex, (u16 *) (eq->this14));
+		vortex_EqHw_SetBypassGain(vortex, eq->this0c, eq->this0c);
+	}
+	vortex_Eqlzr_ProgramA3dBypassGain(vortex);
+}
+
+static void vortex_Eqlzr_ReadAndSetActiveCoefSet(vortex_t * vortex)
+{
+	eqlzr_t *eq = &(vortex->eq);
+
+	/* Set EQ BiQuad filter coeficients */
+	memcpy(&(eq->coefset), &asEqCoefsNormal, sizeof(auxxEqCoeffSet_t));
+	/* Set EQ Band gain levels and dump into hardware registers. */
+	vortex_Eqlzr_SetAllBands(vortex, eq_gains_normal, eq->this10 * 2);
+}
+
+static int vortex_Eqlzr_GetAllPeaks(vortex_t * vortex, u16 * peaks, int *count)
+{
+	eqlzr_t *eq = &(vortex->eq);
+
+	if (eq->this10 == 0)
+		return 1;
+	*count = eq->this10 * 2;
+	vortex_EqHw_GetTenBandLevels(vortex, peaks);
+	return 0;
+}
+
+#if 0
+static auxxEqCoeffSet_t *vortex_Eqlzr_GetActiveCoefSet(vortex_t * vortex)
+{
+	eqlzr_t *eq = &(vortex->eq);
+
+	return (&(eq->coefset));
+}
+#endif
+static void vortex_Eqlzr_init(vortex_t * vortex)
+{
+	eqlzr_t *eq = &(vortex->eq);
+
+	/* Object constructor */
+	//eq->this04 = 0;
+	eq->this08 = 0;		/* Bypass gain with EQ in use. */
+	eq->this0a = 0x5999;
+	eq->this0c = 0x5999;	/* Bypass gain with EQ disabled. */
+	eq->this0e = 0x5999;
+
+	eq->this10 = 0xa;	/* 10 eq frequency bands. */
+	eq->this04.this04 = eq->this10;
+	eq->this28 = 0x1;	/* if 1 => Allow read access to this130 (gains) */
+	eq->this54 = 0x0;	/* if 1 => Dont Allow access to hardware (gains) */
+	eq->this58 = 0xffff;
+	eq->this5c = 0xffff;
+
+	/* Set gains. */
+	memset(eq->this14, 0, 2 * 10);
+
+	/* Actual init. */
+	vortex_EqHw_ZeroState(vortex);
+	vortex_EqHw_SetSampleRate(vortex, 0x11);
+	vortex_Eqlzr_ReadAndSetActiveCoefSet(vortex);
+
+	vortex_EqHw_Program10Band(vortex, &(eq->coefset));
+	vortex_Eqlzr_SetBypass(vortex, eq->this54);
+	vortex_Eqlzr_SetA3dBypassGain(vortex, 0, 0);
+	vortex_EqHw_Enable(vortex);
+}
+
+static void vortex_Eqlzr_shutdown(vortex_t * vortex)
+{
+	vortex_Eqlzr_ShutDownA3d(vortex);
+	vortex_EqHw_ProgramPipe(vortex);
+	vortex_EqHw_Disable(vortex);
+}
+
+/* ALSA interface */
+
+/* Control interface */
+static int
+snd_vortex_eqtoggle_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int
+snd_vortex_eqtoggle_get(snd_kcontrol_t * kcontrol,
+			snd_ctl_elem_value_t * ucontrol)
+{
+	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+	eqlzr_t *eq = &(vortex->eq);
+	//int i = kcontrol->private_value;
+
+	ucontrol->value.integer.value[0] = eq->this54 ? 0 : 1;
+
+	return 0;
+}
+
+static int
+snd_vortex_eqtoggle_put(snd_kcontrol_t * kcontrol,
+			snd_ctl_elem_value_t * ucontrol)
+{
+	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+	eqlzr_t *eq = &(vortex->eq);
+	//int i = kcontrol->private_value;
+
+	eq->this54 = ucontrol->value.integer.value[0] ? 0 : 1;
+	vortex_Eqlzr_SetBypass(vortex, eq->this54);
+
+	return 1;		/* Allways changes */
+}
+
+static snd_kcontrol_new_t vortex_eqtoggle_kcontrol __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "EQ Enable",
+	.index = 0,
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value = 0,
+	.info = snd_vortex_eqtoggle_info,
+	.get = snd_vortex_eqtoggle_get,
+	.put = snd_vortex_eqtoggle_put
+};
+
+static int
+snd_vortex_eq_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0x0000;
+	uinfo->value.integer.max = 0x7fff;
+	return 0;
+}
+
+static int
+snd_vortex_eq_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+	int i = kcontrol->private_value;
+	u16 gainL, gainR;
+
+	vortex_Eqlzr_GetLeftGain(vortex, i, &gainL);
+	vortex_Eqlzr_GetRightGain(vortex, i, &gainR);
+	ucontrol->value.integer.value[0] = gainL;
+	ucontrol->value.integer.value[1] = gainR;
+	return 0;
+}
+
+static int
+snd_vortex_eq_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+	int changed = 0, i = kcontrol->private_value;
+	u16 gainL, gainR;
+
+	vortex_Eqlzr_GetLeftGain(vortex, i, &gainL);
+	vortex_Eqlzr_GetRightGain(vortex, i, &gainR);
+
+	if (gainL != ucontrol->value.integer.value[0]) {
+		vortex_Eqlzr_SetLeftGain(vortex, i,
+					 ucontrol->value.integer.value[0]);
+		changed = 1;
+	}
+	if (gainR != ucontrol->value.integer.value[1]) {
+		vortex_Eqlzr_SetRightGain(vortex, i,
+					  ucontrol->value.integer.value[1]);
+		changed = 1;
+	}
+	return changed;
+}
+
+static snd_kcontrol_new_t vortex_eq_kcontrol __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "                        .",
+	.index = 0,
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value = 0,
+	.info = snd_vortex_eq_info,
+	.get = snd_vortex_eq_get,
+	.put = snd_vortex_eq_put
+};
+
+static int
+snd_vortex_peaks_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 20;
+	uinfo->value.integer.min = 0x0000;
+	uinfo->value.integer.max = 0x7fff;
+	return 0;
+}
+
+static int
+snd_vortex_peaks_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+	int i, count;
+	u16 peaks[20];
+
+	vortex_Eqlzr_GetAllPeaks(vortex, peaks, &count);
+	if (count != 20) {
+		printk("vortex: peak count error 20 != %d \n", count);
+		return -1;
+	}
+	for (i = 0; i < 20; i++)
+		ucontrol->value.integer.value[i] = peaks[i];
+
+	return 0;
+}
+
+static snd_kcontrol_new_t vortex_levels_kcontrol __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "EQ Peaks",
+	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info = snd_vortex_peaks_info,
+	.get = snd_vortex_peaks_get,
+};
+
+/* EQ band gain labels. */
+static char *EqBandLabels[10] __devinitdata = {
+	"EQ0 31Hz\0",
+	"EQ1 63Hz\0",
+	"EQ2 125Hz\0",
+	"EQ3 250Hz\0",
+	"EQ4 500Hz\0",
+	"EQ5 1KHz\0",
+	"EQ6 2KHz\0",
+	"EQ7 4KHz\0",
+	"EQ8 8KHz\0",
+	"EQ9 16KHz\0",
+};
+
+/* ALSA driver entry points. Init and exit. */
+static int vortex_eq_init(vortex_t * vortex)
+{
+	snd_kcontrol_t *kcontrol;
+	int err, i;
+
+	vortex_Eqlzr_init(vortex);
+
+	if ((kcontrol =
+	     snd_ctl_new1(&vortex_eqtoggle_kcontrol, vortex)) == NULL)
+		return -ENOMEM;
+	kcontrol->private_value = 0;
+	if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+		return err;
+
+	/* EQ gain controls */
+	for (i = 0; i < 10; i++) {
+		if ((kcontrol =
+		     snd_ctl_new1(&vortex_eq_kcontrol, vortex)) == NULL)
+			return -ENOMEM;
+		strcpy(kcontrol->id.name, EqBandLabels[i]);
+		kcontrol->private_value = i;
+		if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+			return err;
+		//vortex->eqctrl[i] = kcontrol;
+	}
+	/* EQ band levels */
+	if ((kcontrol = snd_ctl_new1(&vortex_levels_kcontrol, vortex)) == NULL)
+		return -ENOMEM;
+	if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+		return err;
+
+	return 0;
+}
+
+static int vortex_eq_free(vortex_t * vortex)
+{
+	/*
+	   //FIXME: segfault because vortex->eqctrl[i] == 4
+	   int i;
+	   for (i=0; i<10; i++) {
+	   if (vortex->eqctrl[i])
+	   snd_ctl_remove(vortex->card, vortex->eqctrl[i]);
+	   }
+	 */
+	vortex_Eqlzr_shutdown(vortex);
+	return 0;
+}
+
+/* End */
diff --git a/sound/pci/au88x0/au88x0_eq.h b/sound/pci/au88x0/au88x0_eq.h
new file mode 100644
index 0000000..e49bc62
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_eq.h
@@ -0,0 +1,46 @@
+#ifndef AU88X0_EQ_H
+#define AU88X0_EQ_H
+
+/***************************************************************************
+ *            au88x0_eq.h
+ *
+ *  Definitions and constant data for the Aureal Hardware EQ.
+ *
+ *  Sun Jun  8 18:23:38 2003
+ *  Author: Manuel Jander (mjander@users.sourceforge.net)
+ ****************************************************************************/
+
+typedef struct {
+	u16 LeftCoefs[50];	//0x4
+	u16 RightCoefs[50];	// 0x68
+	u16 LeftGains[20];	//0xd0
+	u16 RightGains[20];	//0xe4
+} auxxEqCoeffSet_t;
+
+typedef struct {
+	unsigned int *this00;	/*CAsp4HwIO */
+	long this04;		/* How many filters for each side (default = 10) */
+	long this08;		/* inited to cero. Stereo flag? */
+} eqhw_t;
+
+typedef struct {
+	unsigned int *this00;	/*CAsp4Core */
+	eqhw_t this04;		/* CHwEq */
+	short this08;		/* Bad codec flag ? SetBypassGain: bypass gain */
+	short this0a;
+	short this0c;		/* SetBypassGain: bypass gain when this28 is not set. */
+	short this0e;
+
+	long this10;		/* How many gains are used for each side (right or left). */
+	u16 this14[32];		/* SetLeftGainsTarget: Left (and right?) EQ gains  */
+	long this24;
+	long this28;		/* flag related to EQ enabled or not. Gang flag ? */
+	long this54;		/* SetBypass */
+	long this58;
+	long this5c;
+	/*0x60 */ auxxEqCoeffSet_t coefset;
+	/* 50 u16 word each channel. */
+	u16 this130[20];	/* Left and Right gains */
+} eqlzr_t;
+
+#endif
diff --git a/sound/pci/au88x0/au88x0_eqdata.c b/sound/pci/au88x0/au88x0_eqdata.c
new file mode 100644
index 0000000..abf8d6a
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_eqdata.c
@@ -0,0 +1,112 @@
+/* Data structs */
+
+static u16 asEqCoefsZeros[50] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static u16 asEqCoefsPipes[64] = {
+	0x0000, 0x0000,
+	0x0000, 0x0666, 0x0000, 0x0000, 0x0666,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0666, 0x0000, 0x0000, 0x0666,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0666, 0x0000, 0x0000, 0x0666,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0666, 0x0000, 0x0000, 0x0666,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0666, 0x0000, 0x0000, 0x066a,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000
+};
+
+/* More coef sets can be found in the win2k "inf" file. */
+static auxxEqCoeffSet_t asEqCoefsNormal = {
+	.LeftCoefs = {
+		      0x7e60, 0xc19e, 0x0001, 0x0002, 0x0001,
+		      0x7fa0, 0xc05f, 0x004f, 0x0000, 0xffb1,
+		      0x7f3f, 0xc0bc, 0x00c2, 0x0000, 0xff3e,
+		      0x7e78, 0xc177, 0x011f, 0x0000, 0xfee1,
+		      0x7cd6, 0xc2e5, 0x025c, 0x0000, 0xfda4,
+		      0x7949, 0xc5aa, 0x0467, 0x0000, 0xfb99,
+		      0x7120, 0xcadf, 0x0864, 0x0000, 0xf79c,
+		      0x5d33, 0xd430, 0x0f7e, 0x0000, 0xf082,
+		      0x2beb, 0xe3ca, 0x1bd3, 0x0000, 0xe42d,
+		      0xd740, 0xf01d, 0x2ac5, 0x0000, 0xd53b},
+
+	.RightCoefs = {
+		       0x7e60, 0xc19e, 0x0001, 0x0002, 0x0001,
+		       0x7fa0, 0xc05f, 0x004f, 0x0000, 0xffb1,
+		       0x7f3f, 0xc0bc, 0x00c2, 0x0000, 0xff3e,
+		       0x7e78, 0xc177, 0x011f, 0x0000, 0xfee1,
+		       0x7cd6, 0xc2e5, 0x025c, 0x0000, 0xfda4,
+		       0x7949, 0xc5aa, 0x0467, 0x0000, 0xfb99,
+		       0x7120, 0xcadf, 0x0864, 0x0000, 0xf79c,
+		       0x5d33, 0xd430, 0x0f7e, 0x0000, 0xf082,
+		       0x2beb, 0xe3ca, 0x1bd3, 0x0000, 0xe42d,
+		       0xd740, 0xf01d, 0x2ac5, 0x0000, 0xd53b},
+
+	.LeftGains = {
+		      0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
+		      0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96},
+	.RightGains = {
+		       0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
+		       0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96}
+};
+
+static u16 eq_gains_normal[20] = {
+	0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
+	0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
+	0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
+	0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96
+};
+
+/* _rodatab60 */
+static u16 eq_gains_zero[10] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+/* _rodatab7c:  ProgramPipe */
+static u16 eq_gains_current[12] = {
+	0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+	0x7fff,
+	0x7fff, 0x7fff, 0x7fff
+};
+
+/* _rodatab78 */
+static u16 eq_states_zero[2] = { 0x0000, 0x0000 };
+
+static u16 asEqOutStateZeros[48] = {
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000
+};
+
+/*_rodataba0:*/
+static long eq_levels[32] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c
new file mode 100644
index 0000000..a07d1de
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_game.c
@@ -0,0 +1,135 @@
+/*
+ * $Id: au88x0_game.c,v 1.9 2003/09/22 03:51:28 mjander Exp $
+ *
+ *  Manuel Jander.
+ *
+ *  Based on the work of:
+ *  Vojtech Pavlik
+ *  Raymond Ingles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ *
+ * Based 90% on Vojtech Pavlik pcigame driver.
+ * Merged and modified by Manuel Jander, for the OpenVortex
+ * driver. (email: mjander@embedded.cl).
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "au88x0.h"
+#include <linux/gameport.h>
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+
+#define VORTEX_GAME_DWAIT	20	/* 20 ms */
+
+static unsigned char vortex_game_read(struct gameport *gameport)
+{
+	vortex_t *vortex = gameport_get_port_data(gameport);
+	return hwread(vortex->mmio, VORTEX_GAME_LEGACY);
+}
+
+static void vortex_game_trigger(struct gameport *gameport)
+{
+	vortex_t *vortex = gameport_get_port_data(gameport);
+	hwwrite(vortex->mmio, VORTEX_GAME_LEGACY, 0xff);
+}
+
+static int
+vortex_game_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+	vortex_t *vortex = gameport_get_port_data(gameport);
+	int i;
+
+	*buttons = (~hwread(vortex->mmio, VORTEX_GAME_LEGACY) >> 4) & 0xf;
+
+	for (i = 0; i < 4; i++) {
+		axes[i] =
+		    hwread(vortex->mmio, VORTEX_GAME_AXIS + (i * AXIS_SIZE));
+		if (axes[i] == AXIS_RANGE)
+			axes[i] = -1;
+	}
+	return 0;
+}
+
+static int vortex_game_open(struct gameport *gameport, int mode)
+{
+	vortex_t *vortex = gameport_get_port_data(gameport);
+
+	switch (mode) {
+	case GAMEPORT_MODE_COOKED:
+		hwwrite(vortex->mmio, VORTEX_CTRL2,
+			hwread(vortex->mmio,
+			       VORTEX_CTRL2) | CTRL2_GAME_ADCMODE);
+		msleep(VORTEX_GAME_DWAIT);
+		return 0;
+	case GAMEPORT_MODE_RAW:
+		hwwrite(vortex->mmio, VORTEX_CTRL2,
+			hwread(vortex->mmio,
+			       VORTEX_CTRL2) & ~CTRL2_GAME_ADCMODE);
+		return 0;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+static int __devinit vortex_gameport_register(vortex_t * vortex)
+{
+	struct gameport *gp;
+
+	vortex->gameport = gp = gameport_allocate_port();
+	if (!gp) {
+		printk(KERN_ERR "vortex: cannot allocate memory for gameport\n");
+		return -ENOMEM;
+	};
+
+	gameport_set_name(gp, "AU88x0 Gameport");
+	gameport_set_phys(gp, "pci%s/gameport0", pci_name(vortex->pci_dev));
+	gameport_set_dev_parent(gp, &vortex->pci_dev->dev);
+
+	gp->read = vortex_game_read;
+	gp->trigger = vortex_game_trigger;
+	gp->cooked_read = vortex_game_cooked_read;
+	gp->open = vortex_game_open;
+
+	gameport_set_port_data(gp, vortex);
+	gp->fuzz = 64;
+
+	gameport_register_port(gp);
+
+	return 0;
+}
+
+static void vortex_gameport_unregister(vortex_t * vortex)
+{
+	if (vortex->gameport) {
+		gameport_unregister_port(vortex->gameport);
+		vortex->gameport = NULL;
+	}
+}
+
+#else
+static inline int vortex_gameport_register(vortex_t * vortex) { return -ENOSYS; }
+static inline void vortex_gameport_unregister(vortex_t * vortex) { }
+#endif
diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c
new file mode 100644
index 0000000..86e27d69
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_mixer.c
@@ -0,0 +1,33 @@
+/*
+ * Vortex Mixer support.
+ *
+ * There is much more than just the AC97 mixer...
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "au88x0.h"
+
+static int __devinit snd_vortex_mixer(vortex_t * vortex)
+{
+	ac97_bus_t *pbus;
+	ac97_template_t ac97;
+	int err;
+	static ac97_bus_ops_t ops = {
+		.write = vortex_codec_write,
+		.read = vortex_codec_read,
+	};
+
+	if ((err = snd_ac97_bus(vortex->card, 0, &ops, NULL, &pbus)) < 0)
+		return err;
+	memset(&ac97, 0, sizeof(ac97));
+	// Intialize AC97 codec stuff.
+	ac97.private_data = vortex;
+	ac97.scaps = AC97_SCAP_NO_SPDIF;
+	err = snd_ac97_mixer(pbus, &ac97, &vortex->codec);
+	vortex->isquad = ((vortex->codec == NULL) ?  0 : (vortex->codec->ext_id&0x80));
+	return err;
+}
diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c
new file mode 100644
index 0000000..c0c2346
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_mpu401.c
@@ -0,0 +1,112 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of MPU-401 in UART mode
+ *
+ *   Modified for the Aureal Vortex based Soundcards
+ *   by Manuel Jander (mjande@embedded.cl).
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/mpu401.h>
+#include "au88x0.h"
+
+/* Check for mpu401 mmio support. */
+/* MPU401 legacy support is only provided as a emergency fallback *
+ * for older versions of ALSA. Its usage is strongly discouraged. */
+#ifndef MPU401_HW_AUREAL
+#define VORTEX_MPU401_LEGACY
+#endif
+
+/* Vortex MPU401 defines. */
+#define MIDI_CLOCK_DIV      0x61
+/* Standart MPU401 defines. */
+#define MPU401_RESET		0xff
+#define MPU401_ENTER_UART	0x3f
+#define MPU401_ACK		    0xfe
+
+static int __devinit snd_vortex_midi(vortex_t * vortex)
+{
+	snd_rawmidi_t *rmidi;
+	int temp, mode;
+	mpu401_t *mpu;
+	int port;
+
+#ifdef VORTEX_MPU401_LEGACY
+	/* EnableHardCodedMPU401Port() */
+	/* Enable Legacy MIDI Interface port. */
+	port = (0x03 << 5);	/* FIXME: static address. 0x330 */
+	temp =
+	    (hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_PORT) |
+	    CTRL_MIDI_EN | port;
+	hwwrite(vortex->mmio, VORTEX_CTRL, temp);
+#else
+	/* Disable Legacy MIDI Interface port. */
+	temp =
+	    (hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_PORT) &
+	    ~CTRL_MIDI_EN;
+	hwwrite(vortex->mmio, VORTEX_CTRL, temp);
+#endif
+	/* Mpu401UartInit() */
+	mode = 1;
+	temp = hwread(vortex->mmio, VORTEX_CTRL2) & 0xffff00cf;
+	temp |= (MIDI_CLOCK_DIV << 8) | ((mode >> 24) & 0xff) << 4;
+	hwwrite(vortex->mmio, VORTEX_CTRL2, temp);
+	hwwrite(vortex->mmio, VORTEX_MIDI_CMD, MPU401_RESET);
+	/* Set some kind of mode */
+	if (mode)
+		hwwrite(vortex->mmio, VORTEX_MIDI_CMD, MPU401_ENTER_UART);
+
+	/* Check if anything is OK. */
+	temp = hwread(vortex->mmio, VORTEX_MIDI_DATA);
+	if (temp != MPU401_ACK /*0xfe */ ) {
+		printk(KERN_ERR "midi port doesn't acknowledge!\n");
+		return -ENODEV;
+	}
+	/* Enable MPU401 interrupts. */
+	hwwrite(vortex->mmio, VORTEX_IRQ_CTRL,
+		hwread(vortex->mmio, VORTEX_IRQ_CTRL) | IRQ_MIDI);
+
+	/* Create MPU401 instance. */
+#ifdef VORTEX_MPU401_LEGACY
+	if ((temp =
+	     snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_MPU401, 0x330,
+				 0, 0, 0, &rmidi)) != 0) {
+		hwwrite(vortex->mmio, VORTEX_CTRL,
+			(hwread(vortex->mmio, VORTEX_CTRL) &
+			 ~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
+		return temp;
+	}
+#else
+	port = (unsigned long)(vortex->mmio + (VORTEX_MIDI_DATA >> 2));
+	if ((temp =
+	     snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_AUREAL, port,
+				 1, 0, 0, &rmidi)) != 0) {
+		hwwrite(vortex->mmio, VORTEX_CTRL,
+			(hwread(vortex->mmio, VORTEX_CTRL) &
+			 ~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
+		return temp;
+	}
+	mpu = rmidi->private_data;
+	mpu->cport = (unsigned long)(vortex->mmio + (VORTEX_MIDI_CMD >> 2));
+#endif
+	vortex->rmidi = rmidi;
+	return 0;
+}
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
new file mode 100644
index 0000000..04dcefd
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -0,0 +1,548 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library 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.
+ */
+ 
+/*
+ * Vortex PCM ALSA driver.
+ *
+ * Supports ADB and WT DMA. Unfortunately, WT channels do not run yet.
+ * It remains stuck,and DMA transfers do not happen. 
+ */
+#include <sound/asoundef.h>
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "au88x0.h"
+
+#define VORTEX_PCM_TYPE(x) (x->name[40])
+
+/* hardware definition */
+static snd_pcm_hardware_t snd_vortex_playback_hw_adb = {
+	.info =
+	    (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_RESUME |
+	     SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
+	     SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =
+	    SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 |
+	    SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+	.rates = SNDRV_PCM_RATE_CONTINUOUS,
+	.rate_min = 5000,
+	.rate_max = 48000,
+	.channels_min = 1,
+#ifdef CHIP_AU8830
+	.channels_max = 4,
+#else
+	.channels_max = 2,
+#endif
+	.buffer_bytes_max = 0x10000,
+	.period_bytes_min = 0x1,
+	.period_bytes_max = 0x1000,
+	.periods_min = 2,
+	.periods_max = 32,
+};
+
+#ifndef CHIP_AU8820
+static snd_pcm_hardware_t snd_vortex_playback_hw_a3d = {
+	.info =
+	    (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_RESUME |
+	     SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
+	     SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =
+	    SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 |
+	    SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+	.rates = SNDRV_PCM_RATE_CONTINUOUS,
+	.rate_min = 5000,
+	.rate_max = 48000,
+	.channels_min = 1,
+	.channels_max = 1,
+	.buffer_bytes_max = 0x10000,
+	.period_bytes_min = 0x100,
+	.period_bytes_max = 0x1000,
+	.periods_min = 2,
+	.periods_max = 64,
+};
+#endif
+static snd_pcm_hardware_t snd_vortex_playback_hw_spdif = {
+	.info =
+	    (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_RESUME |
+	     SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
+	     SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =
+	    SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 |
+	    SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | SNDRV_PCM_FMTBIT_MU_LAW |
+	    SNDRV_PCM_FMTBIT_A_LAW,
+	.rates =
+	    SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+	.rate_min = 32000,
+	.rate_max = 48000,
+	.channels_min = 1,
+	.channels_max = 2,
+	.buffer_bytes_max = 0x10000,
+	.period_bytes_min = 0x100,
+	.period_bytes_max = 0x1000,
+	.periods_min = 2,
+	.periods_max = 64,
+};
+
+#ifndef CHIP_AU8810
+static snd_pcm_hardware_t snd_vortex_playback_hw_wt = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS,	// SNDRV_PCM_RATE_48000,
+	.rate_min = 8000,
+	.rate_max = 48000,
+	.channels_min = 1,
+	.channels_max = 2,
+	.buffer_bytes_max = 0x10000,
+	.period_bytes_min = 0x0400,
+	.period_bytes_max = 0x1000,
+	.periods_min = 2,
+	.periods_max = 64,
+};
+#endif
+/* open callback */
+static int snd_vortex_pcm_open(snd_pcm_substream_t * substream)
+{
+	vortex_t *vortex = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+	
+	/* Force equal size periods */
+	if ((err =
+	     snd_pcm_hw_constraint_integer(runtime,
+					   SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	/* Avoid PAGE_SIZE boundary to fall inside of a period. */
+	if ((err =
+	     snd_pcm_hw_constraint_pow2(runtime, 0,
+					SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
+		return err;
+
+	if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+#ifndef CHIP_AU8820
+		if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_A3D) {
+			runtime->hw = snd_vortex_playback_hw_a3d;
+		}
+#endif
+		if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_SPDIF) {
+			runtime->hw = snd_vortex_playback_hw_spdif;
+			switch (vortex->spdif_sr) {
+			case 32000:
+				runtime->hw.rates = SNDRV_PCM_RATE_32000;
+				break;
+			case 44100:
+				runtime->hw.rates = SNDRV_PCM_RATE_44100;
+				break;
+			case 48000:
+				runtime->hw.rates = SNDRV_PCM_RATE_48000;
+				break;
+			}
+		}
+		if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB
+		    || VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_I2S)
+			runtime->hw = snd_vortex_playback_hw_adb;
+		substream->runtime->private_data = NULL;
+	}
+#ifndef CHIP_AU8810
+	else {
+		runtime->hw = snd_vortex_playback_hw_wt;
+		substream->runtime->private_data = NULL;
+	}
+#endif
+	return 0;
+}
+
+/* close callback */
+static int snd_vortex_pcm_close(snd_pcm_substream_t * substream)
+{
+	//vortex_t *chip = snd_pcm_substream_chip(substream);
+	stream_t *stream = (stream_t *) substream->runtime->private_data;
+
+	// the hardware-specific codes will be here
+	if (stream != NULL) {
+		stream->substream = NULL;
+		stream->nr_ch = 0;
+	}
+	substream->runtime->private_data = NULL;
+	return 0;
+}
+
+/* hw_params callback */
+static int
+snd_vortex_pcm_hw_params(snd_pcm_substream_t * substream,
+			 snd_pcm_hw_params_t * hw_params)
+{
+	vortex_t *chip = snd_pcm_substream_chip(substream);
+	stream_t *stream = (stream_t *) (substream->runtime->private_data);
+	snd_pcm_sgbuf_t *sgbuf;
+	int err;
+
+	// Alloc buffer memory.
+	err =
+	    snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+	if (err < 0) {
+		printk(KERN_ERR "Vortex: pcm page alloc failed!\n");
+		return err;
+	}
+	//sgbuf = (snd_pcm_sgbuf_t *) substream->runtime->dma_private;
+	sgbuf = snd_pcm_substream_sgbuf(substream);
+	/*
+	   printk(KERN_INFO "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params),
+	   params_period_bytes(hw_params), params_channels(hw_params));
+	 */
+	spin_lock_irq(&chip->lock);
+	// Make audio routes and config buffer DMA.
+	if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+		int dma, type = VORTEX_PCM_TYPE(substream->pcm);
+		/* Dealloc any routes. */
+		if (stream != NULL)
+			vortex_adb_allocroute(chip, stream->dma,
+					      stream->nr_ch, stream->dir,
+					      stream->type);
+		/* Alloc routes. */
+		dma =
+		    vortex_adb_allocroute(chip, -1,
+					  params_channels(hw_params),
+					  substream->stream, type);
+		if (dma < 0)
+			return dma;
+		stream = substream->runtime->private_data = &chip->dma_adb[dma];
+		stream->substream = substream;
+		/* Setup Buffers. */
+		vortex_adbdma_setbuffers(chip, dma, sgbuf,
+					 params_period_bytes(hw_params),
+					 params_periods(hw_params));
+	}
+#ifndef CHIP_AU8810
+	else {
+		/* if (stream != NULL)
+		   vortex_wt_allocroute(chip, substream->number, 0); */
+		vortex_wt_allocroute(chip, substream->number,
+				     params_channels(hw_params));
+		stream = substream->runtime->private_data =
+		    &chip->dma_wt[substream->number];
+		stream->dma = substream->number;
+		stream->substream = substream;
+		vortex_wtdma_setbuffers(chip, substream->number, sgbuf,
+					params_period_bytes(hw_params),
+					params_periods(hw_params));
+	}
+#endif
+	spin_unlock_irq(&chip->lock);
+	return 0;
+}
+
+/* hw_free callback */
+static int snd_vortex_pcm_hw_free(snd_pcm_substream_t * substream)
+{
+	vortex_t *chip = snd_pcm_substream_chip(substream);
+	stream_t *stream = (stream_t *) (substream->runtime->private_data);
+
+	spin_lock_irq(&chip->lock);
+	// Delete audio routes.
+	if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+		if (stream != NULL)
+			vortex_adb_allocroute(chip, stream->dma,
+					      stream->nr_ch, stream->dir,
+					      stream->type);
+	}
+#ifndef CHIP_AU8810
+	else {
+		if (stream != NULL)
+			vortex_wt_allocroute(chip, stream->dma, 0);
+	}
+#endif
+	substream->runtime->private_data = NULL;
+	spin_unlock_irq(&chip->lock);
+
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare callback */
+static int snd_vortex_pcm_prepare(snd_pcm_substream_t * substream)
+{
+	vortex_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	stream_t *stream = (stream_t *) substream->runtime->private_data;
+	int dma = stream->dma, fmt, dir;
+
+	// set up the hardware with the current configuration.
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dir = 1;
+	else
+		dir = 0;
+	fmt = vortex_alsafmt_aspfmt(runtime->format);
+	spin_lock_irq(&chip->lock);
+	if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+		vortex_adbdma_setmode(chip, dma, 1, dir, fmt, 0 /*? */ ,
+				      0);
+		vortex_adbdma_setstartbuffer(chip, dma, 0);
+		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_SPDIF)
+			vortex_adb_setsrc(chip, dma, runtime->rate, dir);
+	}
+#ifndef CHIP_AU8810
+	else {
+		vortex_wtdma_setmode(chip, dma, 1, fmt, 0, 0);
+		// FIXME: Set rate (i guess using vortex_wt_writereg() somehow).
+		vortex_wtdma_setstartbuffer(chip, dma, 0);
+	}
+#endif
+	spin_unlock_irq(&chip->lock);
+	return 0;
+}
+
+/* trigger callback */
+static int snd_vortex_pcm_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+	vortex_t *chip = snd_pcm_substream_chip(substream);
+	stream_t *stream = (stream_t *) substream->runtime->private_data;
+	int dma = stream->dma;
+
+	spin_lock(&chip->lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		// do something to start the PCM engine
+		//printk(KERN_INFO "vortex: start %d\n", dma);
+		stream->fifo_enabled = 1;
+		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+			vortex_adbdma_resetup(chip, dma);
+			vortex_adbdma_startfifo(chip, dma);
+		}
+#ifndef CHIP_AU8810
+		else {
+			printk(KERN_INFO "vortex: wt start %d\n", dma);
+			vortex_wtdma_startfifo(chip, dma);
+		}
+#endif
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		// do something to stop the PCM engine
+		//printk(KERN_INFO "vortex: stop %d\n", dma);
+		stream->fifo_enabled = 0;
+		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
+			vortex_adbdma_pausefifo(chip, dma);
+		//vortex_adbdma_stopfifo(chip, dma);
+#ifndef CHIP_AU8810
+		else {
+			printk(KERN_INFO "vortex: wt stop %d\n", dma);
+			vortex_wtdma_stopfifo(chip, dma);
+		}
+#endif
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		//printk(KERN_INFO "vortex: pause %d\n", dma);
+		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
+			vortex_adbdma_pausefifo(chip, dma);
+#ifndef CHIP_AU8810
+		else
+			vortex_wtdma_pausefifo(chip, dma);
+#endif
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		//printk(KERN_INFO "vortex: resume %d\n", dma);
+		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
+			vortex_adbdma_resumefifo(chip, dma);
+#ifndef CHIP_AU8810
+		else
+			vortex_wtdma_resumefifo(chip, dma);
+#endif
+		break;
+	default:
+		spin_unlock(&chip->lock);
+		return -EINVAL;
+	}
+	spin_unlock(&chip->lock);
+	return 0;
+}
+
+/* pointer callback */
+static snd_pcm_uframes_t snd_vortex_pcm_pointer(snd_pcm_substream_t * substream)
+{
+	vortex_t *chip = snd_pcm_substream_chip(substream);
+	stream_t *stream = (stream_t *) substream->runtime->private_data;
+	int dma = stream->dma;
+	snd_pcm_uframes_t current_ptr = 0;
+
+	spin_lock(&chip->lock);
+	if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
+		current_ptr = vortex_adbdma_getlinearpos(chip, dma);
+#ifndef CHIP_AU8810
+	else
+		current_ptr = vortex_wtdma_getlinearpos(chip, dma);
+#endif
+	//printk(KERN_INFO "vortex: pointer = 0x%x\n", current_ptr);
+	spin_unlock(&chip->lock);
+	return (bytes_to_frames(substream->runtime, current_ptr));
+}
+
+/* Page callback. */
+/*
+static struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset) {
+	
+	
+}
+*/
+/* operators */
+static snd_pcm_ops_t snd_vortex_playback_ops = {
+	.open = snd_vortex_pcm_open,
+	.close = snd_vortex_pcm_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = snd_vortex_pcm_hw_params,
+	.hw_free = snd_vortex_pcm_hw_free,
+	.prepare = snd_vortex_pcm_prepare,
+	.trigger = snd_vortex_pcm_trigger,
+	.pointer = snd_vortex_pcm_pointer,
+	.page = snd_pcm_sgbuf_ops_page,
+};
+
+/*
+*  definitions of capture are omitted here...
+*/
+
+static char *vortex_pcm_prettyname[VORTEX_PCM_LAST] = {
+	"AU88x0 ADB",
+	"AU88x0 SPDIF",
+	"AU88x0 A3D",
+	"AU88x0 WT",
+	"AU88x0 I2S",
+};
+static char *vortex_pcm_name[VORTEX_PCM_LAST] = {
+	"adb",
+	"spdif",
+	"a3d",
+	"wt",
+	"i2s",
+};
+
+/* SPDIF kcontrol */
+
+static int snd_vortex_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_vortex_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ucontrol->value.iec958.status[0] = 0xff;
+	ucontrol->value.iec958.status[1] = 0xff;
+	ucontrol->value.iec958.status[2] = 0xff;
+	ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
+	return 0;
+}
+
+static int snd_vortex_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.iec958.status[0] = 0x00;
+	ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL|IEC958_AES1_CON_DIGDIGCONV_ID;
+	ucontrol->value.iec958.status[2] = 0x00;
+	switch (vortex->spdif_sr) {
+	case 32000: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_32000; break;
+	case 44100: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_44100; break;
+	case 48000: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000; break;
+	}
+	return 0;
+}
+
+static int snd_vortex_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+	int spdif_sr = 48000;
+	switch (ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) {
+	case IEC958_AES3_CON_FS_32000: spdif_sr = 32000; break;
+	case IEC958_AES3_CON_FS_44100: spdif_sr = 44100; break;
+	case IEC958_AES3_CON_FS_48000: spdif_sr = 48000; break;
+	}
+	if (spdif_sr == vortex->spdif_sr)
+		return 0;
+	vortex->spdif_sr = spdif_sr;
+	vortex_spdif_init(vortex, vortex->spdif_sr, 1);
+	return 1;
+}
+
+/* spdif controls */
+static snd_kcontrol_new_t snd_vortex_mixer_spdif[] __devinitdata = {
+	{
+		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+		.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+		.info =		snd_vortex_spdif_info,
+		.get =		snd_vortex_spdif_get,
+		.put =		snd_vortex_spdif_put,
+	},
+	{
+		.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+		.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+		.info =		snd_vortex_spdif_info,
+		.get =		snd_vortex_spdif_mask_get
+	},
+};
+
+/* create a pcm device */
+static int __devinit snd_vortex_new_pcm(vortex_t * chip, int idx, int nr)
+{
+	snd_pcm_t *pcm;
+	snd_kcontrol_t *kctl;
+	int i;
+	int err, nr_capt;
+
+	if ((chip == 0) || (idx < 0) || (idx > VORTEX_PCM_LAST))
+		return -ENODEV;
+
+	/* idx indicates which kind of PCM device. ADB, SPDIF, I2S and A3D share the 
+	 * same dma engine. WT uses it own separate dma engine whcih cant capture. */
+	if (idx == VORTEX_PCM_ADB)
+		nr_capt = nr;
+	else
+		nr_capt = 0;
+	if ((err =
+	     snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr,
+			 nr_capt, &pcm)) < 0)
+		return err;
+	strcpy(pcm->name, vortex_pcm_name[idx]);
+	chip->pcm[idx] = pcm;
+	// This is an evil hack, but it saves a lot of duplicated code.
+	VORTEX_PCM_TYPE(pcm) = idx;
+	pcm->private_data = chip;
+	/* set operators */
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&snd_vortex_playback_ops);
+	if (idx == VORTEX_PCM_ADB)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+				&snd_vortex_playback_ops);
+	
+	/* pre-allocation of Scatter-Gather buffers */
+	
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+					      snd_dma_pci_data(chip->pci_dev),
+					      0x10000, 0x10000);
+
+	if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) {
+		for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) {
+			kctl = snd_ctl_new1(&snd_vortex_mixer_spdif[i], chip);
+			if (!kctl)
+				return -ENOMEM;
+			if ((err = snd_ctl_add(chip->card, kctl)) < 0)
+				return err;
+		}
+	}
+	return 0;
+}
diff --git a/sound/pci/au88x0/au88x0_sb.h b/sound/pci/au88x0/au88x0_sb.h
new file mode 100644
index 0000000..5a4d8fc
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_sb.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ *            au88x0_sb.h
+ *
+ *  Wed Oct 29 22:10:42 2003
+ *  
+ ****************************************************************************/
+
+#ifdef CHIP_AU8820
+/* AU8820 starting @ 64KiB offset */
+#define SBEMU_BASE 0x10000
+#else
+/* AU8810? and AU8830 starting @ 164KiB offset */
+#define SBEMU_BASE 0x29000
+#endif
+
+#define FM_A_STATUS			(SBEMU_BASE + 0x00)	/* read */
+#define FM_A_ADDRESS		(SBEMU_BASE + 0x00)	/* write */
+#define FM_A_DATA			(SBEMU_BASE + 0x04)
+#define FM_B_STATUS			(SBEMU_BASE + 0x08)
+#define FM_B_ADDRESS		(SBEMU_BASE + 0x08)
+#define FM_B_DATA			(SBEMU_BASE + 0x0C)
+#define SB_MIXER_ADDR		(SBEMU_BASE + 0x10)
+#define SB_MIXER_DATA		(SBEMU_BASE + 0x14)
+#define SB_RESET			(SBEMU_BASE + 0x18)
+#define SB_RESET_ALIAS		(SBEMU_BASE + 0x1C)
+#define FM_STATUS2			(SBEMU_BASE + 0x20)
+#define FM_ADDR2			(SBEMU_BASE + 0x20)
+#define FM_DATA2			(SBEMU_BASE + 0x24)
+#define SB_DSP_READ			(SBEMU_BASE + 0x28)
+#define SB_DSP_WRITE		(SBEMU_BASE + 0x30)
+#define SB_DSP_WRITE_STATUS	(SBEMU_BASE + 0x30)	/* bit 7 */
+#define SB_DSP_READ_STATUS	(SBEMU_BASE + 0x38)	/* bit 7 */
+#define SB_LACR				(SBEMU_BASE + 0x40)	/* ? */
+#define SB_LADCR			(SBEMU_BASE + 0x44)	/* ? */
+#define SB_LAMR				(SBEMU_BASE + 0x48)	/* ? */
+#define SB_LARR				(SBEMU_BASE + 0x4C)	/* ? */
+#define SB_VERSION			(SBEMU_BASE + 0x50)
+#define SB_CTRLSTAT			(SBEMU_BASE + 0x54)
+#define SB_TIMERSTAT		(SBEMU_BASE + 0x58)
+#define FM_RAM				(SBEMU_BASE + 0x100)	/* 0x40 ULONG */
diff --git a/sound/pci/au88x0/au88x0_synth.c b/sound/pci/au88x0/au88x0_synth.c
new file mode 100644
index 0000000..400417d
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_synth.c
@@ -0,0 +1,395 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library 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.
+ */
+
+/*
+ * Someday its supposed to make use of the WT DMA engine
+ * for a Wavetable synthesizer.
+ */
+
+#include "au88x0.h"
+#include "au88x0_wt.h"
+
+static void vortex_fifo_setwtvalid(vortex_t * vortex, int fifo, int en);
+static void vortex_connection_adb_mixin(vortex_t * vortex, int en,
+					unsigned char channel,
+					unsigned char source,
+					unsigned char mixin);
+static void vortex_connection_mixin_mix(vortex_t * vortex, int en,
+					unsigned char mixin,
+					unsigned char mix, int a);
+static void vortex_fifo_wtinitialize(vortex_t * vortex, int fifo, int j);
+static int vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
+			    unsigned long val);
+
+/* WT */
+
+/* Put 2 WT channels together for one stereo interlaced channel. */
+static void vortex_wt_setstereo(vortex_t * vortex, u32 wt, u32 stereo)
+{
+	int temp;
+
+	//temp = hwread(vortex->mmio, 0x80 + ((wt >> 0x5)<< 0xf) + (((wt & 0x1f) >> 1) << 2));
+	temp = hwread(vortex->mmio, WT_STEREO(wt));
+	temp = (temp & 0xfe) | (stereo & 1);
+	//hwwrite(vortex->mmio, 0x80 + ((wt >> 0x5)<< 0xf) + (((wt & 0x1f) >> 1) << 2), temp);
+	hwwrite(vortex->mmio, WT_STEREO(wt), temp);
+}
+
+/* Join to mixdown route. */
+static void vortex_wt_setdsout(vortex_t * vortex, u32 wt, int en)
+{
+	int temp;
+
+	/* There is one DSREG register for each bank (32 voices each). */
+	temp = hwread(vortex->mmio, WT_DSREG((wt >= 0x20) ? 1 : 0));
+	if (en)
+		temp |= (1 << (wt & 0x1f));
+	else
+		temp &= (1 << ~(wt & 0x1f));
+	hwwrite(vortex->mmio, WT_DSREG((wt >= 0x20) ? 1 : 0), temp);
+}
+
+/* Setup WT route. */
+static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
+{
+	wt_voice_t *voice = &(vortex->wt_voice[wt]);
+	int temp;
+
+	//FIXME: WT audio routing.
+	if (nr_ch) {
+		vortex_fifo_wtinitialize(vortex, wt, 1);
+		vortex_fifo_setwtvalid(vortex, wt, 1);
+		vortex_wt_setstereo(vortex, wt, nr_ch - 1);
+	} else
+		vortex_fifo_setwtvalid(vortex, wt, 0);
+	
+	/* Set mixdown mode. */
+	vortex_wt_setdsout(vortex, wt, 1);
+	/* Set other parameter registers. */
+	hwwrite(vortex->mmio, WT_SRAMP(0), 0x880000);
+	//hwwrite(vortex->mmio, WT_GMODE(0), 0xffffffff);
+#ifdef CHIP_AU8830
+	hwwrite(vortex->mmio, WT_SRAMP(1), 0x880000);
+	//hwwrite(vortex->mmio, WT_GMODE(1), 0xffffffff);
+#endif
+	hwwrite(vortex->mmio, WT_PARM(wt, 0), 0);
+	hwwrite(vortex->mmio, WT_PARM(wt, 1), 0);
+	hwwrite(vortex->mmio, WT_PARM(wt, 2), 0);
+
+	temp = hwread(vortex->mmio, WT_PARM(wt, 3));
+	printk("vortex: WT PARM3: %x\n", temp);
+	//hwwrite(vortex->mmio, WT_PARM(wt, 3), temp);
+
+	hwwrite(vortex->mmio, WT_DELAY(wt, 0), 0);
+	hwwrite(vortex->mmio, WT_DELAY(wt, 1), 0);
+	hwwrite(vortex->mmio, WT_DELAY(wt, 2), 0);
+	hwwrite(vortex->mmio, WT_DELAY(wt, 3), 0);
+
+	printk("vortex: WT GMODE: %x\n", hwread(vortex->mmio, WT_GMODE(wt)));
+
+	hwwrite(vortex->mmio, WT_PARM(wt, 2), 0xffffffff);
+	hwwrite(vortex->mmio, WT_PARM(wt, 3), 0xcff1c810);
+
+	voice->parm0 = voice->parm1 = 0xcfb23e2f;
+	hwwrite(vortex->mmio, WT_PARM(wt, 0), voice->parm0);
+	hwwrite(vortex->mmio, WT_PARM(wt, 1), voice->parm1);
+	printk("vortex: WT GMODE 2 : %x\n", hwread(vortex->mmio, WT_GMODE(wt)));
+	return 0;
+}
+
+
+static void vortex_wt_connect(vortex_t * vortex, int en)
+{
+	int i, ii, mix;
+
+#define NR_WTROUTES 6
+#ifdef CHIP_AU8830
+#define NR_WTBLOCKS 2
+#else
+#define NR_WTBLOCKS 1
+#endif
+
+	for (i = 0; i < NR_WTBLOCKS; i++) {
+		for (ii = 0; ii < NR_WTROUTES; ii++) {
+			mix =
+			    vortex_adb_checkinout(vortex,
+						  vortex->fixed_res, en,
+						  VORTEX_RESOURCE_MIXIN);
+			vortex->mixwt[(i * NR_WTROUTES) + ii] = mix;
+
+			vortex_route(vortex, en, 0x11,
+				     ADB_WTOUT(i, ii + 0x20), ADB_MIXIN(mix));
+
+			vortex_connection_mixin_mix(vortex, en, mix,
+						    vortex->mixplayb[ii % 2], 0);
+			if (VORTEX_IS_QUAD(vortex))
+				vortex_connection_mixin_mix(vortex, en,
+							    mix,
+							    vortex->mixplayb[2 +
+								     (ii % 2)], 0);
+		}
+	}
+	for (i = 0; i < NR_WT; i++) {
+		hwwrite(vortex->mmio, WT_RUN(i), 1);
+	}
+}
+
+/* Read WT Register */
+#if 0
+static int vortex_wt_GetReg(vortex_t * vortex, char reg, int wt)
+{
+	//int eax, esi;
+
+	if (reg == 4) {
+		return hwread(vortex->mmio, WT_PARM(wt, 3));
+	}
+	if (reg == 7) {
+		return hwread(vortex->mmio, WT_GMODE(wt));
+	}
+
+	return 0;
+}
+
+/* WT hardware abstraction layer generic register interface. */
+static int
+vortex_wt_SetReg2(vortex_t * vortex, unsigned char reg, int wt,
+		  unsigned short val)
+{
+	/*
+	   int eax, edx;
+
+	   if (wt >= NR_WT)  // 0x40 -> NR_WT
+	   return 0;
+
+	   if ((reg - 0x20) > 0) {
+	   if ((reg - 0x21) != 0) 
+	   return 0;
+	   eax = ((((b & 0xff) << 0xb) + (edx & 0xff)) << 4) + 0x208; // param 2
+	   } else {
+	   eax = ((((b & 0xff) << 0xb) + (edx & 0xff)) << 4) + 0x20a; // param 3
+	   }
+	   hwwrite(vortex->mmio, eax, c);
+	 */
+	return 1;
+}
+
+/*public: static void __thiscall CWTHal::SetReg(unsigned char,int,unsigned long) */
+#endif
+static int
+vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
+		 unsigned long val)
+{
+	int ecx;
+
+	if ((reg == 5) || ((reg >= 7) && (reg <= 10)) || (reg == 0xc)) {
+		if (wt >= (NR_WT / NR_WT_PB)) {
+			printk
+			    ("vortex: WT SetReg: bank out of range. reg=0x%x, wt=%d\n",
+			     reg, wt);
+			return 0;
+		}
+	} else {
+		if (wt >= NR_WT) {
+			printk("vortex: WT SetReg: voice out of range\n");
+			return 0;
+		}
+	}
+	if (reg > 0xc)
+		return 0;
+
+	switch (reg) {
+		/* Voice specific parameters */
+	case 0:		/* running */
+		//printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_RUN(wt), (int)val);
+		hwwrite(vortex->mmio, WT_RUN(wt), val);
+		return 0xc;
+		break;
+	case 1:		/* param 0 */
+		//printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,0), (int)val);
+		hwwrite(vortex->mmio, WT_PARM(wt, 0), val);
+		return 0xc;
+		break;
+	case 2:		/* param 1 */
+		//printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,1), (int)val);
+		hwwrite(vortex->mmio, WT_PARM(wt, 1), val);
+		return 0xc;
+		break;
+	case 3:		/* param 2 */
+		//printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,2), (int)val);
+		hwwrite(vortex->mmio, WT_PARM(wt, 2), val);
+		return 0xc;
+		break;
+	case 4:		/* param 3 */
+		//printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,3), (int)val);
+		hwwrite(vortex->mmio, WT_PARM(wt, 3), val);
+		return 0xc;
+		break;
+	case 6:		/* mute */
+		//printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_MUTE(wt), (int)val);
+		hwwrite(vortex->mmio, WT_MUTE(wt), val);
+		return 0xc;
+		break;
+	case 0xb:
+		{		/* delay */
+			//printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_DELAY(wt,0), (int)val);
+			hwwrite(vortex->mmio, WT_DELAY(wt, 3), val);
+			hwwrite(vortex->mmio, WT_DELAY(wt, 2), val);
+			hwwrite(vortex->mmio, WT_DELAY(wt, 1), val);
+			hwwrite(vortex->mmio, WT_DELAY(wt, 0), val);
+			return 0xc;
+		}
+		break;
+		/* Global WT block parameters */
+	case 5:		/* sramp */
+		ecx = WT_SRAMP(wt);
+		break;
+	case 8:		/* aramp */
+		ecx = WT_ARAMP(wt);
+		break;
+	case 9:		/* mramp */
+		ecx = WT_MRAMP(wt);
+		break;
+	case 0xa:		/* ctrl */
+		ecx = WT_CTRL(wt);
+		break;
+	case 0xc:		/* ds_reg */
+		ecx = WT_DSREG(wt);
+		break;
+	default:
+		return 0;
+		break;
+	}
+	//printk("vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val);
+	hwwrite(vortex->mmio, ecx, val);
+	return 1;
+}
+
+static void vortex_wt_init(vortex_t * vortex)
+{
+	int var4, var8, varc, var10 = 0, edi;
+
+	var10 &= 0xFFFFFFE3;
+	var10 |= 0x22;
+	var10 &= 0xFFFFFEBF;
+	var10 |= 0x80;
+	var10 |= 0x200;
+	var10 &= 0xfffffffe;
+	var10 &= 0xfffffbff;
+	var10 |= 0x1800;
+	// var10 = 0x1AA2
+	var4 = 0x10000000;
+	varc = 0x00830000;
+	var8 = 0x00830000;
+
+	/* Init Bank registers. */
+	for (edi = 0; edi < (NR_WT / NR_WT_PB); edi++) {
+		vortex_wt_SetReg(vortex, 0xc, edi, 0);	/* ds_reg */
+		vortex_wt_SetReg(vortex, 0xa, edi, var10);	/* ctrl  */
+		vortex_wt_SetReg(vortex, 0x9, edi, var4);	/* mramp */
+		vortex_wt_SetReg(vortex, 0x8, edi, varc);	/* aramp */
+		vortex_wt_SetReg(vortex, 0x5, edi, var8);	/* sramp */
+	}
+	/* Init Voice registers. */
+	for (edi = 0; edi < NR_WT; edi++) {
+		vortex_wt_SetReg(vortex, 0x4, edi, 0);	/* param 3 0x20c */
+		vortex_wt_SetReg(vortex, 0x3, edi, 0);	/* param 2 0x208 */
+		vortex_wt_SetReg(vortex, 0x2, edi, 0);	/* param 1 0x204 */
+		vortex_wt_SetReg(vortex, 0x1, edi, 0);	/* param 0 0x200 */
+		vortex_wt_SetReg(vortex, 0xb, edi, 0);	/* delay 0x400 - 0x40c */
+	}
+	var10 |= 1;
+	for (edi = 0; edi < (NR_WT / NR_WT_PB); edi++)
+		vortex_wt_SetReg(vortex, 0xa, edi, var10);	/* ctrl */
+}
+
+/* Extract of CAdbTopology::SetVolume(struct _ASPVOLUME *) */
+#if 0
+static void vortex_wt_SetVolume(vortex_t * vortex, int wt, int vol[])
+{
+	wt_voice_t *voice = &(vortex->wt_voice[wt]);
+	int ecx = vol[1], eax = vol[0];
+
+	/* This is pure guess */
+	voice->parm0 &= 0xff00ffff;
+	voice->parm0 |= (vol[0] & 0xff) << 0x10;
+	voice->parm1 &= 0xff00ffff;
+	voice->parm1 |= (vol[1] & 0xff) << 0x10;
+
+	/* This is real */
+	hwwrite(vortex, WT_PARM(wt, 0), voice->parm0);
+	hwwrite(vortex, WT_PARM(wt, 1), voice->parm0);
+
+	if (voice->this_1D0 & 4) {
+		eax >>= 8;
+		ecx = eax;
+		if (ecx < 0x80)
+			ecx = 0x7f;
+		voice->parm3 &= 0xFFFFC07F;
+		voice->parm3 |= (ecx & 0x7f) << 7;
+		voice->parm3 &= 0xFFFFFF80;
+		voice->parm3 |= (eax & 0x7f);
+	} else {
+		voice->parm3 &= 0xFFE03FFF;
+		voice->parm3 |= (eax & 0xFE00) << 5;
+	}
+
+	hwwrite(vortex, WT_PARM(wt, 3), voice->parm3);
+}
+
+/* Extract of CAdbTopology::SetFrequency(unsigned long arg_0) */
+static void vortex_wt_SetFrequency(vortex_t * vortex, int wt, unsigned int sr)
+{
+	wt_voice_t *voice = &(vortex->wt_voice[wt]);
+	long int eax, edx;
+
+	//FIXME: 64 bit operation.
+	eax = ((sr << 0xf) * 0x57619F1) & 0xffffffff;
+	edx = (((sr << 0xf) * 0x57619F1)) >> 0x20;
+
+	edx >>= 0xa;
+	edx <<= 1;
+	if (edx) {
+		if (edx & 0x0FFF80000)
+			eax = 0x7fff;
+		else {
+			edx <<= 0xd;
+			eax = 7;
+			while ((edx & 0x80000000) == 0) {
+				edx <<= 1;
+				eax--;
+				if (eax == 0) ;
+				break;
+			}
+			if (eax)
+				edx <<= 1;
+			eax <<= 0xc;
+			edx >>= 0x14;
+			eax |= edx;
+		}
+	} else
+		eax = 0;
+	voice->parm0 &= 0xffff0001;
+	voice->parm0 |= (eax & 0x7fff) << 1;
+	voice->parm1 = voice->parm0 | 1;
+	// Wt: this_1D4
+	//AuWt::WriteReg((ulong)(this_1DC<<4)+0x200, (ulong)this_1E4);
+	//AuWt::WriteReg((ulong)(this_1DC<<4)+0x204, (ulong)this_1E8);
+	hwwrite(vortex->mmio, WT_PARM(wt, 0), voice->parm0);
+	hwwrite(vortex->mmio, WT_PARM(wt, 1), voice->parm1);
+}
+#endif
+
+/* End of File */
diff --git a/sound/pci/au88x0/au88x0_wt.h b/sound/pci/au88x0/au88x0_wt.h
new file mode 100644
index 0000000..d536c88
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_wt.h
@@ -0,0 +1,65 @@
+/***************************************************************************
+ *           WT register offsets.
+ *
+ *  Wed Oct 22 13:50:20 2003
+ *  Copyright  2003  mjander
+ *  mjander@users.sourceforge.org
+ ****************************************************************************/
+#ifndef _AU88X0_WT_H
+#define _AU88X0_WT_H
+
+/* WT channels are grouped in banks. Each bank has 0x20 channels. */
+/* Bank register address boundary is 0x8000 */
+
+#define NR_WT_PB 0x20
+
+/* WT bank base register (as dword address). */
+#define WT_BAR(x) (((x)&0xffe0)<<0x8)
+#define WT_BANK(x) (x>>5)
+/* WT Bank registers */
+#define WT_CTRL(bank)	(((((bank)&1)<<0xd) + 0x00)<<2)	/* 0x0000 */
+#define WT_SRAMP(bank)	(((((bank)&1)<<0xd) + 0x01)<<2)	/* 0x0004 */
+#define WT_DSREG(bank)	(((((bank)&1)<<0xd) + 0x02)<<2)	/* 0x0008 */
+#define WT_MRAMP(bank)	(((((bank)&1)<<0xd) + 0x03)<<2)	/* 0x000c */
+#define WT_GMODE(bank)	(((((bank)&1)<<0xd) + 0x04)<<2)	/* 0x0010 */
+#define WT_ARAMP(bank)	(((((bank)&1)<<0xd) + 0x05)<<2)	/* 0x0014 */
+/* WT Voice registers */
+#define WT_STEREO(voice)	((WT_BAR(voice)+ 0x20 +(((voice)&0x1f)>>1))<<2)	/* 0x0080 */
+#define WT_MUTE(voice)		((WT_BAR(voice)+ 0x40 +((voice)&0x1f))<<2)	/* 0x0100 */
+#define WT_RUN(voice)		((WT_BAR(voice)+ 0x60 +((voice)&0x1f))<<2)	/* 0x0180 */
+/* Some kind of parameters. */
+/* PARM0, PARM1 : Filter (0xFF000000), SampleRate (0x0000FFFF) */
+/* PARM2, PARM3 : Still unknown */
+#define WT_PARM(x,y)	(((WT_BAR(x))+ 0x80 +(((x)&0x1f)<<2)+(y))<<2)	/* 0x0200 */
+#define WT_DELAY(x,y)	(((WT_BAR(x))+ 0x100 +(((x)&0x1f)<<2)+(y))<<2)	/* 0x0400 */
+
+/* Numeric indexes used by SetReg() and GetReg() */
+#if 0
+enum {
+	run = 0,		/* 0  W 1:run 0:stop */
+	parm0,			/* 1  W filter, samplerate */
+	parm1,			/* 2  W filter, samplerate */
+	parm2,			/* 3  W  */
+	parm3,			/* 4  RW volume. This value is calculated using floating point ops. */
+	sramp,			/* 5  W */
+	mute,			/* 6  W 1:mute, 0:unmute */
+	gmode,			/* 7  RO Looks like only bit0 is used. */
+	aramp,			/* 8  W */
+	mramp,			/* 9  W */
+	ctrl,			/* a  W */
+	delay,			/* b  W All 4 values are written at once with same value. */
+	dsreg,			/* c  (R)W */
+} wt_reg;
+#endif
+
+typedef struct {
+	unsigned int parm0;	/* this_1E4 */
+	unsigned int parm1;	/* this_1E8 */
+	unsigned int parm2;	/* this_1EC */
+	unsigned int parm3;	/* this_1F0 */
+	unsigned int this_1D0;
+} wt_voice_t;
+
+#endif				/* _AU88X0_WT_H */
+
+/* End of file */
diff --git a/sound/pci/au88x0/au88x0_xtalk.c b/sound/pci/au88x0/au88x0_xtalk.c
new file mode 100644
index 0000000..df915fa
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_xtalk.c
@@ -0,0 +1,787 @@
+/***************************************************************************
+ *            au88x0_cxtalk.c
+ *
+ *  Wed Nov 19 16:29:47 2003
+ *  Copyright  2003  mjander
+ *  mjander@users.sourceforge.org
+ ****************************************************************************/
+
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "au88x0_xtalk.h"
+
+/* Data (a whole lot of data.... ) */
+
+static short const sXtalkWideKLeftEq = 0x269C;
+static short const sXtalkWideKRightEq = 0x269C;
+static short const sXtalkWideKLeftXt = 0xF25E;
+static short const sXtalkWideKRightXt = 0xF25E;
+static short const sXtalkWideShiftLeftEq = 1;
+static short const sXtalkWideShiftRightEq = 1;
+static short const sXtalkWideShiftLeftXt = 0;
+static short const sXtalkWideShiftRightXt = 0;
+static unsigned short const wXtalkWideLeftDelay = 0xd;
+static unsigned short const wXtalkWideRightDelay = 0xd;
+static short const sXtalkNarrowKLeftEq = 0x468D;
+static short const sXtalkNarrowKRightEq = 0x468D;
+static short const sXtalkNarrowKLeftXt = 0xF82E;
+static short const sXtalkNarrowKRightXt = 0xF82E;
+static short const sXtalkNarrowShiftLeftEq = 0x3;
+static short const sXtalkNarrowShiftRightEq = 0x3;
+static short const sXtalkNarrowShiftLeftXt = 0;
+static short const sXtalkNarrowShiftRightXt = 0;
+static unsigned short const wXtalkNarrowLeftDelay = 0x7;
+static unsigned short const wXtalkNarrowRightDelay = 0x7;
+
+static xtalk_gains_t const asXtalkGainsDefault = {
+	0x4000, 0x4000, 4000, 0x4000, 4000, 0x4000, 4000, 0x4000, 4000,
+	0x4000
+};
+
+static xtalk_gains_t const asXtalkGainsTest = {
+	0x8000, 0x7FFF, 0, 0xFFFF, 0x0001, 0xC000, 0x4000, 0xFFFE, 0x0002,
+	0
+};
+static xtalk_gains_t const asXtalkGains1Chan = {
+	0x7FFF, 0, 0, 0, 0x7FFF, 0, 0, 0, 0, 0
+};
+
+// Input gain for 4 A3D slices. One possible input pair is left zero.
+static xtalk_gains_t const asXtalkGainsAllChan = {
+	0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF,
+	0
+	    //0x7FFF,0x7FFF,0x7FFF,0x7FFF,0x7fff,0x7FFF,0x7FFF,0x7FFF,0x7FFF,0x7fff
+};
+static xtalk_gains_t const asXtalkGainsZeros = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static xtalk_dline_t const alXtalkDlineZeros = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0
+};
+static xtalk_dline_t const alXtalkDlineTest = {
+	0xFC18, 0x03E8FFFF, 0x186A0, 0x7960FFFE, 1, 0xFFFFFFFF,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0
+};
+
+static xtalk_instate_t const asXtalkInStateZeros = { 0, 0, 0, 0 };
+static xtalk_instate_t const asXtalkInStateTest =
+    { 0xFF80, 0x0080, 0xFFFF, 0x0001 };
+static xtalk_state_t const asXtalkOutStateZeros = {
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0}
+};
+static short const sDiamondKLeftEq = 0x401d;
+static short const sDiamondKRightEq = 0x401d;
+static short const sDiamondKLeftXt = 0xF90E;
+static short const sDiamondKRightXt = 0xF90E;
+static short const sDiamondShiftLeftEq = 1;	/* 0xF90E Is this a bug ??? */
+static short const sDiamondShiftRightEq = 1;
+static short const sDiamondShiftLeftXt = 0;
+static short const sDiamondShiftRightXt = 0;
+static unsigned short const wDiamondLeftDelay = 0xb;
+static unsigned short const wDiamondRightDelay = 0xb;
+
+static xtalk_coefs_t const asXtalkWideCoefsLeftEq = {
+	{0xEC4C, 0xDCE9, 0xFDC2, 0xFEEC, 0},
+	{0x5F60, 0xCBCB, 0xFC26, 0x0305, 0},
+	{0x340B, 0xf504, 0x6CE8, 0x0D23, 0x00E4},
+	{0xD500, 0x8D76, 0xACC7, 0x5B05, 0x00FA},
+	{0x7F04, 0xC0FA, 0x0263, 0xFDA2, 0}
+};
+static xtalk_coefs_t const asXtalkWideCoefsRightEq = {
+	{0xEC4C, 0xDCE9, 0xFDC2, 0xFEEC, 0},
+	{0x5F60, 0xCBCB, 0xFC26, 0x0305, 0},
+	{0x340B, 0xF504, 0x6CE8, 0x0D23, 0x00E4},
+	{0xD500, 0x8D76, 0xACC7, 0x5B05, 0x00FA},
+	{0x7F04, 0xC0FA, 0x0263, 0xFDA2, 0}
+};
+static xtalk_coefs_t const asXtalkWideCoefsLeftXt = {
+	{0x86C3, 0x7B55, 0x89C3, 0x005B, 0x0047},
+	{0x6000, 0x206A, 0xC6CA, 0x40FF, 0},
+	{0x1100, 0x1164, 0xA1D7, 0x90FC, 0x0001},
+	{0xDC00, 0x9E77, 0xB8C7, 0x0AFF, 0},
+	{0, 0, 0, 0, 0}
+};
+static xtalk_coefs_t const asXtalkWideCoefsRightXt = {
+	{0x86C3, 0x7B55, 0x89C3, 0x005B, 0x0047},
+	{0x6000, 0x206A, 0xC6CA, 0x40FF, 0},
+	{0x1100, 0x1164, 0xA1D7, 0x90FC, 0x0001},
+	{0xDC00, 0x9E77, 0xB8C7, 0x0AFF, 0},
+	{0, 0, 0, 0, 0}
+};
+static xtalk_coefs_t const asXtalkNarrowCoefsLeftEq = {
+	{0x50B5, 0xD07C, 0x026D, 0xFD21, 0},
+	{0x460F, 0xE44F, 0xF75E, 0xEFA6, 0},
+	{0x556D, 0xDCAB, 0x2098, 0xF0F2, 0},
+	{0x7E03, 0xC1F0, 0x007D, 0xFF89, 0},
+	{0x383E, 0xFD9D, 0xB278, 0x4547, 0}
+};
+
+static xtalk_coefs_t const asXtalkNarrowCoefsRightEq = {
+	{0x50B5, 0xD07C, 0x026D, 0xFD21, 0},
+	{0x460F, 0xE44F, 0xF75E, 0xEFA6, 0},
+	{0x556D, 0xDCAB, 0x2098, 0xF0F2, 0},
+	{0x7E03, 0xC1F0, 0x007D, 0xFF89, 0},
+	{0x383E, 0xFD9D, 0xB278, 0x4547, 0}
+};
+
+static xtalk_coefs_t const asXtalkNarrowCoefsLeftXt = {
+	{0x3CB2, 0xDF49, 0xF6EA, 0x095B, 0},
+	{0x6777, 0xC915, 0xFEAF, 0x00B1, 0},
+	{0x7762, 0xC7D9, 0x025B, 0xFDA6, 0},
+	{0x6B7A, 0xD2AA, 0xF2FB, 0x0B64, 0},
+	{0, 0, 0, 0, 0}
+};
+
+static xtalk_coefs_t const asXtalkNarrowCoefsRightXt = {
+	{0x3CB2, 0xDF49, 0xF6EA, 0x095B, 0},
+	{0x6777, 0xC915, 0xFEAF, 0x00B1, 0},
+	{0x7762, 0xC7D9, 0x025B, 0xFDA6, 0},
+	{0x6B7A, 0xD2AA, 0xF2FB, 0x0B64, 0},
+	{0, 0, 0, 0, 0}
+};
+
+static xtalk_coefs_t const asXtalkCoefsZeros = {
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0}
+};
+static xtalk_coefs_t const asXtalkCoefsPipe = {
+	{0, 0, 0x0FA0, 0, 0},
+	{0, 0, 0x0FA0, 0, 0},
+	{0, 0, 0x0FA0, 0, 0},
+	{0, 0, 0x0FA0, 0, 0},
+	{0, 0, 0x1180, 0, 0},
+};
+static xtalk_coefs_t const asXtalkCoefsNegPipe = {
+	{0, 0, 0xF380, 0, 0},
+	{0, 0, 0xF380, 0, 0},
+	{0, 0, 0xF380, 0, 0},
+	{0, 0, 0xF380, 0, 0},
+	{0, 0, 0xF200, 0, 0}
+};
+
+static xtalk_coefs_t const asXtalkCoefsNumTest = {
+	{0, 0, 0xF380, 0x8000, 0x6D60},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0}
+};
+
+static xtalk_coefs_t const asXtalkCoefsDenTest = {
+	{0xC000, 0x2000, 0x4000, 0, 0},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0}
+};
+
+static xtalk_state_t const asXtalkOutStateTest = {
+	{0x7FFF, 0x0004, 0xFFFC, 0},
+	{0xFE00, 0x0008, 0xFFF8, 0x4000},
+	{0x200, 0x0010, 0xFFF0, 0xC000},
+	{0x8000, 0x0020, 0xFFE0, 0},
+	{0, 0, 0, 0}
+};
+
+static xtalk_coefs_t const asDiamondCoefsLeftEq = {
+	{0x0F1E, 0x2D05, 0xF8E3, 0x07C8, 0},
+	{0x45E2, 0xCA51, 0x0448, 0xFCE7, 0},
+	{0xA93E, 0xDBD5, 0x022C, 0x028A, 0},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0}
+};
+
+static xtalk_coefs_t const asDiamondCoefsRightEq = {
+	{0x0F1E, 0x2D05, 0xF8E3, 0x07C8, 0},
+	{0x45E2, 0xCA51, 0x0448, 0xFCE7, 0},
+	{0xA93E, 0xDBD5, 0x022C, 0x028A, 0},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0}
+};
+
+static xtalk_coefs_t const asDiamondCoefsLeftXt = {
+	{0x3B50, 0xFE08, 0xF959, 0x0060, 0},
+	{0x9FCB, 0xD8F1, 0x00A2, 0x003A, 0},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0}
+};
+
+static xtalk_coefs_t const asDiamondCoefsRightXt = {
+	{0x3B50, 0xFE08, 0xF959, 0x0060, 0},
+	{0x9FCB, 0xD8F1, 0x00A2, 0x003A, 0},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0}
+};
+
+ /**/
+/* XTalk EQ and XT */
+static void
+vortex_XtalkHw_SetLeftEQ(vortex_t * vortex, short arg_0, short arg_4,
+			 xtalk_coefs_t const coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		hwwrite(vortex->mmio, 0x24200 + i * 0x24, coefs[i][0]);
+		hwwrite(vortex->mmio, 0x24204 + i * 0x24, coefs[i][1]);
+		hwwrite(vortex->mmio, 0x24208 + i * 0x24, coefs[i][2]);
+		hwwrite(vortex->mmio, 0x2420c + i * 0x24, coefs[i][3]);
+		hwwrite(vortex->mmio, 0x24210 + i * 0x24, coefs[i][4]);
+	}
+	hwwrite(vortex->mmio, 0x24538, arg_0 & 0xffff);
+	hwwrite(vortex->mmio, 0x2453C, arg_4 & 0xffff);
+}
+
+static void
+vortex_XtalkHw_SetRightEQ(vortex_t * vortex, short arg_0, short arg_4,
+			  xtalk_coefs_t const coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		hwwrite(vortex->mmio, 0x242b4 + i * 0x24, coefs[i][0]);
+		hwwrite(vortex->mmio, 0x242b8 + i * 0x24, coefs[i][1]);
+		hwwrite(vortex->mmio, 0x242bc + i * 0x24, coefs[i][2]);
+		hwwrite(vortex->mmio, 0x242c0 + i * 0x24, coefs[i][3]);
+		hwwrite(vortex->mmio, 0x242c4 + i * 0x24, coefs[i][4]);
+	}
+	hwwrite(vortex->mmio, 0x24540, arg_0 & 0xffff);
+	hwwrite(vortex->mmio, 0x24544, arg_4 & 0xffff);
+}
+
+static void
+vortex_XtalkHw_SetLeftXT(vortex_t * vortex, short arg_0, short arg_4,
+			 xtalk_coefs_t const coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		hwwrite(vortex->mmio, 0x24368 + i * 0x24, coefs[i][0]);
+		hwwrite(vortex->mmio, 0x2436c + i * 0x24, coefs[i][1]);
+		hwwrite(vortex->mmio, 0x24370 + i * 0x24, coefs[i][2]);
+		hwwrite(vortex->mmio, 0x24374 + i * 0x24, coefs[i][3]);
+		hwwrite(vortex->mmio, 0x24378 + i * 0x24, coefs[i][4]);
+	}
+	hwwrite(vortex->mmio, 0x24548, arg_0 & 0xffff);
+	hwwrite(vortex->mmio, 0x2454C, arg_4 & 0xffff);
+}
+
+static void
+vortex_XtalkHw_SetRightXT(vortex_t * vortex, short arg_0, short arg_4,
+			  xtalk_coefs_t const coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		hwwrite(vortex->mmio, 0x2441C + i * 0x24, coefs[i][0]);
+		hwwrite(vortex->mmio, 0x24420 + i * 0x24, coefs[i][1]);
+		hwwrite(vortex->mmio, 0x24424 + i * 0x24, coefs[i][2]);
+		hwwrite(vortex->mmio, 0x24428 + i * 0x24, coefs[i][3]);
+		hwwrite(vortex->mmio, 0x2442C + i * 0x24, coefs[i][4]);
+	}
+	hwwrite(vortex->mmio, 0x24550, arg_0 & 0xffff);
+	hwwrite(vortex->mmio, 0x24554, arg_4 & 0xffff);
+}
+
+static void
+vortex_XtalkHw_SetLeftEQStates(vortex_t * vortex,
+			       xtalk_instate_t const arg_0,
+			       xtalk_state_t const coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		hwwrite(vortex->mmio, 0x24214 + i * 0x24, coefs[i][0]);
+		hwwrite(vortex->mmio, 0x24218 + i * 0x24, coefs[i][1]);
+		hwwrite(vortex->mmio, 0x2421C + i * 0x24, coefs[i][2]);
+		hwwrite(vortex->mmio, 0x24220 + i * 0x24, coefs[i][3]);
+	}
+	hwwrite(vortex->mmio, 0x244F8 + i * 0x24, arg_0[0]);
+	hwwrite(vortex->mmio, 0x244FC + i * 0x24, arg_0[1]);
+	hwwrite(vortex->mmio, 0x24500 + i * 0x24, arg_0[2]);
+	hwwrite(vortex->mmio, 0x24504 + i * 0x24, arg_0[3]);
+}
+
+static void
+vortex_XtalkHw_SetRightEQStates(vortex_t * vortex,
+				xtalk_instate_t const arg_0,
+				xtalk_state_t const coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		hwwrite(vortex->mmio, 0x242C8 + i * 0x24, coefs[i][0]);
+		hwwrite(vortex->mmio, 0x242CC + i * 0x24, coefs[i][1]);
+		hwwrite(vortex->mmio, 0x242D0 + i * 0x24, coefs[i][2]);
+		hwwrite(vortex->mmio, 0x244D4 + i * 0x24, coefs[i][3]);
+	}
+	hwwrite(vortex->mmio, 0x24508 + i * 0x24, arg_0[0]);
+	hwwrite(vortex->mmio, 0x2450C + i * 0x24, arg_0[1]);
+	hwwrite(vortex->mmio, 0x24510 + i * 0x24, arg_0[2]);
+	hwwrite(vortex->mmio, 0x24514 + i * 0x24, arg_0[3]);
+}
+
+static void
+vortex_XtalkHw_SetLeftXTStates(vortex_t * vortex,
+			       xtalk_instate_t const arg_0,
+			       xtalk_state_t const coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		hwwrite(vortex->mmio, 0x2437C + i * 0x24, coefs[i][0]);
+		hwwrite(vortex->mmio, 0x24380 + i * 0x24, coefs[i][1]);
+		hwwrite(vortex->mmio, 0x24384 + i * 0x24, coefs[i][2]);
+		hwwrite(vortex->mmio, 0x24388 + i * 0x24, coefs[i][3]);
+	}
+	hwwrite(vortex->mmio, 0x24518 + i * 0x24, arg_0[0]);
+	hwwrite(vortex->mmio, 0x2451C + i * 0x24, arg_0[1]);
+	hwwrite(vortex->mmio, 0x24520 + i * 0x24, arg_0[2]);
+	hwwrite(vortex->mmio, 0x24524 + i * 0x24, arg_0[3]);
+}
+
+static void
+vortex_XtalkHw_SetRightXTStates(vortex_t * vortex,
+				xtalk_instate_t const arg_0,
+				xtalk_state_t const coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		hwwrite(vortex->mmio, 0x24430 + i * 0x24, coefs[i][0]);
+		hwwrite(vortex->mmio, 0x24434 + i * 0x24, coefs[i][1]);
+		hwwrite(vortex->mmio, 0x24438 + i * 0x24, coefs[i][2]);
+		hwwrite(vortex->mmio, 0x2443C + i * 0x24, coefs[i][3]);
+	}
+	hwwrite(vortex->mmio, 0x24528 + i * 0x24, arg_0[0]);
+	hwwrite(vortex->mmio, 0x2452C + i * 0x24, arg_0[1]);
+	hwwrite(vortex->mmio, 0x24530 + i * 0x24, arg_0[2]);
+	hwwrite(vortex->mmio, 0x24534 + i * 0x24, arg_0[3]);
+}
+
+#if 0
+static void
+vortex_XtalkHw_GetLeftEQ(vortex_t * vortex, short *arg_0, short *arg_4,
+			 xtalk_coefs_t coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		coefs[i][0] = hwread(vortex->mmio, 0x24200 + i * 0x24);
+		coefs[i][1] = hwread(vortex->mmio, 0x24204 + i * 0x24);
+		coefs[i][2] = hwread(vortex->mmio, 0x24208 + i * 0x24);
+		coefs[i][3] = hwread(vortex->mmio, 0x2420c + i * 0x24);
+		coefs[i][4] = hwread(vortex->mmio, 0x24210 + i * 0x24);
+	}
+	*arg_0 = hwread(vortex->mmio, 0x24538) & 0xffff;
+	*arg_4 = hwread(vortex->mmio, 0x2453c) & 0xffff;
+}
+
+static void
+vortex_XtalkHw_GetRightEQ(vortex_t * vortex, short *arg_0, short *arg_4,
+			  xtalk_coefs_t coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		coefs[i][0] = hwread(vortex->mmio, 0x242b4 + i * 0x24);
+		coefs[i][1] = hwread(vortex->mmio, 0x242b8 + i * 0x24);
+		coefs[i][2] = hwread(vortex->mmio, 0x242bc + i * 0x24);
+		coefs[i][3] = hwread(vortex->mmio, 0x242c0 + i * 0x24);
+		coefs[i][4] = hwread(vortex->mmio, 0x242c4 + i * 0x24);
+	}
+	*arg_0 = hwread(vortex->mmio, 0x24540) & 0xffff;
+	*arg_4 = hwread(vortex->mmio, 0x24544) & 0xffff;
+}
+
+static void
+vortex_XtalkHw_GetLeftXT(vortex_t * vortex, short *arg_0, short *arg_4,
+			 xtalk_coefs_t coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		coefs[i][0] = hwread(vortex->mmio, 0x24368 + i * 0x24);
+		coefs[i][1] = hwread(vortex->mmio, 0x2436C + i * 0x24);
+		coefs[i][2] = hwread(vortex->mmio, 0x24370 + i * 0x24);
+		coefs[i][3] = hwread(vortex->mmio, 0x24374 + i * 0x24);
+		coefs[i][4] = hwread(vortex->mmio, 0x24378 + i * 0x24);
+	}
+	*arg_0 = hwread(vortex->mmio, 0x24548) & 0xffff;
+	*arg_4 = hwread(vortex->mmio, 0x2454C) & 0xffff;
+}
+
+static void
+vortex_XtalkHw_GetRightXT(vortex_t * vortex, short *arg_0, short *arg_4,
+			  xtalk_coefs_t coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		coefs[i][0] = hwread(vortex->mmio, 0x2441C + i * 0x24);
+		coefs[i][1] = hwread(vortex->mmio, 0x24420 + i * 0x24);
+		coefs[i][2] = hwread(vortex->mmio, 0x24424 + i * 0x24);
+		coefs[i][3] = hwread(vortex->mmio, 0x24428 + i * 0x24);
+		coefs[i][4] = hwread(vortex->mmio, 0x2442C + i * 0x24);
+	}
+	*arg_0 = hwread(vortex->mmio, 0x24550) & 0xffff;
+	*arg_4 = hwread(vortex->mmio, 0x24554) & 0xffff;
+}
+
+static void
+vortex_XtalkHw_GetLeftEQStates(vortex_t * vortex, xtalk_instate_t arg_0,
+			       xtalk_state_t coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		coefs[i][0] = hwread(vortex->mmio, 0x24214 + i * 0x24);
+		coefs[i][1] = hwread(vortex->mmio, 0x24218 + i * 0x24);
+		coefs[i][2] = hwread(vortex->mmio, 0x2421C + i * 0x24);
+		coefs[i][3] = hwread(vortex->mmio, 0x24220 + i * 0x24);
+	}
+	arg_0[0] = hwread(vortex->mmio, 0x244F8 + i * 0x24);
+	arg_0[1] = hwread(vortex->mmio, 0x244FC + i * 0x24);
+	arg_0[2] = hwread(vortex->mmio, 0x24500 + i * 0x24);
+	arg_0[3] = hwread(vortex->mmio, 0x24504 + i * 0x24);
+}
+
+static void
+vortex_XtalkHw_GetRightEQStates(vortex_t * vortex, xtalk_instate_t arg_0,
+				xtalk_state_t coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		coefs[i][0] = hwread(vortex->mmio, 0x242C8 + i * 0x24);
+		coefs[i][1] = hwread(vortex->mmio, 0x242CC + i * 0x24);
+		coefs[i][2] = hwread(vortex->mmio, 0x242D0 + i * 0x24);
+		coefs[i][3] = hwread(vortex->mmio, 0x242D4 + i * 0x24);
+	}
+	arg_0[0] = hwread(vortex->mmio, 0x24508 + i * 0x24);
+	arg_0[1] = hwread(vortex->mmio, 0x2450C + i * 0x24);
+	arg_0[2] = hwread(vortex->mmio, 0x24510 + i * 0x24);
+	arg_0[3] = hwread(vortex->mmio, 0x24514 + i * 0x24);
+}
+
+static void
+vortex_XtalkHw_GetLeftXTStates(vortex_t * vortex, xtalk_instate_t arg_0,
+			       xtalk_state_t coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		coefs[i][0] = hwread(vortex->mmio, 0x2437C + i * 0x24);
+		coefs[i][1] = hwread(vortex->mmio, 0x24380 + i * 0x24);
+		coefs[i][2] = hwread(vortex->mmio, 0x24384 + i * 0x24);
+		coefs[i][3] = hwread(vortex->mmio, 0x24388 + i * 0x24);
+	}
+	arg_0[0] = hwread(vortex->mmio, 0x24518 + i * 0x24);
+	arg_0[1] = hwread(vortex->mmio, 0x2451C + i * 0x24);
+	arg_0[2] = hwread(vortex->mmio, 0x24520 + i * 0x24);
+	arg_0[3] = hwread(vortex->mmio, 0x24524 + i * 0x24);
+}
+
+static void
+vortex_XtalkHw_GetRightXTStates(vortex_t * vortex, xtalk_instate_t arg_0,
+				xtalk_state_t coefs)
+{
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		coefs[i][0] = hwread(vortex->mmio, 0x24430 + i * 0x24);
+		coefs[i][1] = hwread(vortex->mmio, 0x24434 + i * 0x24);
+		coefs[i][2] = hwread(vortex->mmio, 0x24438 + i * 0x24);
+		coefs[i][3] = hwread(vortex->mmio, 0x2443C + i * 0x24);
+	}
+	arg_0[0] = hwread(vortex->mmio, 0x24528 + i * 0x24);
+	arg_0[1] = hwread(vortex->mmio, 0x2452C + i * 0x24);
+	arg_0[2] = hwread(vortex->mmio, 0x24530 + i * 0x24);
+	arg_0[3] = hwread(vortex->mmio, 0x24534 + i * 0x24);
+}
+
+#endif
+/* Gains */
+
+static void
+vortex_XtalkHw_SetGains(vortex_t * vortex, xtalk_gains_t const gains)
+{
+	int i;
+
+	for (i = 0; i < XTGAINS_SZ; i++) {
+		hwwrite(vortex->mmio, 0x244D0 + (i * 4), gains[i]);
+	}
+}
+
+static void
+vortex_XtalkHw_SetGainsAllChan(vortex_t * vortex)
+{
+	vortex_XtalkHw_SetGains(vortex, asXtalkGainsAllChan);
+}
+
+#if 0
+static void vortex_XtalkHw_GetGains(vortex_t * vortex, xtalk_gains_t gains)
+{
+	int i;
+
+	for (i = 0; i < XTGAINS_SZ; i++)
+		gains[i] = hwread(vortex->mmio, 0x244D0 + i * 4);
+}
+
+#endif
+/* Delay parameters */
+
+static void
+vortex_XtalkHw_SetDelay(vortex_t * vortex, unsigned short right,
+			unsigned short left)
+{
+	int esp0 = 0;
+
+	esp0 &= 0x1FFFFFFF;
+	esp0 |= 0xA0000000;
+	esp0 = (esp0 & 0xffffE0ff) | ((right & 0x1F) << 8);
+	esp0 = (esp0 & 0xfffc1fff) | ((left & 0x1F) << 0xd);
+
+	hwwrite(vortex->mmio, 0x24660, esp0);
+}
+
+static void
+vortex_XtalkHw_SetLeftDline(vortex_t * vortex, xtalk_dline_t const dline)
+{
+	int i;
+
+	for (i = 0; i < 0x20; i++) {
+		hwwrite(vortex->mmio, 0x24000 + (i << 2), dline[i] & 0xffff);
+		hwwrite(vortex->mmio, 0x24080 + (i << 2), dline[i] >> 0x10);
+	}
+}
+
+static void
+vortex_XtalkHw_SetRightDline(vortex_t * vortex, xtalk_dline_t const dline)
+{
+	int i;
+
+	for (i = 0; i < 0x20; i++) {
+		hwwrite(vortex->mmio, 0x24100 + (i << 2), dline[i] & 0xffff);
+		hwwrite(vortex->mmio, 0x24180 + (i << 2), dline[i] >> 0x10);
+	}
+}
+
+#if 0
+static void
+vortex_XtalkHw_GetDelay(vortex_t * vortex, unsigned short *right,
+			unsigned short *left)
+{
+	int esp0;
+
+	esp0 = hwread(vortex->mmio, 0x24660);
+	*right = (esp0 >> 8) & 0x1f;
+	*left = (esp0 >> 0xd) & 0x1f;
+}
+
+static void vortex_XtalkHw_GetLeftDline(vortex_t * vortex, xtalk_dline_t dline)
+{
+	int i;
+
+	for (i = 0; i < 0x20; i++) {
+		dline[i] =
+		    (hwread(vortex->mmio, 0x24000 + (i << 2)) & 0xffff) |
+		    (hwread(vortex->mmio, 0x24080 + (i << 2)) << 0x10);
+	}
+}
+
+static void vortex_XtalkHw_GetRightDline(vortex_t * vortex, xtalk_dline_t dline)
+{
+	int i;
+
+	for (i = 0; i < 0x20; i++) {
+		dline[i] =
+		    (hwread(vortex->mmio, 0x24100 + (i << 2)) & 0xffff) |
+		    (hwread(vortex->mmio, 0x24180 + (i << 2)) << 0x10);
+	}
+}
+
+#endif
+/* Control/Global stuff */
+
+#if 0
+static void vortex_XtalkHw_SetControlReg(vortex_t * vortex, unsigned long ctrl)
+{
+	hwwrite(vortex->mmio, 0x24660, ctrl);
+}
+static void vortex_XtalkHw_GetControlReg(vortex_t * vortex, unsigned long *ctrl)
+{
+	*ctrl = hwread(vortex->mmio, 0x24660);
+}
+#endif
+static void vortex_XtalkHw_SetSampleRate(vortex_t * vortex, int sr)
+{
+	int temp;
+
+	temp = (hwread(vortex->mmio, 0x24660) & 0x1FFFFFFF) | 0xC0000000;
+	temp = (temp & 0xffffff07) | ((sr & 0x1f) << 3);
+	hwwrite(vortex->mmio, 0x24660, temp);
+}
+
+#if 0
+static void vortex_XtalkHw_GetSampleRate(vortex_t * vortex, int *sr)
+{
+	*sr = (hwread(vortex->mmio, 0x24660) >> 3) & 0x1f;
+}
+
+#endif
+static void vortex_XtalkHw_Enable(vortex_t * vortex)
+{
+	int temp;
+
+	temp = (hwread(vortex->mmio, 0x24660) & 0x1FFFFFFF) | 0xC0000000;
+	temp |= 1;
+	hwwrite(vortex->mmio, 0x24660, temp);
+
+}
+
+static void vortex_XtalkHw_Disable(vortex_t * vortex)
+{
+	int temp;
+
+	temp = (hwread(vortex->mmio, 0x24660) & 0x1FFFFFFF) | 0xC0000000;
+	temp &= 0xfffffffe;
+	hwwrite(vortex->mmio, 0x24660, temp);
+
+}
+
+static void vortex_XtalkHw_ZeroIO(vortex_t * vortex)
+{
+	int i;
+
+	for (i = 0; i < 20; i++)
+		hwwrite(vortex->mmio, 0x24600 + (i << 2), 0);
+	for (i = 0; i < 4; i++)
+		hwwrite(vortex->mmio, 0x24650 + (i << 2), 0);
+}
+
+static void vortex_XtalkHw_ZeroState(vortex_t * vortex)
+{
+	vortex_XtalkHw_ZeroIO(vortex);	// inlined
+
+	vortex_XtalkHw_SetLeftEQ(vortex, 0, 0, asXtalkCoefsZeros);
+	vortex_XtalkHw_SetRightEQ(vortex, 0, 0, asXtalkCoefsZeros);
+
+	vortex_XtalkHw_SetLeftXT(vortex, 0, 0, asXtalkCoefsZeros);
+	vortex_XtalkHw_SetRightXT(vortex, 0, 0, asXtalkCoefsZeros);
+
+	vortex_XtalkHw_SetGains(vortex, asXtalkGainsZeros);	// inlined
+
+	vortex_XtalkHw_SetDelay(vortex, 0, 0);	// inlined
+
+	vortex_XtalkHw_SetLeftDline(vortex, alXtalkDlineZeros);	// inlined
+	vortex_XtalkHw_SetRightDline(vortex, alXtalkDlineZeros);	// inlined
+	vortex_XtalkHw_SetLeftDline(vortex, alXtalkDlineZeros);	// inlined
+	vortex_XtalkHw_SetRightDline(vortex, alXtalkDlineZeros);	// inlined
+
+	vortex_XtalkHw_SetLeftEQStates(vortex, asXtalkInStateZeros,
+				       asXtalkOutStateZeros);
+	vortex_XtalkHw_SetRightEQStates(vortex, asXtalkInStateZeros,
+					asXtalkOutStateZeros);
+	vortex_XtalkHw_SetLeftXTStates(vortex, asXtalkInStateZeros,
+				       asXtalkOutStateZeros);
+	vortex_XtalkHw_SetRightXTStates(vortex, asXtalkInStateZeros,
+					asXtalkOutStateZeros);
+}
+
+static void vortex_XtalkHw_ProgramPipe(vortex_t * vortex)
+{
+
+	vortex_XtalkHw_SetLeftEQ(vortex, 0, 1, asXtalkCoefsPipe);
+	vortex_XtalkHw_SetRightEQ(vortex, 0, 1, asXtalkCoefsPipe);
+	vortex_XtalkHw_SetLeftXT(vortex, 0, 0, asXtalkCoefsZeros);
+	vortex_XtalkHw_SetRightXT(vortex, 0, 0, asXtalkCoefsZeros);
+
+	vortex_XtalkHw_SetDelay(vortex, 0, 0);	// inlined
+}
+
+static void vortex_XtalkHw_ProgramXtalkWide(vortex_t * vortex)
+{
+
+	vortex_XtalkHw_SetLeftEQ(vortex, sXtalkWideKLeftEq,
+				 sXtalkWideShiftLeftEq, asXtalkWideCoefsLeftEq);
+	vortex_XtalkHw_SetRightEQ(vortex, sXtalkWideKRightEq,
+				  sXtalkWideShiftRightEq,
+				  asXtalkWideCoefsRightEq);
+	vortex_XtalkHw_SetLeftXT(vortex, sXtalkWideKLeftXt,
+				 sXtalkWideShiftLeftXt, asXtalkWideCoefsLeftXt);
+	vortex_XtalkHw_SetRightXT(vortex, sXtalkWideKLeftXt,
+				  sXtalkWideShiftLeftXt,
+				  asXtalkWideCoefsLeftXt);
+
+	vortex_XtalkHw_SetDelay(vortex, wXtalkWideRightDelay, wXtalkWideLeftDelay);	// inlined
+}
+
+static void vortex_XtalkHw_ProgramXtalkNarrow(vortex_t * vortex)
+{
+
+	vortex_XtalkHw_SetLeftEQ(vortex, sXtalkNarrowKLeftEq,
+				 sXtalkNarrowShiftLeftEq,
+				 asXtalkNarrowCoefsLeftEq);
+	vortex_XtalkHw_SetRightEQ(vortex, sXtalkNarrowKRightEq,
+				  sXtalkNarrowShiftRightEq,
+				  asXtalkNarrowCoefsRightEq);
+	vortex_XtalkHw_SetLeftXT(vortex, sXtalkNarrowKLeftXt,
+				 sXtalkNarrowShiftLeftXt,
+				 asXtalkNarrowCoefsLeftXt);
+	vortex_XtalkHw_SetRightXT(vortex, sXtalkNarrowKLeftXt,
+				  sXtalkNarrowShiftLeftXt,
+				  asXtalkNarrowCoefsLeftXt);
+
+	vortex_XtalkHw_SetDelay(vortex, wXtalkNarrowRightDelay, wXtalkNarrowLeftDelay);	// inlined
+}
+
+static void vortex_XtalkHw_ProgramDiamondXtalk(vortex_t * vortex)
+{
+
+	//sDiamondKLeftEq,sDiamondKRightXt,asDiamondCoefsLeftEq
+	vortex_XtalkHw_SetLeftEQ(vortex, sDiamondKLeftEq,
+				 sDiamondShiftLeftEq, asDiamondCoefsLeftEq);
+	vortex_XtalkHw_SetRightEQ(vortex, sDiamondKRightEq,
+				  sDiamondShiftRightEq, asDiamondCoefsRightEq);
+	vortex_XtalkHw_SetLeftXT(vortex, sDiamondKLeftXt,
+				 sDiamondShiftLeftXt, asDiamondCoefsLeftXt);
+	vortex_XtalkHw_SetRightXT(vortex, sDiamondKLeftXt,
+				  sDiamondShiftLeftXt, asDiamondCoefsLeftXt);
+
+	vortex_XtalkHw_SetDelay(vortex, wDiamondRightDelay, wDiamondLeftDelay);	// inlined
+}
+
+static void vortex_XtalkHw_init(vortex_t * vortex)
+{
+	vortex_XtalkHw_ZeroState(vortex);
+}
+
+/* End of file */
diff --git a/sound/pci/au88x0/au88x0_xtalk.h b/sound/pci/au88x0/au88x0_xtalk.h
new file mode 100644
index 0000000..0b8d7b6
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_xtalk.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ *            au88x0_cxtalk.h
+ *
+ *  Wed Nov 19 19:07:17 2003
+ *  Copyright  2003  mjander
+ *  mjander@users.sourceforge.org
+ ****************************************************************************/
+
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* The crosstalk canceler supports 5 stereo input channels. The result is 
+   available at one single output route pair (stereo). */
+
+#ifndef _AU88X0_CXTALK_H
+#define _AU88X0_CXTALK_H
+
+#include "au88x0.h"
+
+#define XTDLINE_SZ 32
+#define XTGAINS_SZ 10
+#define XTINST_SZ 4
+
+#define XT_HEADPHONE	1
+#define XT_SPEAKER0		2
+#define XT_SPEAKER1		3
+#define XT_DIAMOND		4
+
+typedef long xtalk_dline_t[XTDLINE_SZ];
+typedef short xtalk_gains_t[XTGAINS_SZ];
+typedef short xtalk_instate_t[XTINST_SZ];
+typedef short xtalk_coefs_t[5][5];
+typedef short xtalk_state_t[5][4];
+
+static void vortex_XtalkHw_SetGains(vortex_t * vortex,
+				    xtalk_gains_t const gains);
+static void vortex_XtalkHw_SetGainsAllChan(vortex_t * vortex);
+static void vortex_XtalkHw_SetSampleRate(vortex_t * vortex, int sr);
+static void vortex_XtalkHw_ProgramPipe(vortex_t * vortex);
+static void vortex_XtalkHw_ProgramPipe(vortex_t * vortex);
+static void vortex_XtalkHw_ProgramXtalkWide(vortex_t * vortex);
+static void vortex_XtalkHw_ProgramXtalkNarrow(vortex_t * vortex);
+static void vortex_XtalkHw_ProgramDiamondXtalk(vortex_t * vortex);
+static void vortex_XtalkHw_Enable(vortex_t * vortex);
+static void vortex_XtalkHw_Disable(vortex_t * vortex);
+static void vortex_XtalkHw_init(vortex_t * vortex);
+
+#endif				/* _AU88X0_CXTALK_H */