]> git.neil.brown.name Git - history.git/commitdiff
ALSA CVS update - Takashi Iwai <tiwai@suse.de>
authorJaroslav Kysela <perex@suse.cz>
Sat, 13 Mar 2004 19:16:54 +0000 (20:16 +0100)
committerJaroslav Kysela <perex@suse.cz>
Sat, 13 Mar 2004 19:16:54 +0000 (20:16 +0100)
Documentation,PCI drivers,au88x0 driver
added the au88x0 drivers for Aureal soundcards by Manuel Jander <mjander@embedded.cl>

28 files changed:
Documentation/sound/alsa/ALSA-Configuration.txt
sound/pci/Kconfig
sound/pci/Makefile
sound/pci/au88x0/Makefile [new file with mode: 0644]
sound/pci/au88x0/au8810.c [new file with mode: 0644]
sound/pci/au88x0/au8810.h [new file with mode: 0644]
sound/pci/au88x0/au8820.c [new file with mode: 0644]
sound/pci/au88x0/au8820.h [new file with mode: 0644]
sound/pci/au88x0/au8830.c [new file with mode: 0644]
sound/pci/au88x0/au8830.h [new file with mode: 0644]
sound/pci/au88x0/au88x0.c [new file with mode: 0644]
sound/pci/au88x0/au88x0.h [new file with mode: 0644]
sound/pci/au88x0/au88x0_a3d.c [new file with mode: 0644]
sound/pci/au88x0/au88x0_a3d.h [new file with mode: 0644]
sound/pci/au88x0/au88x0_a3ddata.c [new file with mode: 0644]
sound/pci/au88x0/au88x0_core.c [new file with mode: 0644]
sound/pci/au88x0/au88x0_eq.c [new file with mode: 0644]
sound/pci/au88x0/au88x0_eq.h [new file with mode: 0644]
sound/pci/au88x0/au88x0_eqdata.c [new file with mode: 0644]
sound/pci/au88x0/au88x0_game.c [new file with mode: 0644]
sound/pci/au88x0/au88x0_mixer.c [new file with mode: 0644]
sound/pci/au88x0/au88x0_mpu401.c [new file with mode: 0644]
sound/pci/au88x0/au88x0_pcm.c [new file with mode: 0644]
sound/pci/au88x0/au88x0_sb.h [new file with mode: 0644]
sound/pci/au88x0/au88x0_synth.c [new file with mode: 0644]
sound/pci/au88x0/au88x0_wt.h [new file with mode: 0644]
sound/pci/au88x0/au88x0_xtalk.c [new file with mode: 0644]
sound/pci/au88x0/au88x0_xtalk.h [new file with mode: 0644]

index f54fa84ba1c81251e0b421679734ba247f0b5f94..f32f5f156440efe00b1e6afe2eb04b28b8e917a4 100644 (file)
@@ -112,7 +112,8 @@ Module parameters
                - value is used for /proc/asound filesystem
                - this value can be used by applications for identification
                  of card if user does not want identify card with index number
-    enable     - enable card (only first card is enabled by default)
+    enable     - enable card.  (all cards enabled for PCI and ISA PnP cards
+                 as default.)
 
   Module snd-ad1816a
   ------------------
@@ -178,6 +179,32 @@ Module parameters
     
     Module supports up to 8 cards, autoprobe and PnP.
 
+  Module snd-au8810, snd-au8820, snd-au8830
+  -----------------------------------------
+
+    Module for Aureal Vortex, Vortex2 and Advantage device.
+
+    pcifix     - Control PCI workarounds
+                 0 = Disable all workarounds
+                 1 = Force the PCI latency of the Aureal card to 0xff
+                 2 = Force the Extend PCI#2 Internal Master for Efficient
+                     Handling of Dummy Requests on the VIA KT133 AGP Bridge
+                 3 = Force both settings
+                 255 = Autodetect what is required (default)
+
+    This module supports all ADB PCM channels, ac97 mixer, SPDIF, hardware
+    EQ, mpu401, gameport. A3D and wavetable support are still in development.
+    Development and reverse engineering work is being coordinated at
+    http://savannah.nongnu.org/projects/openvortex/
+    SPDIF output has a copy of the AC97 codec output, unless you use the
+    "spdif" pcm device, which allows raw data passthru.
+    The hardware EQ hardware and SPDIF is only present in the Vortex2 and 
+    Advantage.
+
+    Note: Some ALSA mixer applicactions don't handle the SPDIF samplerate 
+           control correctly. If you have problems regarding this, try
+           another ALSA compliant mixer (alsamixer works).
+
   Module snd-azt2320
   ------------------
 
@@ -608,6 +635,7 @@ Module parameters
                      1 = use headphone control as master
                      2 = swap headphone and master controls
                      3 = for AD1985, turn on OMS bit and use headphone
+                     4 = for ALC65x, turn on the jack sense mode
 
     Module supports autoprobe and multiple bus-master chips (max 8).
 
index cf1e2e449107a281ca337ff65e6e0ae6978d4109..2d3e70ec2df418ffe81f2e62f00bf8e566be29fe 100644 (file)
@@ -15,6 +15,30 @@ config SND_ALI5451
        help
          Say 'Y' or 'M' to include support for ALI PCI Audio M5451 sound core.
 
+config SND_AU8810
+        tristate "Aureal Advantage"
+        depends on SND
+       select SND_MPU401_UART
+       select SND_AC97_CODEC
+        help
+          Say 'Y' or 'M' to include support for Aureal Advantage soundcards.
+config SND_AU8820
+        tristate "Aureal Vortex"
+        depends on SND
+       select SND_MPU401_UART
+       select SND_AC97_CODEC
+        help
+          Say 'Y' or 'M' to include support for Aureal Vortex soundcards.
+config SND_AU8830
+        tristate "Aureal Vortex 2"
+        depends on SND
+       select SND_MPU401_UART
+       select SND_AC97_CODEC
+        help
+          Say 'Y' or 'M' to include support for Aureal Vortex 2 soundcards.
 config SND_AZT3328
        tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)"
        depends on SND && EXPERIMENTAL
index adac4555569b526087bc53eb68cc46cdeb0ac0fc..ddff86193d6e45d60168c4ab28651114152feacd 100644 (file)
@@ -41,6 +41,7 @@ obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o
 obj-$(CONFIG_SND) += \
        ac97/ \
        ali5451/ \
+       au88x0/ \
        cs46xx/ \
        emu10k1/ \
        ice1712/ \
diff --git a/sound/pci/au88x0/Makefile b/sound/pci/au88x0/Makefile
new file mode 100644 (file)
index 0000000..d0a66bc
--- /dev/null
@@ -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 (file)
index 0000000..fce22c7
--- /dev/null
@@ -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 (file)
index 0000000..42a6093
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+    Aureal Advantage Soundcard driver.
+ */
+
+#define CHIP_AU8810
+
+#define CARD_NAME "Aureal Advantage 3D Sound Processor"
+#define CARD_NAME_SHORT "au8810"
+
+#ifndef PCI_VENDOR_ID_AUREAL
+#define PCI_VENDOR_ID_AUREAL 0x12eb
+#endif
+#ifndef PCI_VENDOR_ID_AUREAL_ADVANTAGE
+#define PCI_DEVICE_ID_AUREAL_ADVANTAGE 0x0003
+#endif
+
+#define hwread(x,y) readl((x)+((y)>>2))
+#define hwwrite(x,y,z) writel((z),(x)+((y)>>2))
+
+#define NR_ADB         0x20
+#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_SIZE (VORTEX_ADB_CHNBASE - VORTEX_ADB_RTBASE)
+#define VORTEX_ADB_CHNBASE 0x282b4
+#define VORTEX_ADB_CHNBASE_SIZE (ADB_MASK - VORTEX_ADB_RTBASE_SIZE)
+#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_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 + 0x50)       /* A3D blocks */
+#define ADB_A3DIN(x) (x + 0x70)
+#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_SRCBLOCK_SR     0x26cc0
+#define VORTEX_SRC_CHNBASE     0x26c40
+#define VORTEX_SRC_RTBASE      0x26c00
+#define VORTEX_SRC_SOURCE      0x26cc4
+#define VORTEX_SRC_SOURCESIZE 0x26cc8
+#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 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_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
+
+/* 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 (file)
index 0000000..b067af6
--- /dev/null
@@ -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,
+        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 (file)
index 0000000..a15b0fe
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+    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"
+
+#ifndef PCI_VENDOR_ID_AUREAL
+#define PCI_VENDOR_ID_AUREAL 0x12eb
+#endif
+
+#ifndef PCI_VENDOR_ID_AUREAL_VORTEX
+#define PCI_DEVICE_ID_AUREAL_VORTEX 0x0001
+#endif
+
+/* 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_SIZE (VORTEX_ADB_CHNBASE - VORTEX_ADB_RTBASE)
+#define VORTEX_ADB_CHNBASE 0x1099c
+#define VORTEX_ADB_CHNBASE_SIZE (ADB_MASK - VORTEX_ADB_RTBASE_SIZE)
+#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 (file)
index 0000000..010bad3
--- /dev/null
@@ -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_VORTEX2,
+        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 (file)
index 0000000..b129347
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+    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"
+
+#ifndef PCI_VENDOR_ID_AUREAL
+#define PCI_VENDOR_ID_AUREAL 0x12eb
+#endif
+#ifndef PCI_VENDOR_ID_AUREAL_VORTEX2
+#define PCI_DEVICE_ID_AUREAL_VORTEX2 0x0002
+#endif
+
+#define hwread(x,y) readl((x)+((y)>>2))
+#define hwwrite(x,y,z) writel((z),(x)+((y)>>2))
+
+#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 MSB, each pair 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_SIZE (VORTEX_ADB_CHNBASE - VORTEX_ADB_RTBASE)
+#define VORTEX_ADB_CHNBASE 0x282b4
+#define VORTEX_ADB_CHNBASE_SIZE (ADB_MASK - VORTEX_ADB_RTBASE_SIZE)
+#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  0x26cc4
+/* 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 (file)
index 0000000..278d348
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * 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 Muizelar 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>
+#define SNDRV_GET_ID
+#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_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(pcifix, "1-255i");
+MODULE_PARM_DESC(pcifix, "Enable VIA-workaround for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(pcifix,
+                  SNDRV_ENABLED
+                  ",allows:{{0,Disabled},{1,Latency},{2,Bridge},{3,Both},{255,Auto}},default:4,dialog:check");
+
+MODULE_DESCRIPTION("Aureal vortex");
+MODULE_CLASSES("{sound}");
+MODULE_LICENSE("GPL");
+MODULE_DEVICES("{{Aureal Semiconductor Inc., Aureal Vortex Sound Processor}}");
+
+#ifndef MODULE
+/* format is: snd-mychip=enable,index,id */
+static int __init alsa_card_vortex_setup(char *str)
+{
+       static unsigned __initdata nr_dev = 0;
+
+       if (nr_dev >= SNDRV_CARDS)
+               return 0;
+       (void)(get_option(&str, &enable[nr_dev]) == 2 &&
+              get_option(&str, &index[nr_dev]) == 2 &&
+              get_id(&str, &id[nr_dev]) == 2);
+       nr_dev++;
+       return 1;
+}
+
+__setup("snd-au88x0=", alsa_card_vortex_setup);
+#endif                         /* ifndef MODULE */
+
+MODULE_DEVICE_TABLE(pci, snd_vortex_ids);
+
+static void __devinit snd_vortex_workaround(struct pci_dev *vortex, int fix)
+{
+       struct pci_dev *via = NULL;
+       /* autodetect if workarounds are required */
+       while ((via = pci_find_device(PCI_VENDOR_ID_VIA,
+                                     PCI_DEVICE_ID_VIA_8365_1, via))) {
+               if (fix == 255) {
+                       printk(KERN_INFO CARD_NAME
+                              ": detected VIA KT133/KM133. activating workaround...\n");
+                       fix = 3;        // do latency and via bridge workaround
+               }
+               break;
+       }
+
+       /* do not do anything if autodetection was enabled and found no VIA */
+       if (fix == 255)
+               return;
+
+       int rc;
+
+       /* fix vortex latency */
+       if (fix & 0x01) {
+               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);
+               }
+       }
+
+       /* fix via agp bridge */
+       if (via && (fix & 0x02)) {
+               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);
+               }
+       }
+}
+
+// component-destructor
+// (see "Management of Cards and Components")
+static int snd_vortex_dev_free(snd_device_t * device)
+{
+       vortex_t *vortex = snd_magic_cast(vortex_t, device->device_data,
+                                         return -ENXIO);
+
+       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);
+       snd_magic_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 = snd_magic_kcalloc(vortex_t, 0, 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,
+                        (void *)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:
+       //FIXME: the type of chip->mmio might need to be changed??
+       iounmap((void *)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;
+       }
+       if ((err = vortex_gameport_register(chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+#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, "Aureal Vortex");
+       strcpy(card->shortname, CARD_NAME_SHORT);
+       sprintf(card->longname, "%s at 0x%lx irq %i",
+               card->shortname, chip->io, chip->irq);
+
+#ifdef CHIP_AU8830
+       {
+               unsigned char revision;
+               if ((err =
+                    pci_read_config_byte(pci, PCI_REVISION_ID,
+                                         &revision)) < 0) {
+                       snd_card_free(card);
+                       return err;
+               }
+
+               if (revision != 0xfe && revision != 0xfa) {
+                       printk(KERN_ALERT
+                              "vortex: The revision (%x) of your card has not been seen before.\n",
+                              revision);
+                       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, chip);
+       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)
+{
+       vortex_t *vortex = snd_magic_cast(vortex_t,
+                                         pci_get_drvdata(pci), return);
+
+       if (vortex) {
+               // Release ALSA stuff.
+               snd_card_free(vortex->card);
+               // Free Vortex struct.
+               pci_set_drvdata(pci, NULL);
+       } else
+               printk("snd_vortex_remove called more than one time!\n");
+}
+
+// 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)
+{
+       int err;
+
+       if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+               printk(KERN_ERR "Aureal soundcard not found "
+                      "or device busy\n");
+#endif
+               return err;
+       }
+       return 0;
+}
+
+// clean up the module
+static void __exit alsa_card_vortex_exit(void)
+{
+       pci_unregister_driver(&driver);
+}
+
+EXPORT_NO_SYMBOLS;
+
+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 (file)
index 0000000..a978f07
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+    Aureal Vortex Soundcard driver.
+
+    IO addr collected from asp4core.vxd:
+    function    address
+    0005D5A0    13004
+    00080674    14004
+    00080AFF    12818
+
+ */
+
+#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>
+
+/*
+#ifndef        PCI_VENDOR_ID_AUREAL
+#define        PCI_VENDOR_ID_AUREAL 0x12eb
+#endif
+#ifndef        PCI_VENDOR_ID_AUREAL_VORTEX
+#define        PCI_DEVICE_ID_AUREAL_VORTEX 0x0001
+#endif
+#ifndef        PCI_VENDOR_ID_AUREAL_VORTEX2
+#define        PCI_DEVICE_ID_AUREAL_VORTEX2 0x0002
+#endif
+#ifndef        PCI_VENDOR_ID_AUREAL_ADVANTAGE
+#define        PCI_DEVICE_ID_AUREAL_ADVANTAGE 0x0003
+#endif
+*/
+#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))
+
+/* 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 */
+       char mixcapt[2];
+       char mixplayb[4];
+#ifndef CHIP_AU8820
+       char mixspdif[2];
+       char mixa3d[2];         /* mixers which collect all a3d streams. */
+       char 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
+
+       /* Gameport stuff. */
+       struct gameport *gameport;
+
+       /* PCI hardware resources */
+       unsigned long io;
+       unsigned long *mmio;
+       unsigned int irq;
+       spinlock_t lock;
+
+       /* PCI device */
+       struct pci_dev *pci_dev;
+       u16 vendor;
+       u16 device;
+       u8 rev;
+};
+
+#define chip_t vortex_t
+
+/* 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);
+
+#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 int __devexit 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 (file)
index 0000000..f3399b3
--- /dev/null
@@ -0,0 +1,912 @@
+/***************************************************************************
+ *            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);
+}
+
+int Vort3DRend_Initialize(vortex_t * v, unsigned short mode)
+{
+       v->xt_mode = mode;      /* this_14 */
+
+       vortex_XtalkHw_init(v);
+       vortex_XtalkHw_SetGains(v, asXtalkGainsAllChan);
+       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;
+#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 = (a3dsrc_t*)(kcontrol->private_value);
+       /* 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 = (a3dsrc_t *) (kcontrol->private_value);
+       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 = (a3dsrc_t *) (kcontrol->private_value);
+       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 = (a3dsrc_t *) (kcontrol->private_value);
+       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 = (a3dsrc_t *) (kcontrol->private_value);
+       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",.index =
+           0,.access =
+           SNDRV_CTL_ELEM_ACCESS_READWRITE,.private_value =
+           0,.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)) == NULL)
+                       return -ENOMEM;
+               kcontrol->private_value = (int)&(vortex->a3d[i]);
+               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)) == NULL)
+                       return -ENOMEM;
+               kcontrol->private_value = (int)&(vortex->a3d[i]);
+               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)) == NULL)
+                       return -ENOMEM;
+               kcontrol->private_value = (int)&(vortex->a3d[i]);
+               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)) == NULL)
+                       return -ENOMEM;
+               kcontrol->private_value = (int)&(vortex->a3d[i]);
+               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 (file)
index 0000000..0584c65
--- /dev/null
@@ -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 (file)
index 0000000..6fab4bb
--- /dev/null
@@ -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 (file)
index 0000000..d91e91a
--- /dev/null
@@ -0,0 +1,2822 @@
+/*
+ *  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 Muizelar, 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.
+*/
+
+#include "au88x0.h"
+#include "au88x0_a3d.h"
+#include <linux/delay.h>
+
+/*  MIXER (CAsp4Mix.s and CAsp4Mixer.s) */
+
+// FIXME: get rid of this.
+int mchannels[NR_MIXIN];
+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 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;
+
+       psize--;
+
+       dma->cfg0 = 0;
+       dma->cfg1 = 0;
+       switch (count) {
+               /* Four or more pages */
+       default:
+       case 4:
+               dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | psize;
+               hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4),
+                       snd_sgbuf_get_addr(sgbuf, psize * 3));
+               /* 3 pages */
+       case 3:
+               dma->cfg0 |= 0x12000000;
+               dma->cfg1 |= 0x80000000 | 0x40000000 | (psize << 0xc);
+               hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4),
+                       snd_sgbuf_get_addr(sgbuf, psize * 2));
+               /* 2 pages */
+       case 2:
+               dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | psize;
+               hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4),
+                       snd_sgbuf_get_addr(sgbuf, psize));
+               /* 1 page */
+       case 1:
+               dma->cfg0 |= 0x80000000 | 0x40000000 | (psize << 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_SIZE; 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_SIZE; 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));
+       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 void vortex_connect_default(vortex_t * vortex, int en)
+{
+       // FIXME: check if checkout was succesful.
+       // 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).
+#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
+
+       /* Fast Play Workaround */
+#ifndef CHIP_AU8820
+       vortex->fixed_res[VORTEX_RESOURCE_DMA] = 0x00000001;
+#endif
+       // Channel swapping workaround. We are nuking registers somewhere, or
+       // its a hardware bug.
+       vortex->fixed_res[VORTEX_RESOURCE_SRC] = 0x00000001;
+}
+
+/*
+  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;
+
+       spin_lock(&vortex->lock);
+       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;
+
+       // FIXME: check for success of checkout or checkin.
+       /* 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], 
+                                                            src[0], 
+                                                            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[0], 
+                                                                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[nr_ch - 1],
+                                                    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[0], src[0],
+                                                        src[1], dma);
+               }
+       }
+       vortex->dma_adb[dma].nr_ch = nr_ch;
+       spin_unlock(&vortex->lock);
+
+#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 = snd_magic_cast(vortex_t, dev_id, return IRQ_NONE);
+       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. */
+               for (i = 0; i < NR_ADB; i++) {
+                       if (vortex->dma_adb[i].fifo_status == FIFO_START) {
+                               if (vortex_adbdma_bufshift(vortex, i)) ;
+                               snd_pcm_period_elapsed(vortex->dma_adb[i].
+                                                      substream);
+                       }
+               }
+#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)) ;
+                               snd_pcm_period_elapsed(vortex->dma_wt[i].
+                                                      substream);
+                       }
+               }
+#endif
+               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++) {
+               hwwrite(vortex->mmio, (VORTEX_CODEC_CHN + (i << 2)), 0);
+               udelay(2000);
+       }
+       if (0) {
+               hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x8068);
+               udelay(1000);
+               hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00e8);
+               udelay(1000);
+       } else {
+               hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00a8);
+               udelay(2000);
+               hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x80a8);
+               udelay(2000);
+               hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x80e8);
+               udelay(2000);
+               hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x80a8);
+               udelay(2000);
+               hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00a8);
+               udelay(2000);
+               hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00e8);
+       }
+       for (i = 0; i < 32; i++) {
+               hwwrite(vortex->mmio, (VORTEX_CODEC_CHN + (i << 2)), 0);
+               udelay(5000);
+       }
+       hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0xe8);
+       udelay(1000);
+       /* 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 long flags;
+       unsigned int lifeboat = 0;
+       spin_lock_irqsave(&card->lock, flags);
+
+       /* 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");
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       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);
+
+       spin_unlock_irqrestore(&card->lock, flags);
+}
+
+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 long flags;
+       unsigned lifeboat = 0;
+
+       spin_lock_irqsave(&card->lock, flags);
+
+       /* 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");
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       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 */
+       {
+               udelay(100);
+               data = hwread(card->mmio, VORTEX_CODEC_IO);
+               if (lifeboat++ > POLL_COUNT) {
+                       printk(KERN_ERR "vortex: ac97 address never arrived\n");
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       return 0xffff;
+               }
+       }
+       while ((data & VORTEX_CODEC_ADDMASK) !=
+              (addr << VORTEX_CODEC_ADDSHIFT)) ;
+
+       /* Unlock. */
+       spin_unlock_irqrestore(&card->lock, flags);
+
+       /* 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: hardware init.... ");
+       /* Hardware Init. */
+       hwwrite(vortex->mmio, VORTEX_CTRL, 0xffffffff);
+       udelay(5000);
+       hwwrite(vortex->mmio, VORTEX_CTRL,
+               hwread(vortex->mmio, VORTEX_CTRL) & 0xffdfffff);
+       udelay(5000);
+       /* 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: hardware 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);
+       udelay(5000);
+       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 (file)
index 0000000..b9fba01
--- /dev/null
@@ -0,0 +1,1001 @@
+/***************************************************************************
+ *            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"
+
+/* CEqHw.s */
+static void vortex_EqHw_SetTimeConsts(vortex_t * vortex, u16 a, u16 b)
+{
+       hwwrite(vortex->mmio, 0x2b3c4, a);
+       hwwrite(vortex->mmio, 0x2b3c8, b);
+}
+
+static void vortex_EqHw_SetLeftCoefs(vortex_t * vortex, u16 a[])
+{
+       eqhw_t *eqhw = &(vortex->eq.this04);
+       int eax, i = 0, n /*esp2c */  = 0;
+
+       if (eqhw->this04 <= n)
+               return;
+
+       do {
+               hwwrite(vortex->mmio, 0x2b000 + n * 0x30, a[i + 0]);
+               hwwrite(vortex->mmio, 0x2b004 + n * 0x30, a[i + 1]);
+
+               if (eqhw->this08 == 0) {
+                       hwwrite(vortex->mmio, 0x2b008 + n * 0x30, a[i + 2]);
+                       hwwrite(vortex->mmio, 0x2b00c + n * 0x30, a[i + 3]);
+                       eax = a[i + 4]; //esp24;
+               } else {
+                       if (a[2 + i] == 0x8000)
+                               eax = 0x7fff;
+                       else
+                               eax = ~a[2 + i];
+                       hwwrite(vortex->mmio, 0x2b008 + n * 0x30, eax & 0xffff);
+                       if (a[3 + i] == 0x8000)
+                               eax = 0x7fff;
+                       else
+                               eax = ~a[3 + i];
+                       hwwrite(vortex->mmio, 0x2b00c + n * 0x30, eax & 0xffff);
+                       if (a[4 + i] == 0x8000)
+                               eax = 0x7fff;
+                       else
+                               eax = ~a[4 + i];
+               }
+               hwwrite(vortex->mmio, 0x2b010 + n * 0x30, eax);
+
+               n++;
+               i += 5;
+       }
+       while (n < eqhw->this04);
+}
+
+static void vortex_EqHw_SetRightCoefs(vortex_t * vortex, u16 a[])
+{
+       eqhw_t *eqhw = &(vortex->eq.this04);
+       int i = 0, n /*esp2c */  = 0, eax;
+
+       if (eqhw->this04 <= n)
+               return;
+
+       do {
+               hwwrite(vortex->mmio, 0x2b1e0 + n * 0x30, a[0 + i]);
+               hwwrite(vortex->mmio, 0x2b1e4 + n * 0x30, a[1 + i]);
+
+               if (eqhw->this08 == 0) {
+                       hwwrite(vortex->mmio, 0x2b1e8 + n * 0x30, a[2 + i]);
+                       hwwrite(vortex->mmio, 0x2b1ec + n * 0x30, a[3 + i]);
+                       eax = a[4 + i]; //*esp24;
+               } else {
+                       if (a[2 + i] == 0x8000)
+                               eax = 0x7fff;
+                       else
+                               eax = ~(a[2 + i]);
+                       hwwrite(vortex->mmio, 0x2b1e8 + n * 0x30, eax & 0xffff);
+                       if (a[3 + i] == 0x8000)
+                               eax = 0x7fff;
+                       else
+                               eax = ~a[3 + i];
+                       hwwrite(vortex->mmio, 0x2b1ec + n * 0x30, eax & 0xffff);
+                       if (a[4 + i] == 0x8000)
+                               eax = 0x7fff;
+                       else
+                               eax = ~a[4 + i];
+               }
+               hwwrite(vortex->mmio, 0x2b1f0 + n * 0x30, eax);
+               i += 5;
+               n++;
+       }
+       while (n < eqhw->this04);
+
+}
+
+static void vortex_EqHw_SetLeftStates(vortex_t * vortex, u16 a[], u16 b[])
+{
+       eqhw_t *eqhw = &(vortex->eq.this04);
+       int i = 0, ebx = 0;
+
+       hwwrite(vortex->mmio, 0x2b3fc, a[0]);
+       hwwrite(vortex->mmio, 0x2b400, a[1]);
+
+       if (eqhw->this04 < 0)
+               return;
+
+       do {
+               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;
+               ebx++;
+       }
+       while (eqhw->this04 > ebx);
+}
+
+static void vortex_EqHw_SetRightStates(vortex_t * vortex, u16 a[], u16 b[])
+{
+       eqhw_t *eqhw = &(vortex->eq.this04);
+       int i = 0, ebx = 0;
+
+       hwwrite(vortex->mmio, 0x2b404, a[0]);
+       hwwrite(vortex->mmio, 0x2b408, a[1]);
+
+       if (eqhw->this04 < 0)
+               return;
+
+       do {
+               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;
+               ebx++;
+       }
+       while (ebx < eqhw->this04);
+}
+
+#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);
+       int eax;
+
+       if (eqhw->this08 == 0) {
+               hwwrite(vortex->mmio, 0x2b3d4, a);
+               hwwrite(vortex->mmio, 0x2b3ec, b);
+       } else {
+               if (a == 0x8000)
+                       eax = 0x7fff;
+               else
+                       eax = ~a;
+               hwwrite(vortex->mmio, 0x2b3d4, eax & 0xffff);
+               if (b == 0x8000)
+                       eax = 0x7fff;
+               else
+                       eax = ~b;
+               hwwrite(vortex->mmio, 0x2b3ec, eax & 0xffff);
+       }
+}
+
+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 = 0;
+
+       if (eqhw->this04 < 0)
+               return;
+       do {
+               hwwrite(vortex->mmio, 0x2b02c + ebx * 0x30, a[ebx]);
+               ebx++;
+       }
+       while (ebx < eqhw->this04);
+}
+
+static void vortex_EqHw_SetRightGainsTarget(vortex_t * vortex, u16 a[])
+{
+       eqhw_t *eqhw = &(vortex->eq.this04);
+       int ebx = 0;
+
+       if (eqhw->this04 < 0)
+               return;
+
+       do {
+               hwwrite(vortex->mmio, 0x2b20c + ebx * 0x30, a[ebx]);
+               ebx++;
+       }
+       while (ebx < eqhw->this04);
+}
+
+static void vortex_EqHw_SetLeftGainsCurrent(vortex_t * vortex, u16 a[])
+{
+       eqhw_t *eqhw = &(vortex->eq.this04);
+       int ebx = 0;
+
+       if (eqhw->this04 < 0)
+               return;
+
+       do {
+               hwwrite(vortex->mmio, 0x2b028 + ebx * 0x30, a[ebx]);
+               ebx++;
+       }
+       while (ebx < eqhw->this04);
+}
+
+static void vortex_EqHw_SetRightGainsCurrent(vortex_t * vortex, u16 a[])
+{
+       eqhw_t *eqhw = &(vortex->eq.this04);
+       int ebx = 0;
+
+       if (eqhw->this04 < 0)
+               return;
+
+       do {
+               hwwrite(vortex->mmio, 0x2b208 + ebx * 0x30, a[ebx]);
+               ebx++;
+       }
+       while (ebx < eqhw->this04);
+}
+
+#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 a[])
+{
+       eqhw_t *eqhw = &(vortex->eq.this04);
+       int ebx;
+
+       if (eqhw->this04 < 0)
+               return;
+
+       ebx = 0;
+       do {
+               hwwrite(vortex->mmio, 0x2b024 + ebx * 0x30, a[ebx]);
+               ebx++;
+       }
+       while (ebx < eqhw->this04);
+
+       hwwrite(vortex->mmio, 0x2b3cc, a[eqhw->this04]);
+       hwwrite(vortex->mmio, 0x2b3d8, a[eqhw->this04 + 1]);
+
+       ebx = 0;
+       do {
+               hwwrite(vortex->mmio, 0x2b204 + ebx * 0x30,
+                       a[ebx + (eqhw->this04 + 2)]);
+               ebx++;
+       }
+       while (ebx < eqhw->this04);
+
+       hwwrite(vortex->mmio, 0x2b3e4, a[2 + (eqhw->this04 * 2)]);
+       hwwrite(vortex->mmio, 0x2b3f0, a[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, 0x2b440, 0xf001);
+}
+
+static void vortex_EqHw_Disable(vortex_t * vortex)
+{
+       hwwrite(vortex->mmio, 0x2b440, 0xf000);
+}
+
+/* Reset (zero) buffers */
+static void vortex_EqHw_ZeroIO(vortex_t * vortex)
+{
+       int i;
+       for (i = 0; i < 0x8; i++)
+               hwwrite(vortex->mmio, 0x2b410 + (i << 2), 0x0);
+       for (i = 0; i < 0x4; i++)
+               hwwrite(vortex->mmio, 0x2b430 + (i << 2), 0x0);
+}
+
+static void vortex_EqHw_ZeroA3DIO(vortex_t * vortex)
+{
+       int i;
+       for (i = 0; i < 0x4; i++)
+               hwwrite(vortex->mmio, 0x2b410 + (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;
+
+       if (0 < count) {
+               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)) {
+               vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex);
+               vortex_EqHw_SetBypassGain(vortex, eq->this08, eq->this08);
+       } else {
+               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 (file)
index 0000000..e49bc62
--- /dev/null
@@ -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 (file)
index 0000000..abf8d6a
--- /dev/null
@@ -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 (file)
index 0000000..5c00a46
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * $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/init.h>
+#include <sound/core.h>
+#include "au88x0.h"
+#include <linux/gameport.h>
+
+#define VORTEX_GAME_DWAIT      20      /* 20 ms */
+
+static struct gameport gameport;
+
+static unsigned char vortex_game_read(struct gameport *gameport)
+{
+       vortex_t *vortex = gameport->driver;
+       return hwread(vortex->mmio, VORTEX_GAME_LEGACY);
+}
+
+static void vortex_game_trigger(struct gameport *gameport)
+{
+       vortex_t *vortex = gameport->driver;
+       hwwrite(vortex->mmio, VORTEX_GAME_LEGACY, 0xff);
+}
+
+static int
+vortex_game_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+       vortex_t *vortex = gameport->driver;
+       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->driver;
+
+       switch (mode) {
+       case GAMEPORT_MODE_COOKED:
+               hwwrite(vortex->mmio, VORTEX_CTRL2,
+                       hwread(vortex->mmio,
+                              VORTEX_CTRL2) | CTRL2_GAME_ADCMODE);
+               wait_ms(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 vortex_gameport_register(vortex_t * vortex)
+{
+       vortex->gameport = &gameport;
+
+       vortex->gameport->driver = vortex;
+       vortex->gameport->fuzz = 64;
+
+       vortex->gameport->read = vortex_game_read;
+       vortex->gameport->trigger = vortex_game_trigger;
+       vortex->gameport->cooked_read = vortex_game_cooked_read;
+       vortex->gameport->open = vortex_game_open;
+
+       gameport_register_port((struct gameport *)vortex->gameport);
+
+/*     printk(KERN_INFO "gameport%d: %s at speed %d kHz\n",
+               vortex->gameport->number, vortex->pci_dev->name, vortex->gameport->speed);
+*/
+       return 0;
+}
+
+static int vortex_gameport_unregister(vortex_t * vortex)
+{
+       if (vortex->gameport != NULL)
+               gameport_unregister_port(vortex->gameport);
+       return 0;
+}
diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c
new file mode 100644 (file)
index 0000000..c4f9413
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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 bus, *pbus;
+       ac97_t ac97;
+       int err;
+
+       memset(&bus, 0, sizeof(bus));
+       bus.write = vortex_codec_write;
+       bus.read = vortex_codec_read;
+       if ((err = snd_ac97_bus(vortex->card, &bus, &pbus)) < 0)
+               return err;
+       memset(&ac97, 0, sizeof(ac97));
+       // Intialize AC97 codec stuff.
+       ac97.private_data = vortex;
+       return snd_ac97_mixer(pbus, &ac97, &vortex->codec);
+}
diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c
new file mode 100644 (file)
index 0000000..3027c35
--- /dev/null
@@ -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 = snd_magic_cast(mpu401_t, rmidi->private_data, return -ENOMEM);
+       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 (file)
index 0000000..8596d1d
--- /dev/null
@@ -0,0 +1,509 @@
+/*
+ * Vortex PCM ALSA driver.
+ *
+ * Supports ADB and WT DMA. Unfortunately, WT routing is still a
+ * mistery. To discover that, we need to disassemble the windoze
+ * driver too.
+ *
+ *
+ */
+
+#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 chip_t vortex_t
+#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)
+{
+       chip_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));
+        */
+       // 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->substream = substream;
+               vortex_wtdma_setbuffers(chip, substream->number, sgbuf,
+                                       params_period_bytes(hw_params),
+                                       params_periods(hw_params));
+       }
+#endif
+       return 0;
+}
+
+/* hw_free callback */
+static int snd_vortex_pcm_hw_free(snd_pcm_substream_t * substream)
+{
+       chip_t *chip = snd_pcm_substream_chip(substream);
+       stream_t *stream = (stream_t *) (substream->runtime->private_data);
+
+       // 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;
+
+       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);
+       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
+       return 0;
+}
+
+/* trigger callback */
+static int snd_vortex_pcm_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+       chip_t *chip = snd_pcm_substream_chip(substream);
+       stream_t *stream = (stream_t *) substream->runtime->private_data;
+       int dma = stream->dma;
+
+       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_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:
+               return -EINVAL;
+       }
+       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)
+{
+       static char *texts[] = { "32000", "44100", "48000" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 3;
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                   uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       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);
+
+       if (vortex->spdif_sr == 32000)
+               ucontrol->value.enumerated.item[0] = 0;
+       if (vortex->spdif_sr == 44100)
+               ucontrol->value.enumerated.item[0] = 1;
+       if (vortex->spdif_sr == 48000)
+               ucontrol->value.enumerated.item[0] = 2;
+       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);
+       static unsigned int sr[3] = { 32000, 44100, 48000 };
+
+       //printk("vortex: spdif sr = %d\n", ucontrol->value.enumerated.item[0]);
+       vortex->spdif_sr = sr[ucontrol->value.enumerated.item[0] % 3];
+       vortex_spdif_init(vortex,
+                         sr[ucontrol->value.enumerated.item[0] % 3], 1);
+       return 1;
+}
+static snd_kcontrol_new_t vortex_spdif_kcontrol __devinitdata = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "SPDIF SR",
+       .index = 0,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .private_value = 0,
+       .info = snd_vortex_spdif_info,
+       .get = snd_vortex_spdif_get,
+       .put = snd_vortex_spdif_put
+};
+
+/* create a pcm device */
+static int __devinit snd_vortex_new_pcm(vortex_t * chip, int idx, int nr)
+{
+       snd_pcm_t *pcm;
+       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 linear buffers */
+       //snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+       //                                    snd_dma_pci_data(chip->pci_dev), 0x10000, 0x10000);
+       /* 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) {
+               snd_kcontrol_t *kcontrol;
+
+               if ((kcontrol =
+                    snd_ctl_new1(&vortex_spdif_kcontrol, chip)) == NULL)
+                       return -ENOMEM;
+               if ((err = snd_ctl_add(chip->card, kcontrol)) < 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 (file)
index 0000000..5a4d8fc
--- /dev/null
@@ -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 (file)
index 0000000..5c2bb32
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ *  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 */
+
+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);
+}
+
+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);
+}
+
+// WT routing is still a mistery.
+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, 2);
+               vortex_fifo_setwtvalid(vortex, wt, 1);
+               vortex_wt_setstereo(vortex, wt, nr_ch - 1);
+       } else
+               vortex_fifo_setwtvalid(vortex, wt, 0);
+
+       vortex_wt_setdsout(vortex, wt, 0);
+       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 (file)
index 0000000..d536c88
--- /dev/null
@@ -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 (file)
index 0000000..9c0700c
--- /dev/null
@@ -0,0 +1,781 @@
+/***************************************************************************
+ *            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]);
+       }
+}
+
+#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 (file)
index 0000000..832f62f
--- /dev/null
@@ -0,0 +1,62 @@
+/***************************************************************************
+ *            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];
+
+extern xtalk_gains_t const asXtalkGainsAllChan;
+
+static void vortex_XtalkHw_SetGains(vortex_t * vortex,
+                                   xtalk_gains_t const gains);
+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 */