summaryrefslogtreecommitdiffstats
path: root/sound/pci/au88x0
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sound/pci/au88x0/Makefile8
-rw-r--r--sound/pci/au88x0/au8810.c17
-rw-r--r--sound/pci/au88x0/au8810.h225
-rw-r--r--sound/pci/au88x0/au8820.c15
-rw-r--r--sound/pci/au88x0/au8820.h205
-rw-r--r--sound/pci/au88x0/au8830.c18
-rw-r--r--sound/pci/au88x0/au8830.h252
-rw-r--r--sound/pci/au88x0/au88x0.c326
-rw-r--r--sound/pci/au88x0/au88x0.h281
-rw-r--r--sound/pci/au88x0/au88x0_a3d.c906
-rw-r--r--sound/pci/au88x0/au88x0_a3d.h111
-rw-r--r--sound/pci/au88x0/au88x0_a3ddata.c79
-rw-r--r--sound/pci/au88x0/au88x0_core.c2862
-rw-r--r--sound/pci/au88x0/au88x0_eq.c922
-rw-r--r--sound/pci/au88x0/au88x0_eq.h44
-rw-r--r--sound/pci/au88x0/au88x0_eqdata.c117
-rw-r--r--sound/pci/au88x0/au88x0_game.c121
-rw-r--r--sound/pci/au88x0/au88x0_mixer.c45
-rw-r--r--sound/pci/au88x0/au88x0_mpu401.c98
-rw-r--r--sound/pci/au88x0/au88x0_pcm.c682
-rw-r--r--sound/pci/au88x0/au88x0_synth.c400
-rw-r--r--sound/pci/au88x0/au88x0_wt.h66
-rw-r--r--sound/pci/au88x0/au88x0_xtalk.c783
-rw-r--r--sound/pci/au88x0/au88x0_xtalk.h49
24 files changed, 8632 insertions, 0 deletions
diff --git a/sound/pci/au88x0/Makefile b/sound/pci/au88x0/Makefile
new file mode 100644
index 000000000..78ab11562
--- /dev/null
+++ b/sound/pci/au88x0/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-au8810-objs := au8810.o
+snd-au8820-objs := au8820.o
+snd-au8830-objs := au8830.o
+
+obj-$(CONFIG_SND_AU8810) += snd-au8810.o
+obj-$(CONFIG_SND_AU8820) += snd-au8820.o
+obj-$(CONFIG_SND_AU8830) += snd-au8830.o
diff --git a/sound/pci/au88x0/au8810.c b/sound/pci/au88x0/au8810.c
new file mode 100644
index 000000000..b2bfa50bf
--- /dev/null
+++ b/sound/pci/au88x0/au8810.c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "au8810.h"
+#include "au88x0.h"
+static const struct pci_device_id snd_vortex_ids[] = {
+ {PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_ADVANTAGE), 1,},
+ {0,}
+};
+
+#include "au88x0_core.c"
+#include "au88x0_pcm.c"
+#include "au88x0_mixer.c"
+#include "au88x0_mpu401.c"
+#include "au88x0_game.c"
+#include "au88x0_eq.c"
+#include "au88x0_a3d.c"
+#include "au88x0_xtalk.c"
+#include "au88x0.c"
diff --git a/sound/pci/au88x0/au8810.h b/sound/pci/au88x0/au8810.h
new file mode 100644
index 000000000..94f110320
--- /dev/null
+++ b/sound/pci/au88x0/au8810.h
@@ -0,0 +1,225 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ Aureal Advantage Soundcard driver.
+ */
+
+#define CHIP_AU8810
+
+#define CARD_NAME "Aureal Advantage"
+#define CARD_NAME_SHORT "au8810"
+
+#define NR_ADB 0x10
+#define NR_WT 0x00
+#define NR_SRC 0x10
+#define NR_A3D 0x10
+#define NR_MIXIN 0x20
+#define NR_MIXOUT 0x10
+
+
+/* ADBDMA */
+#define VORTEX_ADBDMA_STAT 0x27e00 /* read only, subbuffer, DMA pos */
+#define POS_MASK 0x00000fff
+#define POS_SHIFT 0x0
+#define ADB_SUBBUF_MASK 0x00003000 /* ADB only. */
+#define ADB_SUBBUF_SHIFT 0xc /* ADB only. */
+#define VORTEX_ADBDMA_CTRL 0x27180 /* write only; format, flags, DMA pos */
+#define OFFSET_MASK 0x00000fff
+#define OFFSET_SHIFT 0x0
+#define IE_MASK 0x00001000 /* interrupt enable. */
+#define IE_SHIFT 0xc
+#define DIR_MASK 0x00002000 /* Direction */
+#define DIR_SHIFT 0xd
+#define FMT_MASK 0x0003c000
+#define FMT_SHIFT 0xe
+// The ADB masks and shift also are valid for the wtdma, except if specified otherwise.
+#define VORTEX_ADBDMA_BUFCFG0 0x27100
+#define VORTEX_ADBDMA_BUFCFG1 0x27104
+#define VORTEX_ADBDMA_BUFBASE 0x27000
+#define VORTEX_ADBDMA_START 0x27c00 /* Which subbuffer starts */
+
+#define VORTEX_ADBDMA_STATUS 0x27A90 /* stored at AdbDma->this_10 / 2 DWORD in size. */
+
+/* WTDMA */
+#define VORTEX_WTDMA_CTRL 0x27fd8 /* format, DMA pos */
+#define VORTEX_WTDMA_STAT 0x27fe8 /* DMA subbuf, DMA pos */
+#define WT_SUBBUF_MASK 0x3
+#define WT_SUBBUF_SHIFT 0xc
+#define VORTEX_WTDMA_BUFBASE 0x27fc0
+#define VORTEX_WTDMA_BUFCFG0 0x27fd0
+#define VORTEX_WTDMA_BUFCFG1 0x27fd4
+#define VORTEX_WTDMA_START 0x27fe4 /* which subbuffer is first */
+
+/* ADB */
+#define VORTEX_ADB_SR 0x28400 /* Samplerates enable/disable */
+#define VORTEX_ADB_RTBASE 0x28000
+#define VORTEX_ADB_RTBASE_COUNT 173
+#define VORTEX_ADB_CHNBASE 0x282b4
+#define VORTEX_ADB_CHNBASE_COUNT 24
+#define ROUTE_MASK 0xffff
+#define SOURCE_MASK 0xff00
+#define ADB_MASK 0xff
+#define ADB_SHIFT 0x8
+
+/* ADB address */
+#define OFFSET_ADBDMA 0x00
+#define OFFSET_SRCIN 0x40
+#define OFFSET_SRCOUT 0x20
+#define OFFSET_MIXIN 0x50
+#define OFFSET_MIXOUT 0x30
+#define OFFSET_CODECIN 0x70
+#define OFFSET_CODECOUT 0x88
+#define OFFSET_SPORTIN 0x78 /* ch 0x13 */
+#define OFFSET_SPORTOUT 0x90
+#define OFFSET_SPDIFOUT 0x92 /* ch 0x14 check this! */
+#define OFFSET_EQIN 0xa0
+#define OFFSET_EQOUT 0x7e /* 2 routes on ch 0x11 */
+#define OFFSET_XTALKOUT 0x66 /* crosstalk canceller (source) */
+#define OFFSET_XTALKIN 0x96 /* crosstalk canceller (sink) */
+#define OFFSET_A3DIN 0x70 /* ADB sink. */
+#define OFFSET_A3DOUT 0xA6 /* ADB source. 2 routes per slice = 8 */
+#define OFFSET_EFXIN 0x80 /* ADB sink. */
+#define OFFSET_EFXOUT 0x68 /* ADB source. */
+
+/* ADB route translate helper */
+#define ADB_DMA(x) (x)
+#define ADB_SRCOUT(x) (x + OFFSET_SRCOUT)
+#define ADB_SRCIN(x) (x + OFFSET_SRCIN)
+#define ADB_MIXOUT(x) (x + OFFSET_MIXOUT)
+#define ADB_MIXIN(x) (x + OFFSET_MIXIN)
+#define ADB_CODECIN(x) (x + OFFSET_CODECIN)
+#define ADB_CODECOUT(x) (x + OFFSET_CODECOUT)
+#define ADB_SPORTIN(x) (x + OFFSET_SPORTIN)
+#define ADB_SPORTOUT(x) (x + OFFSET_SPORTOUT)
+#define ADB_SPDIFOUT(x) (x + OFFSET_SPDIFOUT)
+#define ADB_EQIN(x) (x + OFFSET_EQIN)
+#define ADB_EQOUT(x) (x + OFFSET_EQOUT)
+#define ADB_A3DOUT(x) (x + OFFSET_A3DOUT) /* 0x10 A3D blocks */
+#define ADB_A3DIN(x) (x + OFFSET_A3DIN)
+#define ADB_XTALKIN(x) (x + OFFSET_XTALKIN)
+#define ADB_XTALKOUT(x) (x + OFFSET_XTALKOUT)
+
+#define MIX_OUTL 0xe
+#define MIX_OUTR 0xf
+#define MIX_INL 0x1e
+#define MIX_INR 0x1f
+#define MIX_DEFIGAIN 0x08 /* 0x8 => 6dB */
+#define MIX_DEFOGAIN 0x08
+
+/* MIXER */
+#define VORTEX_MIXER_SR 0x21f00
+#define VORTEX_MIXER_CLIP 0x21f80
+#define VORTEX_MIXER_CHNBASE 0x21e40
+#define VORTEX_MIXER_RTBASE 0x21e00
+#define MIXER_RTBASE_SIZE 0x38
+#define VORTEX_MIX_ENIN 0x21a00 /* Input enable bits. 4 bits wide. */
+#define VORTEX_MIX_SMP 0x21c00 /* AU8820: 0x9c00 */
+
+/* MIX */
+#define VORTEX_MIX_INVOL_A 0x21000 /* in? */
+#define VORTEX_MIX_INVOL_B 0x20000 /* out? */
+#define VORTEX_MIX_VOL_A 0x21800
+#define VORTEX_MIX_VOL_B 0x20800
+
+#define VOL_MIN 0x80 /* Input volume when muted. */
+#define VOL_MAX 0x7f /* FIXME: Not confirmed! Just guessed. */
+
+/* SRC */
+#define VORTEX_SRC_CHNBASE 0x26c40
+#define VORTEX_SRC_RTBASE 0x26c00
+#define VORTEX_SRCBLOCK_SR 0x26cc0
+#define VORTEX_SRC_SOURCE 0x26cc4
+#define VORTEX_SRC_SOURCESIZE 0x26cc8
+/* Params
+ 0x26e00 : 1 U0
+ 0x26e40 : 2 CR
+ 0x26e80 : 3 U3
+ 0x26ec0 : 4 DRIFT1
+ 0x26f00 : 5 U1
+ 0x26f40 : 6 DRIFT2
+ 0x26f80 : 7 U2 : Target rate, direction
+*/
+
+#define VORTEX_SRC_CONVRATIO 0x26e40
+#define VORTEX_SRC_DRIFT0 0x26e80
+#define VORTEX_SRC_DRIFT1 0x26ec0
+#define VORTEX_SRC_DRIFT2 0x26f40
+#define VORTEX_SRC_U0 0x26e00
+#define U0_SLOWLOCK 0x200
+#define VORTEX_SRC_U1 0x26f00
+#define VORTEX_SRC_U2 0x26f80
+#define VORTEX_SRC_DATA 0x26800 /* 0xc800 */
+#define VORTEX_SRC_DATA0 0x26000
+
+/* FIFO */
+#define VORTEX_FIFO_ADBCTRL 0x16100 /* Control bits. */
+#define VORTEX_FIFO_WTCTRL 0x16000
+#define FIFO_RDONLY 0x00000001
+#define FIFO_CTRL 0x00000002 /* Allow ctrl. ? */
+#define FIFO_VALID 0x00000010
+#define FIFO_EMPTY 0x00000020
+#define FIFO_U0 0x00001000 /* Unknown. */
+#define FIFO_U1 0x00010000
+#define FIFO_SIZE_BITS 5
+#define FIFO_SIZE (1<<FIFO_SIZE_BITS) // 0x20
+#define FIFO_MASK (FIFO_SIZE-1) //0x1f /* at shift left 0xc */
+//#define FIFO_MASK 0x1f /* at shift left 0xb */
+//#define FIFO_SIZE 0x20
+#define FIFO_BITS 0x03880000
+#define VORTEX_FIFO_ADBDATA 0x14000
+#define VORTEX_FIFO_WTDATA 0x10000
+
+/* CODEC */
+#define VORTEX_CODEC_CTRL 0x29184
+#define VORTEX_CODEC_EN 0x29190
+#define EN_CODEC0 0x00000300
+#define EN_AC98 0x00000c00 /* Modem AC98 slots. */
+#define EN_CODEC1 0x00003000
+#define EN_CODEC (EN_CODEC0 | EN_CODEC1)
+#define EN_SPORT 0x00030000
+#define EN_SPDIF 0x000c0000
+
+#define VORTEX_CODEC_CHN 0x29080
+#define VORTEX_CODEC_IO 0x29188
+
+/* SPDIF */
+#define VORTEX_SPDIF_FLAGS 0x2205c
+#define VORTEX_SPDIF_CFG0 0x291D0
+#define VORTEX_SPDIF_CFG1 0x291D4
+#define VORTEX_SPDIF_SMPRATE 0x29194
+
+/* Sample timer */
+#define VORTEX_SMP_TIME 0x29198
+
+#define VORTEX_MODEM_CTRL 0x291ac
+
+/* IRQ */
+#define VORTEX_IRQ_SOURCE 0x2a000 /* Interrupt source flags. */
+#define VORTEX_IRQ_CTRL 0x2a004 /* Interrupt source mask. */
+
+#define VORTEX_STAT 0x2a008 /* Status */
+
+#define VORTEX_CTRL 0x2a00c
+#define CTRL_MIDI_EN 0x00000001
+#define CTRL_MIDI_PORT 0x00000060
+#define CTRL_GAME_EN 0x00000008
+#define CTRL_GAME_PORT 0x00000e00
+//#define CTRL_IRQ_ENABLE 0x01004000
+#define CTRL_IRQ_ENABLE 0x00004000
+
+/* write: Timer period config / read: TIMER IRQ ack. */
+#define VORTEX_IRQ_STAT 0x2919c
+
+/* DMA */
+#define VORTEX_ENGINE_CTRL 0x27ae8
+#define ENGINE_INIT 0x1380000
+
+/* MIDI *//* GAME. */
+#define VORTEX_MIDI_DATA 0x28800
+#define VORTEX_MIDI_CMD 0x28804 /* Write command / Read status */
+
+#define VORTEX_CTRL2 0x2880c
+#define CTRL2_GAME_ADCMODE 0x40
+#define VORTEX_GAME_LEGACY 0x28808
+#define VORTEX_GAME_AXIS 0x28810
+#define AXIS_SIZE 4
+#define AXIS_RANGE 0x1fff
diff --git a/sound/pci/au88x0/au8820.c b/sound/pci/au88x0/au8820.c
new file mode 100644
index 000000000..dbc2263b4
--- /dev/null
+++ b/sound/pci/au88x0/au8820.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "au8820.h"
+#include "au88x0.h"
+static const struct pci_device_id snd_vortex_ids[] = {
+ {PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_1), 0,},
+ {0,}
+};
+
+#include "au88x0_synth.c"
+#include "au88x0_core.c"
+#include "au88x0_pcm.c"
+#include "au88x0_mpu401.c"
+#include "au88x0_game.c"
+#include "au88x0_mixer.c"
+#include "au88x0.c"
diff --git a/sound/pci/au88x0/au8820.h b/sound/pci/au88x0/au8820.h
new file mode 100644
index 000000000..8a128e8fe
--- /dev/null
+++ b/sound/pci/au88x0/au8820.h
@@ -0,0 +1,205 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ 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"
+#define CARD_NAME_SHORT "au8820"
+
+/* Number of ADB and WT channels */
+#define NR_ADB 0x10
+#define NR_WT 0x20
+#define NR_SRC 0x10
+#define NR_A3D 0x00
+#define NR_MIXIN 0x10
+#define NR_MIXOUT 0x10
+
+
+/* ADBDMA */
+#define VORTEX_ADBDMA_STAT 0x105c0 /* read only, subbuffer, DMA pos */
+#define POS_MASK 0x00000fff
+#define POS_SHIFT 0x0
+#define ADB_SUBBUF_MASK 0x00003000 /* ADB only. */
+#define ADB_SUBBUF_SHIFT 0xc /* ADB only. */
+#define VORTEX_ADBDMA_CTRL 0x10580 /* write only, format, flags, DMA pos */
+#define OFFSET_MASK 0x00000fff
+#define OFFSET_SHIFT 0x0
+#define IE_MASK 0x00001000 /* interrupt enable. */
+#define IE_SHIFT 0xc
+#define DIR_MASK 0x00002000 /* Direction. */
+#define DIR_SHIFT 0xd
+#define FMT_MASK 0x0003c000
+#define FMT_SHIFT 0xe
+// The masks and shift also work for the wtdma, if not specified otherwise.
+#define VORTEX_ADBDMA_BUFCFG0 0x10400
+#define VORTEX_ADBDMA_BUFCFG1 0x10404
+#define VORTEX_ADBDMA_BUFBASE 0x10200
+#define VORTEX_ADBDMA_START 0x106c0 /* Which subbuffer starts */
+#define VORTEX_ADBDMA_STATUS 0x10600 /* stored at AdbDma->this_10 / 2 DWORD in size. */
+
+/* ADB */
+#define VORTEX_ADB_SR 0x10a00 /* Samplerates enable/disable */
+#define VORTEX_ADB_RTBASE 0x10800
+#define VORTEX_ADB_RTBASE_COUNT 103
+#define VORTEX_ADB_CHNBASE 0x1099c
+#define VORTEX_ADB_CHNBASE_COUNT 22
+#define ROUTE_MASK 0x3fff
+#define ADB_MASK 0x7f
+#define ADB_SHIFT 0x7
+//#define ADB_MIX_MASK 0xf
+/* ADB address */
+#define OFFSET_ADBDMA 0x00
+#define OFFSET_SRCOUT 0x10 /* on channel 0x11 */
+#define OFFSET_SRCIN 0x10 /* on channel < 0x11 */
+#define OFFSET_MIXOUT 0x20 /* source */
+#define OFFSET_MIXIN 0x30 /* sink */
+#define OFFSET_CODECIN 0x48 /* ADB source */
+#define OFFSET_CODECOUT 0x58 /* ADB sink/target */
+#define OFFSET_SPORTOUT 0x60 /* sink */
+#define OFFSET_SPORTIN 0x50 /* source */
+#define OFFSET_EFXOUT 0x50 /* sink */
+#define OFFSET_EFXIN 0x40 /* source */
+#define OFFSET_A3DOUT 0x00 /* This card has no HRTF :( */
+#define OFFSET_A3DIN 0x00
+#define OFFSET_WTOUT 0x58 /* */
+
+/* ADB route translate helper */
+#define ADB_DMA(x) (x + OFFSET_ADBDMA)
+#define ADB_SRCOUT(x) (x + OFFSET_SRCOUT)
+#define ADB_SRCIN(x) (x + OFFSET_SRCIN)
+#define ADB_MIXOUT(x) (x + OFFSET_MIXOUT)
+#define ADB_MIXIN(x) (x + OFFSET_MIXIN)
+#define ADB_CODECIN(x) (x + OFFSET_CODECIN)
+#define ADB_CODECOUT(x) (x + OFFSET_CODECOUT)
+#define ADB_SPORTOUT(x) (x + OFFSET_SPORTOUT)
+#define ADB_SPORTIN(x) (x + OFFSET_SPORTIN) /* */
+#define ADB_A3DOUT(x) (x + OFFSET_A3DOUT) /* 8 A3D blocks */
+#define ADB_A3DIN(x) (x + OFFSET_A3DIN)
+#define ADB_WTOUT(x,y) (y + OFFSET_WTOUT)
+
+/* WTDMA */
+#define VORTEX_WTDMA_CTRL 0x10500 /* format, DMA pos */
+#define VORTEX_WTDMA_STAT 0x10500 /* DMA subbuf, DMA pos */
+#define WT_SUBBUF_MASK (0x3 << WT_SUBBUF_SHIFT)
+#define WT_SUBBUF_SHIFT 0x15
+#define VORTEX_WTDMA_BUFBASE 0x10000
+#define VORTEX_WTDMA_BUFCFG0 0x10300
+#define VORTEX_WTDMA_BUFCFG1 0x10304
+#define VORTEX_WTDMA_START 0x10640 /* which subbuffer is first */
+
+#define VORTEX_WT_BASE 0x9000
+
+/* MIXER */
+#define VORTEX_MIXER_SR 0x9f00
+#define VORTEX_MIXER_CLIP 0x9f80
+#define VORTEX_MIXER_CHNBASE 0x9e40
+#define VORTEX_MIXER_RTBASE 0x9e00
+#define MIXER_RTBASE_SIZE 0x26
+#define VORTEX_MIX_ENIN 0x9a00 /* Input enable bits. 4 bits wide. */
+#define VORTEX_MIX_SMP 0x9c00
+
+/* MIX */
+#define VORTEX_MIX_INVOL_A 0x9000 /* in? */
+#define VORTEX_MIX_INVOL_B 0x8000 /* out? */
+#define VORTEX_MIX_VOL_A 0x9800
+#define VORTEX_MIX_VOL_B 0x8800
+
+#define VOL_MIN 0x80 /* Input volume when muted. */
+#define VOL_MAX 0x7f /* FIXME: Not confirmed! Just guessed. */
+
+//#define MIX_OUTL 0xe
+//#define MIX_OUTR 0xf
+//#define MIX_INL 0xe
+//#define MIX_INR 0xf
+#define MIX_DEFIGAIN 0x08 /* 0x8 => 6dB */
+#define MIX_DEFOGAIN 0x08
+
+/* SRC */
+#define VORTEX_SRCBLOCK_SR 0xccc0
+#define VORTEX_SRC_CHNBASE 0xcc40
+#define VORTEX_SRC_RTBASE 0xcc00
+#define VORTEX_SRC_SOURCE 0xccc4
+#define VORTEX_SRC_SOURCESIZE 0xccc8
+#define VORTEX_SRC_U0 0xce00
+#define VORTEX_SRC_DRIFT0 0xce80
+#define VORTEX_SRC_DRIFT1 0xcec0
+#define VORTEX_SRC_U1 0xcf00
+#define VORTEX_SRC_DRIFT2 0xcf40
+#define VORTEX_SRC_U2 0xcf80
+#define VORTEX_SRC_DATA 0xc800
+#define VORTEX_SRC_DATA0 0xc000
+#define VORTEX_SRC_CONVRATIO 0xce40
+//#define SRC_RATIO(x) ((((x<<15)/48000) + 1)/2) /* Playback */
+//#define SRC_RATIO2(x) ((((48000<<15)/x) + 1)/2) /* Recording */
+
+/* FIFO */
+#define VORTEX_FIFO_ADBCTRL 0xf800 /* Control bits. */
+#define VORTEX_FIFO_WTCTRL 0xf840
+#define FIFO_RDONLY 0x00000001
+#define FIFO_CTRL 0x00000002 /* Allow ctrl. ? */
+#define FIFO_VALID 0x00000010
+#define FIFO_EMPTY 0x00000020
+#define FIFO_U0 0x00001000 /* Unknown. */
+#define FIFO_U1 0x00010000
+#define FIFO_SIZE_BITS 5
+#define FIFO_SIZE (1<<FIFO_SIZE_BITS) // 0x20
+#define FIFO_MASK (FIFO_SIZE-1) //0x1f /* at shift left 0xc */
+#define VORTEX_FIFO_ADBDATA 0xe000
+#define VORTEX_FIFO_WTDATA 0xe800
+
+/* CODEC */
+#define VORTEX_CODEC_CTRL 0x11984
+#define VORTEX_CODEC_EN 0x11990
+#define EN_CODEC 0x00000300
+#define EN_SPORT 0x00030000
+#define EN_SPDIF 0x000c0000
+#define VORTEX_CODEC_CHN 0x11880
+#define VORTEX_CODEC_IO 0x11988
+
+#define VORTEX_SPDIF_FLAGS 0x1005c /* FIXME */
+#define VORTEX_SPDIF_CFG0 0x119D0
+#define VORTEX_SPDIF_CFG1 0x119D4
+#define VORTEX_SPDIF_SMPRATE 0x11994
+
+/* Sample timer */
+#define VORTEX_SMP_TIME 0x11998
+
+/* IRQ */
+#define VORTEX_IRQ_SOURCE 0x12800 /* Interrupt source flags. */
+#define VORTEX_IRQ_CTRL 0x12804 /* Interrupt source mask. */
+
+#define VORTEX_STAT 0x12808 /* ?? */
+
+#define VORTEX_CTRL 0x1280c
+#define CTRL_MIDI_EN 0x00000001
+#define CTRL_MIDI_PORT 0x00000060
+#define CTRL_GAME_EN 0x00000008
+#define CTRL_GAME_PORT 0x00000e00
+#define CTRL_IRQ_ENABLE 0x4000
+
+/* write: Timer period config / read: TIMER IRQ ack. */
+#define VORTEX_IRQ_STAT 0x1199c
+
+/* DMA */
+#define VORTEX_DMA_BUFFER 0x10200
+#define VORTEX_ENGINE_CTRL 0x1060c
+#define ENGINE_INIT 0x0L
+
+ /* MIDI *//* GAME. */
+#define VORTEX_MIDI_DATA 0x11000
+#define VORTEX_MIDI_CMD 0x11004 /* Write command / Read status */
+#define VORTEX_GAME_LEGACY 0x11008
+#define VORTEX_CTRL2 0x1100c
+#define CTRL2_GAME_ADCMODE 0x40
+#define VORTEX_GAME_AXIS 0x11010
+#define AXIS_SIZE 4
+#define AXIS_RANGE 0x1fff
diff --git a/sound/pci/au88x0/au8830.c b/sound/pci/au88x0/au8830.c
new file mode 100644
index 000000000..e963c4e2f
--- /dev/null
+++ b/sound/pci/au88x0/au8830.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "au8830.h"
+#include "au88x0.h"
+static const struct pci_device_id snd_vortex_ids[] = {
+ {PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_2), 0,},
+ {0,}
+};
+
+#include "au88x0_synth.c"
+#include "au88x0_core.c"
+#include "au88x0_pcm.c"
+#include "au88x0_mixer.c"
+#include "au88x0_mpu401.c"
+#include "au88x0_game.c"
+#include "au88x0_eq.c"
+#include "au88x0_a3d.c"
+#include "au88x0_xtalk.c"
+#include "au88x0.c"
diff --git a/sound/pci/au88x0/au8830.h b/sound/pci/au88x0/au8830.h
new file mode 100644
index 000000000..40f671ffd
--- /dev/null
+++ b/sound/pci/au88x0/au8830.h
@@ -0,0 +1,252 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ 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"
+#define CARD_NAME_SHORT "au8830"
+
+#define NR_ADB 0x20
+#define NR_SRC 0x10
+#define NR_A3D 0x10
+#define NR_MIXIN 0x20
+#define NR_MIXOUT 0x10
+#define NR_WT 0x40
+
+/* ADBDMA */
+#define VORTEX_ADBDMA_STAT 0x27e00 /* read only, subbuffer, DMA pos */
+#define POS_MASK 0x00000fff
+#define POS_SHIFT 0x0
+#define ADB_SUBBUF_MASK 0x00003000 /* ADB only. */
+#define ADB_SUBBUF_SHIFT 0xc /* ADB only. */
+#define VORTEX_ADBDMA_CTRL 0x27a00 /* write only; format, flags, DMA pos */
+#define OFFSET_MASK 0x00000fff
+#define OFFSET_SHIFT 0x0
+#define IE_MASK 0x00001000 /* interrupt enable. */
+#define IE_SHIFT 0xc
+#define DIR_MASK 0x00002000 /* Direction. */
+#define DIR_SHIFT 0xd
+#define FMT_MASK 0x0003c000
+#define FMT_SHIFT 0xe
+#define ADB_FIFO_EN_SHIFT 0x15
+#define ADB_FIFO_EN (1 << 0x15)
+// The ADB masks and shift also are valid for the wtdma, except if specified otherwise.
+#define VORTEX_ADBDMA_BUFCFG0 0x27800
+#define VORTEX_ADBDMA_BUFCFG1 0x27804
+#define VORTEX_ADBDMA_BUFBASE 0x27400
+#define VORTEX_ADBDMA_START 0x27c00 /* Which subbuffer starts */
+
+#define VORTEX_ADBDMA_STATUS 0x27A90 /* stored at AdbDma->this_10 / 2 DWORD in size. */
+/* Starting at the MSB, each pair of bits seem to be the current DMA page. */
+/* This current page bits are consistent (same value) with VORTEX_ADBDMA_STAT) */
+
+/* DMA */
+#define VORTEX_ENGINE_CTRL 0x27ae8
+#define ENGINE_INIT 0x1380000
+
+/* WTDMA */
+#define VORTEX_WTDMA_CTRL 0x27900 /* format, DMA pos */
+#define VORTEX_WTDMA_STAT 0x27d00 /* DMA subbuf, DMA pos */
+#define WT_SUBBUF_MASK 0x3
+#define WT_SUBBUF_SHIFT 0xc
+#define VORTEX_WTDMA_BUFBASE 0x27000
+#define VORTEX_WTDMA_BUFCFG0 0x27600
+#define VORTEX_WTDMA_BUFCFG1 0x27604
+#define VORTEX_WTDMA_START 0x27b00 /* which subbuffer is first */
+
+/* ADB */
+#define VORTEX_ADB_SR 0x28400 /* Samplerates enable/disable */
+#define VORTEX_ADB_RTBASE 0x28000
+#define VORTEX_ADB_RTBASE_COUNT 173
+#define VORTEX_ADB_CHNBASE 0x282b4
+#define VORTEX_ADB_CHNBASE_COUNT 24
+#define ROUTE_MASK 0xffff
+#define SOURCE_MASK 0xff00
+#define ADB_MASK 0xff
+#define ADB_SHIFT 0x8
+/* ADB address */
+#define OFFSET_ADBDMA 0x00
+#define OFFSET_ADBDMAB 0x20
+#define OFFSET_SRCIN 0x40
+#define OFFSET_SRCOUT 0x20 /* ch 0x11 */
+#define OFFSET_MIXIN 0x50 /* ch 0x11 */
+#define OFFSET_MIXOUT 0x30 /* ch 0x11 */
+#define OFFSET_CODECIN 0x70 /* ch 0x11 */ /* adb source */
+#define OFFSET_CODECOUT 0x88 /* ch 0x11 */ /* adb target */
+#define OFFSET_SPORTIN 0x78 /* ch 0x13 ADB source. 2 routes. */
+#define OFFSET_SPORTOUT 0x90 /* ch 0x13 ADB sink. 2 routes. */
+#define OFFSET_SPDIFIN 0x7A /* ch 0x14 ADB source. */
+#define OFFSET_SPDIFOUT 0x92 /* ch 0x14 ADB sink. */
+#define OFFSET_AC98IN 0x7c /* ch 0x14 ADB source. */
+#define OFFSET_AC98OUT 0x94 /* ch 0x14 ADB sink. */
+#define OFFSET_EQIN 0xa0 /* ch 0x11 */
+#define OFFSET_EQOUT 0x7e /* ch 0x11 */ /* 2 routes on ch 0x11 */
+#define OFFSET_A3DIN 0x70 /* ADB sink. */
+#define OFFSET_A3DOUT 0xA6 /* ADB source. 2 routes per slice = 8 */
+#define OFFSET_WT0 0x40 /* WT bank 0 output. 0x40 - 0x65 */
+#define OFFSET_WT1 0x80 /* WT bank 1 output. 0x80 - 0xA5 */
+/* WT sources offset : 0x00-0x1f Direct stream. */
+/* WT sources offset : 0x20-0x25 Mixed Output. */
+#define OFFSET_XTALKOUT 0x66 /* crosstalk canceller (source) 2 routes */
+#define OFFSET_XTALKIN 0x96 /* crosstalk canceller (sink). 10 routes */
+#define OFFSET_EFXOUT 0x68 /* ADB source. 8 routes. */
+#define OFFSET_EFXIN 0x80 /* ADB sink. 8 routes. */
+
+/* ADB route translate helper */
+#define ADB_DMA(x) (x)
+#define ADB_SRCOUT(x) (x + OFFSET_SRCOUT)
+#define ADB_SRCIN(x) (x + OFFSET_SRCIN)
+#define ADB_MIXOUT(x) (x + OFFSET_MIXOUT)
+#define ADB_MIXIN(x) (x + OFFSET_MIXIN)
+#define ADB_CODECIN(x) (x + OFFSET_CODECIN)
+#define ADB_CODECOUT(x) (x + OFFSET_CODECOUT)
+#define ADB_SPORTIN(x) (x + OFFSET_SPORTIN)
+#define ADB_SPORTOUT(x) (x + OFFSET_SPORTOUT)
+#define ADB_SPDIFIN(x) (x + OFFSET_SPDIFIN)
+#define ADB_SPDIFOUT(x) (x + OFFSET_SPDIFOUT)
+#define ADB_EQIN(x) (x + OFFSET_EQIN)
+#define ADB_EQOUT(x) (x + OFFSET_EQOUT)
+#define ADB_A3DOUT(x) (x + OFFSET_A3DOUT) /* 0x10 A3D blocks */
+#define ADB_A3DIN(x) (x + OFFSET_A3DIN)
+//#define ADB_WTOUT(x) ((x<x20)?(x + OFFSET_WT0):(x + OFFSET_WT1))
+#define ADB_WTOUT(x,y) (((x)==0)?((y) + OFFSET_WT0):((y) + OFFSET_WT1))
+#define ADB_XTALKIN(x) ((x) + OFFSET_XTALKIN)
+#define ADB_XTALKOUT(x) ((x) + OFFSET_XTALKOUT)
+
+#define MIX_DEFIGAIN 0x08
+#define MIX_DEFOGAIN 0x08 /* 0x8->6dB (6dB = x4) 16 to 18 bit conversion? */
+
+/* MIXER */
+#define VORTEX_MIXER_SR 0x21f00
+#define VORTEX_MIXER_CLIP 0x21f80
+#define VORTEX_MIXER_CHNBASE 0x21e40
+#define VORTEX_MIXER_RTBASE 0x21e00
+#define MIXER_RTBASE_SIZE 0x38
+#define VORTEX_MIX_ENIN 0x21a00 /* Input enable bits. 4 bits wide. */
+#define VORTEX_MIX_SMP 0x21c00 /* wave data buffers. AU8820: 0x9c00 */
+
+/* MIX */
+#define VORTEX_MIX_INVOL_B 0x20000 /* Input volume current */
+#define VORTEX_MIX_VOL_B 0x20800 /* Output Volume current */
+#define VORTEX_MIX_INVOL_A 0x21000 /* Input Volume target */
+#define VORTEX_MIX_VOL_A 0x21800 /* Output Volume target */
+
+#define VOL_MIN 0x80 /* Input volume when muted. */
+#define VOL_MAX 0x7f /* FIXME: Not confirmed! Just guessed. */
+
+/* SRC */
+#define VORTEX_SRC_CHNBASE 0x26c40
+#define VORTEX_SRC_RTBASE 0x26c00
+#define VORTEX_SRCBLOCK_SR 0x26cc0
+#define VORTEX_SRC_SOURCE 0x26cc4
+#define VORTEX_SRC_SOURCESIZE 0x26cc8
+/* Params
+ 0x26e00 : 1 U0
+ 0x26e40 : 2 CR
+ 0x26e80 : 3 U3
+ 0x26ec0 : 4 DRIFT1
+ 0x26f00 : 5 U1
+ 0x26f40 : 6 DRIFT2
+ 0x26f80 : 7 U2 : Target rate, direction
+*/
+
+#define VORTEX_SRC_CONVRATIO 0x26e40
+#define VORTEX_SRC_DRIFT0 0x26e80
+#define VORTEX_SRC_DRIFT1 0x26ec0
+#define VORTEX_SRC_DRIFT2 0x26f40
+#define VORTEX_SRC_U0 0x26e00
+#define U0_SLOWLOCK 0x200
+#define VORTEX_SRC_U1 0x26f00
+#define VORTEX_SRC_U2 0x26f80
+#define VORTEX_SRC_DATA 0x26800 /* 0xc800 */
+#define VORTEX_SRC_DATA0 0x26000
+
+/* FIFO */
+#define VORTEX_FIFO_ADBCTRL 0x16100 /* Control bits. */
+#define VORTEX_FIFO_WTCTRL 0x16000
+#define FIFO_RDONLY 0x00000001
+#define FIFO_CTRL 0x00000002 /* Allow ctrl. ? */
+#define FIFO_VALID 0x00000010
+#define FIFO_EMPTY 0x00000020
+#define FIFO_U0 0x00002000 /* Unknown. */
+#define FIFO_U1 0x00040000
+#define FIFO_SIZE_BITS 6
+#define FIFO_SIZE (1<<(FIFO_SIZE_BITS)) // 0x40
+#define FIFO_MASK (FIFO_SIZE-1) //0x3f /* at shift left 0xc */
+#define FIFO_BITS 0x1c400000
+#define VORTEX_FIFO_ADBDATA 0x14000
+#define VORTEX_FIFO_WTDATA 0x10000
+
+#define VORTEX_FIFO_GIRT 0x17000 /* wt0, wt1, adb */
+#define GIRT_COUNT 3
+
+/* CODEC */
+
+#define VORTEX_CODEC_CHN 0x29080 /* The name "CHN" is wrong. */
+
+#define VORTEX_CODEC_CTRL 0x29184
+#define VORTEX_CODEC_IO 0x29188
+
+#define VORTEX_CODEC_SPORTCTRL 0x2918c
+
+#define VORTEX_CODEC_EN 0x29190
+#define EN_AUDIO0 0x00000300
+#define EN_MODEM 0x00000c00
+#define EN_AUDIO1 0x00003000
+#define EN_SPORT 0x00030000
+#define EN_SPDIF 0x000c0000
+#define EN_CODEC (EN_AUDIO1 | EN_AUDIO0)
+
+#define VORTEX_SPDIF_SMPRATE 0x29194
+
+#define VORTEX_SPDIF_FLAGS 0x2205c
+#define VORTEX_SPDIF_CFG0 0x291D0 /* status data */
+#define VORTEX_SPDIF_CFG1 0x291D4
+
+#define VORTEX_SMP_TIME 0x29198 /* Sample counter/timer */
+#define VORTEX_SMP_TIMER 0x2919c
+#define VORTEX_CODEC2_CTRL 0x291a0
+
+#define VORTEX_MODEM_CTRL 0x291ac
+
+/* IRQ */
+#define VORTEX_IRQ_SOURCE 0x2a000 /* Interrupt source flags. */
+#define VORTEX_IRQ_CTRL 0x2a004 /* Interrupt source mask. */
+
+//#define VORTEX_IRQ_U0 0x2a008 /* ?? */
+#define VORTEX_STAT 0x2a008 /* Some sort of status */
+#define STAT_IRQ 0x00000001 /* This bitis set if the IRQ is valid. */
+
+#define VORTEX_CTRL 0x2a00c
+#define CTRL_MIDI_EN 0x00000001
+#define CTRL_MIDI_PORT 0x00000060
+#define CTRL_GAME_EN 0x00000008
+#define CTRL_GAME_PORT 0x00000e00
+#define CTRL_IRQ_ENABLE 0x00004000
+#define CTRL_SPDIF 0x00000000 /* unknown. Please find this value */
+#define CTRL_SPORT 0x00200000
+#define CTRL_RST 0x00800000
+#define CTRL_UNKNOWN 0x01000000
+
+/* write: Timer period config / read: TIMER IRQ ack. */
+#define VORTEX_IRQ_STAT 0x2919c
+
+ /* MIDI *//* GAME. */
+#define VORTEX_MIDI_DATA 0x28800
+#define VORTEX_MIDI_CMD 0x28804 /* Write command / Read status */
+
+#define VORTEX_GAME_LEGACY 0x28808
+#define VORTEX_CTRL2 0x2880c
+#define CTRL2_GAME_ADCMODE 0x40
+#define VORTEX_GAME_AXIS 0x28810 /* Axis base register. 4 axis's */
+#define AXIS_SIZE 4
+#define AXIS_RANGE 0x1fff
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
new file mode 100644
index 000000000..eb2341536
--- /dev/null
+++ b/sound/pci/au88x0/au88x0.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ALSA driver for the Aureal Vortex family of soundprocessors.
+ * Author: Manuel Jander (mjander@embedded.cl)
+ *
+ * This driver is the result of the OpenVortex Project from Savannah
+ * (savannah.nongnu.org/projects/openvortex). I would like to thank
+ * the developers of OpenVortex, Jeff Muizelaar and Kester Maddock, from
+ * whom i got plenty of help, and their codebase was invaluable.
+ * Thanks to the ALSA developers, they helped a lot working out
+ * the ALSA part.
+ * Thanks also to Sourceforge for maintaining the old binary drivers,
+ * and the forum, where developers could comunicate.
+ *
+ * Now at least i can play Legacy DOOM with MIDI music :-)
+ */
+
+#include "au88x0.h"
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <sound/initval.h>
+
+// module parameters (see "Module Parameters")
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static int pcifix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 255 };
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+module_param_array(pcifix, int, NULL, 0444);
+MODULE_PARM_DESC(pcifix, "Enable VIA-workaround for " CARD_NAME " soundcard.");
+
+MODULE_DESCRIPTION("Aureal vortex");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, snd_vortex_ids);
+
+static void vortex_fix_latency(struct pci_dev *vortex)
+{
+ int rc;
+ rc = pci_write_config_byte(vortex, 0x40, 0xff);
+ if (!rc) {
+ dev_info(&vortex->dev, "vortex latency is 0xff\n");
+ } else {
+ dev_warn(&vortex->dev,
+ "could not set vortex latency: pci error 0x%x\n", rc);
+ }
+}
+
+static void vortex_fix_agp_bridge(struct pci_dev *via)
+{
+ int rc;
+ u8 value;
+
+ /*
+ * only set the bit (Extend PCI#2 Internal Master for
+ * Efficient Handling of Dummy Requests) if the can
+ * read the config and it is not already set
+ */
+
+ rc = pci_read_config_byte(via, 0x42, &value);
+ if (!rc) {
+ if (!(value & 0x10))
+ rc = pci_write_config_byte(via, 0x42, value | 0x10);
+ }
+ if (!rc) {
+ dev_info(&via->dev, "bridge config is 0x%x\n", value | 0x10);
+ } else {
+ dev_warn(&via->dev,
+ "could not set vortex latency: pci error 0x%x\n", rc);
+ }
+}
+
+static void snd_vortex_workaround(struct pci_dev *vortex, int fix)
+{
+ struct pci_dev *via = NULL;
+
+ /* autodetect if workarounds are required */
+ if (fix == 255) {
+ /* VIA KT133 */
+ via = pci_get_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_8365_1, NULL);
+ /* VIA Apollo */
+ if (via == NULL) {
+ via = pci_get_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_82C598_1, NULL);
+ /* AMD Irongate */
+ if (via == NULL)
+ via = pci_get_device(PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL);
+ }
+ if (via) {
+ dev_info(&vortex->dev,
+ "Activating latency workaround...\n");
+ vortex_fix_latency(vortex);
+ vortex_fix_agp_bridge(via);
+ }
+ } else {
+ if (fix & 0x1)
+ vortex_fix_latency(vortex);
+ if (fix & 0x2)
+ via = pci_get_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_8365_1, NULL);
+ else if (fix & 0x4)
+ via = pci_get_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_82C598_1, NULL);
+ else if (fix & 0x8)
+ via = pci_get_device(PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL);
+ if (via)
+ vortex_fix_agp_bridge(via);
+ }
+ pci_dev_put(via);
+}
+
+// component-destructor
+// (see "Management of Cards and Components")
+static void snd_vortex_free(struct snd_card *card)
+{
+ vortex_t *vortex = card->private_data;
+
+ vortex_gameport_unregister(vortex);
+ vortex_core_shutdown(vortex);
+}
+
+// chip-specific constructor
+// (see "Management of Cards and Components")
+static int
+snd_vortex_create(struct snd_card *card, struct pci_dev *pci)
+{
+ vortex_t *chip = card->private_data;
+ int err;
+
+ // check PCI availability (DMA).
+ err = pcim_enable_device(pci);
+ if (err < 0)
+ return err;
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
+ dev_err(card->dev, "error to set DMA mask\n");
+ return -ENXIO;
+ }
+
+ chip->card = card;
+
+ // initialize the stuff
+ chip->pci_dev = pci;
+ chip->vendor = pci->vendor;
+ chip->device = pci->device;
+ chip->card = card;
+ chip->irq = -1;
+
+ // (1) PCI resource allocation
+ // Get MMIO area
+ //
+ err = pcim_iomap_regions(pci, 1 << 0, CARD_NAME_SHORT);
+ if (err)
+ return err;
+
+ chip->io = pci_resource_start(pci, 0);
+ chip->mmio = pcim_iomap_table(pci)[0];
+
+ /* Init audio core.
+ * This must be done before we do request_irq otherwise we can get spurious
+ * interrupts that we do not handle properly and make a mess of things */
+ err = vortex_core_init(chip);
+ if (err) {
+ dev_err(card->dev, "hw core init failed\n");
+ return err;
+ }
+
+ err = devm_request_irq(&pci->dev, pci->irq, vortex_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (err) {
+ dev_err(card->dev, "cannot grab irq\n");
+ return err;
+ }
+ chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_vortex_free;
+
+ pci_set_master(pci);
+ // End of PCI setup.
+ return 0;
+}
+
+// constructor -- see "Constructor" sub-section
+static int
+__snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+ static int dev;
+ struct snd_card *card;
+ vortex_t *chip;
+ int err;
+
+ // (1)
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+ // (2)
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
+ if (err < 0)
+ return err;
+ chip = card->private_data;
+
+ // (3)
+ err = snd_vortex_create(card, pci);
+ if (err < 0)
+ return err;
+ snd_vortex_workaround(pci, pcifix[dev]);
+
+ // Card details needed in snd_vortex_midi
+ strcpy(card->driver, CARD_NAME_SHORT);
+ sprintf(card->shortname, "Aureal Vortex %s", CARD_NAME_SHORT);
+ sprintf(card->longname, "%s at 0x%lx irq %i",
+ card->shortname, chip->io, chip->irq);
+
+ // (4) Alloc components.
+ err = snd_vortex_mixer(chip);
+ if (err < 0)
+ return err;
+ // ADB pcm.
+ err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_PCM);
+ if (err < 0)
+ return err;
+#ifndef CHIP_AU8820
+ // ADB SPDIF
+ err = snd_vortex_new_pcm(chip, VORTEX_PCM_SPDIF, 1);
+ if (err < 0)
+ return err;
+ // A3D
+ err = snd_vortex_new_pcm(chip, VORTEX_PCM_A3D, NR_A3D);
+ if (err < 0)
+ return err;
+#endif
+ /*
+ // ADB I2S
+ if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_I2S, 1)) < 0) {
+ return err;
+ }
+ */
+#ifndef CHIP_AU8810
+ // WT pcm.
+ err = snd_vortex_new_pcm(chip, VORTEX_PCM_WT, NR_WT);
+ if (err < 0)
+ return err;
+#endif
+ err = snd_vortex_midi(chip);
+ if (err < 0)
+ return err;
+
+ vortex_gameport_register(chip);
+
+#if 0
+ if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_VORTEX_SYNTH,
+ sizeof(snd_vortex_synth_arg_t), &wave) < 0
+ || wave == NULL) {
+ dev_err(card->dev, "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)
+ err = pci_read_config_word(pci, PCI_DEVICE_ID, &chip->device);
+ if (err < 0)
+ return err;
+ err = pci_read_config_word(pci, PCI_VENDOR_ID, &chip->vendor);
+ if (err < 0)
+ return err;
+ chip->rev = pci->revision;
+#ifdef CHIP_AU8830
+ if ((chip->rev) != 0xfe && (chip->rev) != 0xfa) {
+ dev_alert(card->dev,
+ "The revision (%x) of your card has not been seen before.\n",
+ chip->rev);
+ dev_alert(card->dev,
+ "Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n");
+ return -ENODEV;
+ }
+#endif
+
+ // (6)
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
+ // (7)
+ pci_set_drvdata(pci, card);
+ dev++;
+ vortex_connect_default(chip, 1);
+ vortex_enable_int(chip);
+ return 0;
+}
+
+static int
+snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+ return snd_card_free_on_error(&pci->dev, __snd_vortex_probe(pci, pci_id));
+}
+
+// pci_driver definition
+static struct pci_driver vortex_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_vortex_ids,
+ .probe = snd_vortex_probe,
+};
+
+module_pci_driver(vortex_driver);
diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h
new file mode 100644
index 000000000..6cbb2bc4a
--- /dev/null
+++ b/sound/pci/au88x0/au88x0.h
@@ -0,0 +1,281 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ */
+
+#ifndef __SOUND_AU88X0_H
+#define __SOUND_AU88X0_H
+
+#include <linux/pci.h>
+#include <linux/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>
+#include <sound/tlv.h>
+
+#ifndef CHIP_AU8820
+#include "au88x0_eq.h"
+#include "au88x0_a3d.h"
+#endif
+#ifndef CHIP_AU8810
+#include "au88x0_wt.h"
+#endif
+
+#define hwread(x,y) readl((x)+(y))
+#define hwwrite(x,y,z) writel((z),(x)+(y))
+
+/* 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
+
+/* codec io: VORTEX_CODEC_IO bits */
+#define VORTEX_CODEC_ID_SHIFT 24
+#define VORTEX_CODEC_WRITE 0x00800000
+#define VORTEX_CODEC_ADDSHIFT 16
+#define VORTEX_CODEC_ADDMASK 0x7f0000
+#define VORTEX_CODEC_DATSHIFT 0
+#define VORTEX_CODEC_DATMASK 0xffff
+
+/* Check for SDAC bit in "Extended audio ID" AC97 register */
+//#define VORTEX_IS_QUAD(x) (((x)->codec == NULL) ? 0 : ((x)->codec->ext_id&0x80))
+#define VORTEX_IS_QUAD(x) ((x)->isquad)
+/* Check if chip has bug. */
+#define IS_BAD_CHIP(x) (\
+ (x->rev == 0xfe && x->device == PCI_DEVICE_ID_AUREAL_VORTEX_2) || \
+ (x->rev == 0xfe && x->device == PCI_DEVICE_ID_AUREAL_ADVANTAGE))
+
+
+/* PCM devices */
+#define VORTEX_PCM_ADB 0
+#define VORTEX_PCM_SPDIF 1
+#define VORTEX_PCM_A3D 2
+#define VORTEX_PCM_WT 3
+#define VORTEX_PCM_I2S 4
+#define VORTEX_PCM_LAST 5
+
+#define MIX_CAPT(x) (vortex->mixcapt[x])
+#define MIX_PLAYB(x) (vortex->mixplayb[x])
+#define MIX_SPDIF(x) (vortex->mixspdif[x])
+
+#define NR_WTPB 0x20 /* WT channels per each bank. */
+#define NR_PCM 0x10
+
+struct pcm_vol {
+ struct snd_kcontrol *kctl;
+ int active;
+ int dma;
+ int mixin[4];
+ int vol[4];
+};
+
+/* Structs */
+typedef struct {
+ //int this_08; /* Still unknown */
+ int fifo_enabled; /* this_24 */
+ int fifo_status; /* this_1c */
+ u32 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;
+ int period_real;
+ int period_virt;
+
+ struct snd_pcm_substream *substream;
+} stream_t;
+
+typedef struct snd_vortex vortex_t;
+struct snd_vortex {
+ /* ALSA structs. */
+ struct snd_card *card;
+ struct snd_pcm *pcm[VORTEX_PCM_LAST];
+
+ struct snd_rawmidi *rmidi; /* Legacy Midi interface. */
+ struct snd_ac97 *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. */
+ s8 mixwt[(NR_WT / NR_WTPB) * 6]; /* WT mixin objects */
+#endif
+
+ /* Global resources */
+ s8 mixcapt[2];
+ s8 mixplayb[4];
+#ifndef CHIP_AU8820
+ s8 mixspdif[2];
+ s8 mixa3d[2]; /* mixers which collect all a3d streams. */
+ s8 mixxtlk[2]; /* crosstalk canceler mixer inputs. */
+#endif
+ u32 fixed_res[5];
+
+#ifndef CHIP_AU8820
+ /* Hardware equalizer structs */
+ eqlzr_t eq;
+ /* A3D structs */
+ a3dsrc_t a3d[NR_A3D];
+ /* Xtalk canceler */
+ int xt_mode; /* 1: speakers, 0:headphones. */
+#endif
+ struct pcm_vol pcm_vol[NR_PCM];
+
+ int isquad; /* cache of extended ID codec flag. */
+
+ /* Gameport stuff. */
+ struct gameport *gameport;
+
+ /* PCI hardware resources */
+ unsigned long io;
+ void __iomem *mmio;
+ unsigned int irq;
+ spinlock_t lock;
+
+ /* PCI device */
+ struct pci_dev *pci_dev;
+ u16 vendor;
+ u16 device;
+ u8 rev;
+};
+
+/* Functions. */
+
+/* SRC */
+static void vortex_adb_setsrc(vortex_t * vortex, int adbdma,
+ unsigned int cvrt, int dir);
+
+/* DMA Engines. */
+static void vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
+ int size, int count);
+static void vortex_adbdma_setmode(vortex_t * vortex, int adbdma, int ie,
+ int dir, int fmt, int d,
+ u32 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,
+ int size, int count);
+static void vortex_wtdma_setmode(vortex_t * vortex, int wtdma, int ie, int fmt, int d, /*int e, */
+ u32 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 inline int vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma);
+static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma);
+
+#ifndef CHIP_AU8810
+static void vortex_wtdma_startfifo(vortex_t * vortex, int wtdma);
+static void vortex_wtdma_stopfifo(vortex_t * vortex, int wtdma);
+static void vortex_wtdma_pausefifo(vortex_t * vortex, int wtdma);
+static void vortex_wtdma_resumefifo(vortex_t * vortex, int wtdma);
+static inline int vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma);
+#endif
+
+/* global stuff. */
+static void vortex_codec_init(vortex_t * vortex);
+static void vortex_codec_write(struct snd_ac97 * codec, unsigned short addr,
+ unsigned short data);
+static unsigned short vortex_codec_read(struct snd_ac97 * 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);
+static int vortex_alsafmt_aspfmt(snd_pcm_format_t alsafmt, vortex_t *v);
+
+/* 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, int subdev);
+static int 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_enable(vortex_t * v);
+static void vortex_Vort3D_disable(vortex_t * v);
+static void vortex_Vort3D_connect(vortex_t * vortex, int en);
+static void vortex_Vort3D_InitializeSource(a3dsrc_t *a, int en, vortex_t *v);
+#endif
+
+/* Driver stuff. */
+static int vortex_gameport_register(vortex_t * card);
+static void vortex_gameport_unregister(vortex_t * card);
+#ifndef CHIP_AU8820
+static int vortex_eq_init(vortex_t * vortex);
+static int vortex_eq_free(vortex_t * vortex);
+#endif
+/* ALSA stuff. */
+static int snd_vortex_new_pcm(vortex_t * vortex, int idx, int nr);
+static int snd_vortex_mixer(vortex_t * vortex);
+static int snd_vortex_midi(vortex_t * vortex);
+#endif
diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c
new file mode 100644
index 000000000..eabaee046
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_a3d.c
@@ -0,0 +1,906 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/***************************************************************************
+ * 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...
+ ****************************************************************************/
+
+/*
+ */
+
+#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 absorption. */
+
+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)
+{
+ /*
+ pr_debug( "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, vortex_t *v)
+{
+ int i, var, var2;
+
+ if ((a->vortex) == NULL) {
+ dev_err(v->card->dev,
+ "ZeroStateA3D: ERROR: a->vortex is NULL\n");
+ return;
+ }
+
+ a3dsrc_SetA3DControlReg(a, 0);
+ a3dsrc_SetA3DPointerReg(a, 0);
+
+ var = a->slice;
+ var2 = a->source;
+ for (i = 0; i < 4; i++) {
+ a->slice = i;
+ a3dsrc_ZeroSliceIO(a);
+ //a3dsrc_ZeroState(a);
+ }
+ a->source = var2;
+ a->slice = var;
+}
+
+/* Program A3D block as pass through */
+static void a3dsrc_ProgramPipe(a3dsrc_t * a)
+{
+ a3dsrc_SetTimeConsts(a, 0, 0, 0, 0);
+ a3dsrc_SetAtmosCurrent(a, 0, 0x4000, 0, 0, 0);
+ a3dsrc_SetAtmosTarget(a, 0x4000, 0, 0, 0, 0);
+ a3dsrc_SetItdCurrent(a, 0, 0);
+ a3dsrc_SetItdTarget(a, 0, 0);
+ a3dsrc_SetGainCurrent(a, 0x7fff, 0x7fff);
+ a3dsrc_SetGainTarget(a, 0x7fff, 0x7fff);
+
+ /* SET HRTF HERE */
+
+ /* Single spike leads to identity transfer function. */
+ a3dsrc_SetHrtfCurrent(a, A3dHrirImpulse, A3dHrirImpulse);
+ a3dsrc_SetHrtfTarget(a, A3dHrirImpulse, A3dHrirImpulse);
+
+ /* Test: Sounds saturated. */
+ //a3dsrc_SetHrtfCurrent(a, A3dHrirSatTest, A3dHrirSatTest);
+ //a3dsrc_SetHrtfTarget(a, A3dHrirSatTest, A3dHrirSatTest);
+}
+
+/* VDB = Vortex audio Dataflow Bus */
+#if 0
+static void a3dsrc_ClearVDBData(a3dsrc_t * a, unsigned long aa)
+{
+ vortex_t *vortex = (vortex_t *) (a->vortex);
+
+ // ((aa >> 2) << 8) - (aa >> 2)
+ hwwrite(vortex->mmio,
+ a3d_addrS(a->slice, A3D_SLICE_VDBDest) + (a->source << 2), 0);
+ hwwrite(vortex->mmio,
+ a3d_addrS(a->slice,
+ A3D_SLICE_VDBDest + 4) + (a->source << 2), 0);
+ /*
+ hwwrite(vortex->mmio, 0x19c00 + (((aa>>2)*255*4)+aa)*8, 0);
+ hwwrite(vortex->mmio, 0x19c04 + (((aa>>2)*255*4)+aa)*8, 0);
+ */
+}
+#endif
+
+/* A3D HwSource stuff. */
+
+static void vortex_A3dSourceHw_Initialize(vortex_t * v, int source, int slice)
+{
+ a3dsrc_t *a3dsrc = &(v->a3d[source + (slice * 4)]);
+ //a3dsrc_t *a3dsrc = &(v->a3d[source + (slice*4)]);
+
+ a3dsrc->vortex = (void *)v;
+ a3dsrc->source = source; /* source */
+ a3dsrc->slice = slice; /* slice */
+ a3dsrc_ZeroState(a3dsrc);
+ /* Added by me. */
+ a3dsrc_SetA3DSampleRate(a3dsrc, 0x11);
+}
+
+static int Vort3DRend_Initialize(vortex_t * v, unsigned short mode)
+{
+ v->xt_mode = mode; /* this_14 */
+
+ vortex_XtalkHw_init(v);
+ vortex_XtalkHw_SetGainsAllChan(v);
+ switch (v->xt_mode) {
+ case XT_SPEAKER0:
+ vortex_XtalkHw_ProgramXtalkNarrow(v);
+ break;
+ case XT_SPEAKER1:
+ vortex_XtalkHw_ProgramXtalkWide(v);
+ break;
+ default:
+ case XT_HEADPHONE:
+ vortex_XtalkHw_ProgramPipe(v);
+ break;
+ case XT_DIAMOND:
+ vortex_XtalkHw_ProgramDiamondXtalk(v);
+ break;
+ }
+ vortex_XtalkHw_SetSampleRate(v, 0x11);
+ vortex_XtalkHw_Enable(v);
+ return 0;
+}
+
+/* 3D Sound entry points. */
+
+static int vortex_a3d_register_controls(vortex_t * vortex);
+static void vortex_a3d_unregister_controls(vortex_t * vortex);
+/* A3D base support init/shudown */
+static void vortex_Vort3D_enable(vortex_t *v)
+{
+ int i;
+
+ 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], v);
+ }
+ /* Register ALSA controls */
+ vortex_a3d_register_controls(v);
+}
+
+static void vortex_Vort3D_disable(vortex_t * v)
+{
+ vortex_XtalkHw_Disable(v);
+ vortex_a3d_unregister_controls(v);
+}
+
+/* Make A3D subsystem connections. */
+static void vortex_Vort3D_connect(vortex_t * v, int en)
+{
+ int i;
+
+// Disable AU8810 routes, since they seem to be wrong (in au8810.h).
+#ifdef CHIP_AU8810
+ return;
+#endif
+
+#if 1
+ /* Alloc Xtalk mixin resources */
+ v->mixxtlk[0] =
+ vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
+ if (v->mixxtlk[0] < 0) {
+ dev_warn(v->card->dev,
+ "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) {
+ dev_warn(v->card->dev,
+ "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, vortex_t *v)
+{
+ if (a->vortex == NULL) {
+ dev_warn(v->card->dev,
+ "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(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *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(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *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(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *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(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *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(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ //a3dsrc_t *a = kcontrol->private_data;
+ /* No read yet. Would this be really useable/needed ? */
+
+ return 0;
+}
+
+static int
+snd_vortex_a3d_hrtf_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ a3dsrc_t *a = kcontrol->private_data;
+ int 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 1;
+}
+
+static int
+snd_vortex_a3d_itd_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ a3dsrc_t *a = kcontrol->private_data;
+ int coord[6];
+ int i;
+ 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 1;
+}
+
+static int
+snd_vortex_a3d_ild_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ a3dsrc_t *a = kcontrol->private_data;
+ 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 1;
+}
+
+static int
+snd_vortex_a3d_filter_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ a3dsrc_t *a = kcontrol->private_data;
+ int i;
+ 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 absorption 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 1;
+}
+
+static const struct snd_kcontrol_new vortex_a3d_kcontrol = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Playback PCM advanced processing",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_vortex_a3d_hrtf_info,
+ .get = snd_vortex_a3d_get,
+ .put = snd_vortex_a3d_hrtf_put,
+};
+
+/* Control (un)registration. */
+static int vortex_a3d_register_controls(vortex_t *vortex)
+{
+ struct snd_kcontrol *kcontrol;
+ int err, i;
+ /* HRTF controls. */
+ for (i = 0; i < NR_A3D; i++) {
+ kcontrol = snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i]);
+ if (!kcontrol)
+ return -ENOMEM;
+ kcontrol->id.numid = CTRLID_HRTF;
+ kcontrol->info = snd_vortex_a3d_hrtf_info;
+ kcontrol->put = snd_vortex_a3d_hrtf_put;
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
+ return err;
+ }
+ /* ITD controls. */
+ for (i = 0; i < NR_A3D; i++) {
+ kcontrol = snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i]);
+ if (!kcontrol)
+ return -ENOMEM;
+ kcontrol->id.numid = CTRLID_ITD;
+ kcontrol->info = snd_vortex_a3d_itd_info;
+ kcontrol->put = snd_vortex_a3d_itd_put;
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
+ return err;
+ }
+ /* ILD (gains) controls. */
+ for (i = 0; i < NR_A3D; i++) {
+ kcontrol = snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i]);
+ if (!kcontrol)
+ return -ENOMEM;
+ kcontrol->id.numid = CTRLID_GAINS;
+ kcontrol->info = snd_vortex_a3d_ild_info;
+ kcontrol->put = snd_vortex_a3d_ild_put;
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
+ return err;
+ }
+ /* Filter controls. */
+ for (i = 0; i < NR_A3D; i++) {
+ kcontrol = snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i]);
+ if (!kcontrol)
+ return -ENOMEM;
+ kcontrol->id.numid = CTRLID_FILTER;
+ kcontrol->info = snd_vortex_a3d_filter_info;
+ kcontrol->put = snd_vortex_a3d_filter_put;
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static void vortex_a3d_unregister_controls(vortex_t * vortex)
+{
+
+}
+
+/* End of File*/
diff --git a/sound/pci/au88x0/au88x0_a3d.h b/sound/pci/au88x0/au88x0_a3d.h
new file mode 100644
index 000000000..4078173da
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_a3d.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/***************************************************************************
+ * au88x0_a3d.h
+ *
+ * Fri Jul 18 14:16:03 2003
+ * Copyright 2003 mjander
+ * mjander@users.sourceforge.net
+ ****************************************************************************/
+
+/*
+ */
+
+#ifndef _AU88X0_A3D_H
+#define _AU88X0_A3D_H
+
+//#include <openal.h>
+
+#define HRTF_SZ 0x38
+#define DLINE_SZ 0x28
+
+#define CTRLID_HRTF 1
+#define CTRLID_ITD 2
+#define CTRLID_ILD 4
+#define CTRLID_FILTER 8
+#define CTRLID_GAINS 16
+
+/* 3D parameter structs */
+typedef unsigned short int a3d_Hrtf_t[HRTF_SZ];
+typedef unsigned short int a3d_ItdDline_t[DLINE_SZ];
+typedef unsigned short int a3d_atmos_t[5];
+typedef unsigned short int a3d_LRGains_t[2];
+typedef unsigned short int a3d_Itd_t[2];
+typedef unsigned short int a3d_Ild_t[2];
+
+typedef struct {
+ void *vortex; // Formerly CAsp4HwIO*, now vortex_t*.
+ unsigned int source; /* this_04 */
+ unsigned int slice; /* this_08 */
+ a3d_Hrtf_t hrtf[2];
+ a3d_Itd_t itd;
+ a3d_Ild_t ild;
+ a3d_ItdDline_t dline;
+ a3d_atmos_t filter;
+} a3dsrc_t;
+
+/* First Register bank */
+
+#define A3D_A_HrtfCurrent 0x18000 /* 56 ULONG */
+#define A3D_A_GainCurrent 0x180E0
+#define A3D_A_GainTarget 0x180E4
+#define A3D_A_A12Current 0x180E8 /* Atmospheric current. */
+#define A3D_A_A21Target 0x180EC /* Atmospheric target */
+#define A3D_A_B01Current 0x180F0 /* Atmospheric current */
+#define A3D_A_B10Target 0x180F4 /* Atmospheric target */
+#define A3D_A_B2Current 0x180F8 /* Atmospheric current */
+#define A3D_A_B2Target 0x180FC /* Atmospheric target */
+#define A3D_A_HrtfTarget 0x18100 /* 56 ULONG */
+#define A3D_A_ITDCurrent 0x181E0
+#define A3D_A_ITDTarget 0x181E4
+#define A3D_A_HrtfDelayLine 0x181E8 /* 56 ULONG */
+#define A3D_A_ITDDelayLine 0x182C8 /* 40/45 ULONG */
+#define A3D_A_HrtfTrackTC 0x1837C /* Time Constants */
+#define A3D_A_GainTrackTC 0x18380
+#define A3D_A_CoeffTrackTC 0x18384
+#define A3D_A_ITDTrackTC 0x18388
+#define A3D_A_x1 0x1838C
+#define A3D_A_x2 0x18390
+#define A3D_A_y1 0x18394
+#define A3D_A_y2 0x18398
+#define A3D_A_HrtfOutL 0x1839C
+#define A3D_A_HrtfOutR 0x183A0
+#define A3D_A_TAIL 0x183A4
+
+/* Second register bank */
+#define A3D_B_HrtfCurrent 0x19000 /* 56 ULONG */
+#define A3D_B_GainCurrent 0x190E0
+#define A3D_B_GainTarget 0x190E4
+#define A3D_B_A12Current 0x190E8
+#define A3D_B_A21Target 0x190EC
+#define A3D_B_B01Current 0x190F0
+#define A3D_B_B10Target 0x190F4
+#define A3D_B_B2Current 0x190F8
+#define A3D_B_B2Target 0x190FC
+#define A3D_B_HrtfTarget 0x19100 /* 56 ULONG */
+#define A3D_B_ITDCurrent 0x191E0
+#define A3D_B_ITDTarget 0x191E4
+#define A3D_B_HrtfDelayLine 0x191E8 /* 56 ULONG */
+#define A3D_B_TAIL 0x192C8
+
+/* There are 4 slices, 4 a3d each = 16 a3d sources. */
+#define A3D_SLICE_BANK_A 0x18000 /* 4 sources */
+#define A3D_SLICE_BANK_B 0x19000 /* 4 sources */
+#define A3D_SLICE_VDBDest 0x19C00 /* 8 ULONG */
+#define A3D_SLICE_VDBSource 0x19C20 /* 4 ULONG */
+#define A3D_SLICE_ABReg 0x19C30
+#define A3D_SLICE_CReg 0x19C34
+#define A3D_SLICE_Control 0x19C38
+#define A3D_SLICE_DebugReserved 0x19C3c /* Dangerous! */
+#define A3D_SLICE_Pointers 0x19C40
+#define A3D_SLICE_TAIL 0x1A000
+
+// Slice size: 0x2000
+// Source size: 0x3A4, 0x2C8
+
+/* Address generator macro. */
+#define a3d_addrA(slice,source,reg) (((slice)<<0xd)+((source)*0x3A4)+(reg))
+#define a3d_addrB(slice,source,reg) (((slice)<<0xd)+((source)*0x2C8)+(reg))
+#define a3d_addrS(slice,reg) (((slice)<<0xd)+(reg))
+//#define a3d_addr(slice,source,reg) (((reg)>=0x19000) ? a3d_addr2((slice),(source),(reg)) : a3d_addr1((slice),(source),(reg)))
+
+#endif /* _AU88X0_A3D_H */
diff --git a/sound/pci/au88x0/au88x0_a3ddata.c b/sound/pci/au88x0/au88x0_a3ddata.c
new file mode 100644
index 000000000..a5da3b3a5
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_a3ddata.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/***************************************************************************
+ * au88x0_a3ddata.c
+ *
+ * Wed Nov 19 21:11:32 2003
+ * Copyright 2003 mjander
+ * mjander@users.sourceforge.org
+ ****************************************************************************/
+
+/*
+ */
+
+/* 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 __maybe_unused 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 __maybe_unused 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 __maybe_unused 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 __maybe_unused const a3d_Hrtf_t A3dHrirDImpulse = {
+ 0, 0x7fff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0
+};
+
+static const a3d_ItdDline_t A3dItdDlineZeros = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static short const GainTCDefault = 0x300;
+static short const ItdTCDefault = 0x0C8;
+static short const HrtfTCDefault = 0x147;
+static short const CoefTCDefault = 0x300;
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
new file mode 100644
index 000000000..f217c02df
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -0,0 +1,2862 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+/*
+ Vortex core low level functions.
+
+ Author: Manuel Jander (mjander@users.sourceforge.cl)
+ These functions are mainly the result of translations made
+ from the original disassembly of the au88x0 binary drivers,
+ written by Aureal before they went down.
+ Many thanks to the Jeff Muizelaar, Kester Maddock, and whoever
+ contributed to the OpenVortex project.
+ The author of this file, put the few available pieces together
+ and translated the rest of the riddle (Mix, Src and connection stuff).
+ Some things are still to be discovered, and their meanings are unclear.
+
+ Some of these functions aren't intended to be really used, rather
+ to help to understand how does the AU88X0 chips work. Keep them in, because
+ they could be used somewhere in the future.
+
+ This code hasn't been tested or proof read thoroughly. If you wanna help,
+ take a look at the AU88X0 assembly and check if this matches.
+ Functions tested ok so far are (they show the desired effect
+ at least):
+ vortex_routes(); (1 bug fixed).
+ vortex_adb_addroute();
+ vortex_adb_addroutes();
+ vortex_connect_codecplay();
+ vortex_src_flushbuffers();
+ vortex_adbdma_setmode(); note: still some unknown arguments!
+ vortex_adbdma_startfifo();
+ vortex_adbdma_stopfifo();
+ vortex_fifo_setadbctrl(); note: still some unknown arguments!
+ vortex_mix_setinputvolumebyte();
+ vortex_mix_enableinput();
+ vortex_mixer_addWTD(); (fixed)
+ vortex_connection_adbdma_src_src();
+ vortex_connection_adbdma_src();
+ vortex_src_change_convratio();
+ vortex_src_addWTD(); (fixed)
+
+ History:
+
+ 01-03-2003 First revision.
+ 01-21-2003 Some bug fixes.
+ 17-02-2003 many bugfixes after a big versioning mess.
+ 18-02-2003 JAAAAAHHHUUUUUU!!!! The mixer works !! I'm just so happy !
+ (2 hours later...) I cant believe it! Im really lucky today.
+ Now the SRC is working too! Yeah! XMMS works !
+ 20-02-2003 First steps into the ALSA world.
+ 28-02-2003 As my birthday present, i discovered how the DMA buffer pages really
+ work :-). It was all wrong.
+ 12-03-2003 ALSA driver starts working (2 channels).
+ 16-03-2003 More srcblock_setupchannel discoveries.
+ 12-04-2003 AU8830 playback support. Recording in the works.
+ 17-04-2003 vortex_route() and vortex_routes() bug fixes. AU8830 recording
+ works now, but chipn' dale effect is still there.
+ 16-05-2003 SrcSetupChannel cleanup. Moved the Src setup stuff entirely
+ into au88x0_pcm.c .
+ 06-06-2003 Buffer shifter bugfix. Mixer volume fix.
+ 07-12-2003 A3D routing finally fixed. Believed to be OK.
+ 25-03-2004 Many thanks to Claudia, for such valuable bug reports.
+
+*/
+
+#include "au88x0.h"
+#include "au88x0_a3d.h"
+#include <linux/delay.h>
+
+/* MIXER (CAsp4Mix.s and CAsp4Mixer.s) */
+
+// FIXME: get rid of this.
+static int mchannels[NR_MIXIN];
+static int rampchs[NR_MIXIN];
+
+static void vortex_mixer_en_sr(vortex_t * vortex, int channel)
+{
+ hwwrite(vortex->mmio, VORTEX_MIXER_SR,
+ hwread(vortex->mmio, VORTEX_MIXER_SR) | (0x1 << channel));
+}
+static void vortex_mixer_dis_sr(vortex_t * vortex, int channel)
+{
+ hwwrite(vortex->mmio, VORTEX_MIXER_SR,
+ hwread(vortex->mmio, VORTEX_MIXER_SR) & ~(0x1 << channel));
+}
+
+#if 0
+static void
+vortex_mix_muteinputgain(vortex_t * vortex, unsigned char mix,
+ unsigned char channel)
+{
+ hwwrite(vortex->mmio, VORTEX_MIX_INVOL_A + ((mix << 5) + channel),
+ 0x80);
+ hwwrite(vortex->mmio, VORTEX_MIX_INVOL_B + ((mix << 5) + channel),
+ 0x80);
+}
+
+static int vortex_mix_getvolume(vortex_t * vortex, unsigned char mix)
+{
+ int a;
+ a = hwread(vortex->mmio, VORTEX_MIX_VOL_A + (mix << 2)) & 0xff;
+ //FP2LinearFrac(a);
+ return (a);
+}
+
+static int
+vortex_mix_getinputvolume(vortex_t * vortex, unsigned char mix,
+ int channel, int *vol)
+{
+ int a;
+ if (!(mchannels[mix] & (1 << channel)))
+ return 0;
+ a = hwread(vortex->mmio,
+ VORTEX_MIX_INVOL_A + (((mix << 5) + channel) << 2));
+ /*
+ if (rampchs[mix] == 0)
+ a = FP2LinearFrac(a);
+ else
+ a = FP2LinearFracWT(a);
+ */
+ *vol = a;
+ return (0);
+}
+
+static unsigned int vortex_mix_boost6db(unsigned char vol)
+{
+ return (vol + 8); /* WOW! what a complex function! */
+}
+
+static void vortex_mix_rampvolume(vortex_t * vortex, int mix)
+{
+ int ch;
+ char a;
+ // This function is intended for ramping down only (see vortex_disableinput()).
+ for (ch = 0; ch < 0x20; ch++) {
+ if (((1 << ch) & rampchs[mix]) == 0)
+ continue;
+ a = hwread(vortex->mmio,
+ VORTEX_MIX_INVOL_B + (((mix << 5) + ch) << 2));
+ if (a > -126) {
+ a -= 2;
+ hwwrite(vortex->mmio,
+ VORTEX_MIX_INVOL_A +
+ (((mix << 5) + ch) << 2), a);
+ hwwrite(vortex->mmio,
+ VORTEX_MIX_INVOL_B +
+ (((mix << 5) + ch) << 2), a);
+ } else
+ vortex_mix_killinput(vortex, mix, ch);
+ }
+}
+
+static int
+vortex_mix_getenablebit(vortex_t * vortex, unsigned char mix, int mixin)
+{
+ int addr, temp;
+ if (mixin >= 0)
+ addr = mixin;
+ else
+ addr = mixin + 3;
+ addr = ((mix << 3) + (addr >> 2)) << 2;
+ temp = hwread(vortex->mmio, VORTEX_MIX_ENIN + addr);
+ return ((temp >> (mixin & 3)) & 1);
+}
+#endif
+static void
+vortex_mix_setvolumebyte(vortex_t * vortex, unsigned char mix,
+ unsigned char vol)
+{
+ int temp;
+ hwwrite(vortex->mmio, VORTEX_MIX_VOL_A + (mix << 2), vol);
+ if (1) { /*if (this_10) */
+ temp = hwread(vortex->mmio, VORTEX_MIX_VOL_B + (mix << 2));
+ if ((temp != 0x80) || (vol == 0x80))
+ return;
+ }
+ hwwrite(vortex->mmio, VORTEX_MIX_VOL_B + (mix << 2), vol);
+}
+
+static void
+vortex_mix_setinputvolumebyte(vortex_t * vortex, unsigned char mix,
+ int mixin, unsigned char vol)
+{
+ int temp;
+
+ hwwrite(vortex->mmio,
+ VORTEX_MIX_INVOL_A + (((mix << 5) + mixin) << 2), vol);
+ if (1) { /* this_10, initialized to 1. */
+ temp =
+ hwread(vortex->mmio,
+ VORTEX_MIX_INVOL_B + (((mix << 5) + mixin) << 2));
+ if ((temp != 0x80) || (vol == 0x80))
+ return;
+ }
+ hwwrite(vortex->mmio,
+ VORTEX_MIX_INVOL_B + (((mix << 5) + mixin) << 2), vol);
+}
+
+static void
+vortex_mix_setenablebit(vortex_t * vortex, unsigned char mix, int mixin, int en)
+{
+ int temp, addr;
+
+ if (mixin < 0)
+ addr = (mixin + 3);
+ else
+ addr = mixin;
+ addr = ((mix << 3) + (addr >> 2)) << 2;
+ temp = hwread(vortex->mmio, VORTEX_MIX_ENIN + addr);
+ if (en)
+ temp |= (1 << (mixin & 3));
+ else
+ temp &= ~(1 << (mixin & 3));
+ /* Mute input. Astatic void crackling? */
+ hwwrite(vortex->mmio,
+ VORTEX_MIX_INVOL_B + (((mix << 5) + mixin) << 2), 0x80);
+ /* Looks like clear buffer. */
+ hwwrite(vortex->mmio, VORTEX_MIX_SMP + (mixin << 2), 0x0);
+ hwwrite(vortex->mmio, VORTEX_MIX_SMP + 4 + (mixin << 2), 0x0);
+ /* Write enable bit. */
+ hwwrite(vortex->mmio, VORTEX_MIX_ENIN + addr, temp);
+}
+
+static void
+vortex_mix_killinput(vortex_t * vortex, unsigned char mix, int mixin)
+{
+ rampchs[mix] &= ~(1 << mixin);
+ vortex_mix_setinputvolumebyte(vortex, mix, mixin, 0x80);
+ mchannels[mix] &= ~(1 << mixin);
+ vortex_mix_setenablebit(vortex, mix, mixin, 0);
+}
+
+static void
+vortex_mix_enableinput(vortex_t * vortex, unsigned char mix, int mixin)
+{
+ vortex_mix_killinput(vortex, mix, mixin);
+ if ((mchannels[mix] & (1 << mixin)) == 0) {
+ vortex_mix_setinputvolumebyte(vortex, mix, mixin, 0x80); /*0x80 : mute */
+ mchannels[mix] |= (1 << mixin);
+ }
+ vortex_mix_setenablebit(vortex, mix, mixin, 1);
+}
+
+static void
+vortex_mix_disableinput(vortex_t * vortex, unsigned char mix, int channel,
+ int ramp)
+{
+ if (ramp) {
+ rampchs[mix] |= (1 << channel);
+ // Register callback.
+ //vortex_mix_startrampvolume(vortex);
+ vortex_mix_killinput(vortex, mix, channel);
+ } else
+ vortex_mix_killinput(vortex, mix, channel);
+}
+
+static int
+vortex_mixer_addWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
+{
+ int temp, lifeboat = 0, prev;
+
+ temp = hwread(vortex->mmio, VORTEX_MIXER_SR);
+ if ((temp & (1 << ch)) == 0) {
+ hwwrite(vortex->mmio, VORTEX_MIXER_CHNBASE + (ch << 2), mix);
+ vortex_mixer_en_sr(vortex, ch);
+ return 1;
+ }
+ prev = VORTEX_MIXER_CHNBASE + (ch << 2);
+ temp = hwread(vortex->mmio, prev);
+ while (temp & 0x10) {
+ prev = VORTEX_MIXER_RTBASE + ((temp & 0xf) << 2);
+ temp = hwread(vortex->mmio, prev);
+ //printk(KERN_INFO "vortex: mixAddWTD: while addr=%x, val=%x\n", prev, temp);
+ if ((++lifeboat) > 0xf) {
+ dev_err(vortex->card->dev,
+ "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) {
+ dev_err(vortex->card->dev, "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) {
+ dev_err(vortex->card->dev,
+ "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)
+{
+ u32 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) {
+ dev_err(vortex->card->dev, "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) {
+ pr_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)
+{
+ u32 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) {
+ dev_err(vortex->card->dev,
+ "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) {
+ dev_err(vortex->card->dev, "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) {
+ dev_warn(vortex->card->dev,
+ "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 stereo, 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) {
+ dev_err(vortex->card->dev,
+ "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) | ((stereo & 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) {
+ dev_err(vortex->card->dev,
+ "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) {
+ pr_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;
+ u32 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))
+ dev_err(vortex->card->dev, "bad adb fifo reset!\n");
+ 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)
+ dev_err(vortex->card->dev,
+ "bad wt fifo reset (0x%08x, 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,
+ int psize, int count)
+{
+ stream_t *dma = &vortex->dma_adb[adbdma];
+
+ dma->period_bytes = psize;
+ dma->nr_periods = count;
+
+ 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_pcm_sgbuf_get_addr(dma->substream, psize * 3));
+ fallthrough;
+ /* 3 pages */
+ case 3:
+ dma->cfg0 |= 0x12000000;
+ dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc);
+ hwwrite(vortex->mmio,
+ VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x8,
+ snd_pcm_sgbuf_get_addr(dma->substream, psize * 2));
+ fallthrough;
+ /* 2 pages */
+ case 2:
+ dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize - 1);
+ hwwrite(vortex->mmio,
+ VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x4,
+ snd_pcm_sgbuf_get_addr(dma->substream, psize));
+ fallthrough;
+ /* 1 page */
+ case 1:
+ dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc);
+ hwwrite(vortex->mmio,
+ VORTEX_ADBDMA_BUFBASE + (adbdma << 4),
+ snd_pcm_sgbuf_get_addr(dma->substream, 0));
+ break;
+ }
+ /*
+ pr_debug( "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 stereo, u32 offset)
+{
+ stream_t *dma = &vortex->dma_adb[adbdma];
+
+ dma->dma_unknown = stereo;
+ 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_pcm_sgbuf_get_addr(dma->substream,
+ 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)
+ dev_info(vortex->card->dev,
+ "%d virt=%d, real=%d, delta=%d\n",
+ adbdma, dma->period_virt, dma->period_real, delta);
+
+ return delta;
+}
+
+
+static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma) {
+ stream_t *dma = &vortex->dma_adb[adbdma];
+ int p, pp, i;
+
+ /* refresh hw page table */
+ for (i=0 ; i < 4 && i < dma->nr_periods; i++) {
+ /* p: audio buffer page index */
+ p = dma->period_virt + i;
+ if (p >= dma->nr_periods)
+ p -= dma->nr_periods;
+ /* pp: hardware DMA page index. */
+ pp = dma->period_real + i;
+ if (dma->nr_periods < 4) {
+ if (pp >= dma->nr_periods)
+ pp -= dma->nr_periods;
+ }
+ else {
+ if (pp >= 4)
+ pp -= 4;
+ }
+ hwwrite(vortex->mmio,
+ VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2),
+ snd_pcm_sgbuf_get_addr(dma->substream,
+ dma->period_bytes * p));
+ /* Force write thru cache. */
+ hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (((adbdma << 2)+pp) << 2));
+ }
+}
+
+static inline int vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma)
+{
+ stream_t *dma = &vortex->dma_adb[adbdma];
+ int temp, page, delta;
+
+ temp = hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2));
+ page = (temp & 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;
+ }
+ return (dma->period_virt + delta) * dma->period_bytes
+ + (temp & (dma->period_bytes - 1));
+}
+
+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;
+}
+
+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;
+}
+
+/* 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,
+ int psize, int count)
+{
+ stream_t *dma = &vortex->dma_wt[wtdma];
+
+ dma->period_bytes = psize;
+ dma->nr_periods = count;
+
+ dma->cfg0 = 0;
+ dma->cfg1 = 0;
+ switch (count) {
+ /* Four or more pages */
+ default:
+ case 4:
+ dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | (psize-1);
+ hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0xc,
+ snd_pcm_sgbuf_get_addr(dma->substream, psize * 3));
+ fallthrough;
+ /* 3 pages */
+ case 3:
+ dma->cfg0 |= 0x12000000;
+ dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);
+ hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x8,
+ snd_pcm_sgbuf_get_addr(dma->substream, psize * 2));
+ fallthrough;
+ /* 2 pages */
+ case 2:
+ dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize-1);
+ hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x4,
+ snd_pcm_sgbuf_get_addr(dma->substream, psize));
+ fallthrough;
+ /* 1 page */
+ case 1:
+ dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);
+ hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4),
+ snd_pcm_sgbuf_get_addr(dma->substream, 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, */ u32 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_SHIFT) & WT_SUBBUF_MASK;
+ 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_pcm_sgbuf_get_addr(dma->substream,
+ 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)
+ dev_warn(vortex->card->dev, "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 inline int 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 = (dma->period_virt * dma->period_bytes) + (temp & (dma->period_bytes - 1));
+ return temp;
+}
+
+static void vortex_wtdma_startfifo(vortex_t * vortex, int wtdma)
+{
+ stream_t *dma = &vortex->dma_wt[wtdma];
+ int this_8 = 0, this_4 = 0;
+
+ switch (dma->fifo_status) {
+ case FIFO_START:
+ vortex_fifo_setwtvalid(vortex, wtdma,
+ dma->fifo_enabled ? 1 : 0);
+ break;
+ case FIFO_STOP:
+ this_8 = 1;
+ hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2),
+ dma->dma_ctrl);
+ vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+ this_4, this_8,
+ dma->fifo_enabled ? 1 : 0, 0);
+ break;
+ case FIFO_PAUSE:
+ vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+ this_4, this_8,
+ dma->fifo_enabled ? 1 : 0, 0);
+ break;
+ }
+ dma->fifo_status = FIFO_START;
+}
+
+static void vortex_wtdma_resumefifo(vortex_t * vortex, int wtdma)
+{
+ stream_t *dma = &vortex->dma_wt[wtdma];
+
+ int this_8 = 0, this_4 = 0;
+ switch (dma->fifo_status) {
+ case FIFO_STOP:
+ hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2),
+ dma->dma_ctrl);
+ vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+ this_4, this_8,
+ dma->fifo_enabled ? 1 : 0, 0);
+ break;
+ case FIFO_PAUSE:
+ vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+ this_4, this_8,
+ dma->fifo_enabled ? 1 : 0, 0);
+ break;
+ }
+ dma->fifo_status = FIFO_START;
+}
+
+static void vortex_wtdma_pausefifo(vortex_t * vortex, int wtdma)
+{
+ stream_t *dma = &vortex->dma_wt[wtdma];
+
+ int this_8 = 0, this_4 = 0;
+ switch (dma->fifo_status) {
+ case FIFO_START:
+ vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+ this_4, this_8, 0, 0);
+ break;
+ case FIFO_STOP:
+ hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2),
+ dma->dma_ctrl);
+ vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+ this_4, this_8, 0, 0);
+ break;
+ }
+ dma->fifo_status = FIFO_PAUSE;
+}
+
+static void vortex_wtdma_stopfifo(vortex_t * vortex, int wtdma)
+{
+ stream_t *dma = &vortex->dma_wt[wtdma];
+
+ int this_4 = 0, this_8 = 0;
+ if (dma->fifo_status == FIFO_START)
+ vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown,
+ this_4, this_8, 0, 0);
+ else if (dma->fifo_status == FIFO_STOP)
+ return;
+ dma->fifo_status = FIFO_STOP;
+ dma->fifo_enabled = 0;
+}
+
+#endif
+/* ADB Routes */
+
+typedef int ADBRamLink;
+static void vortex_adb_init(vortex_t * vortex)
+{
+ int i;
+ /* it looks like we are writing more than we need to...
+ * if we write what we are supposed to it breaks things... */
+ hwwrite(vortex->mmio, VORTEX_ADB_SR, 0);
+ for (i = 0; i < VORTEX_ADB_RTBASE_COUNT; i++)
+ hwwrite(vortex->mmio, VORTEX_ADB_RTBASE + (i << 2),
+ hwread(vortex->mmio,
+ VORTEX_ADB_RTBASE + (i << 2)) | ROUTE_MASK);
+ for (i = 0; i < VORTEX_ADB_CHNBASE_COUNT; i++) {
+ hwwrite(vortex->mmio, VORTEX_ADB_CHNBASE + (i << 2),
+ hwread(vortex->mmio,
+ VORTEX_ADB_CHNBASE + (i << 2)) | ROUTE_MASK);
+ }
+}
+
+static void vortex_adb_en_sr(vortex_t * vortex, int channel)
+{
+ hwwrite(vortex->mmio, VORTEX_ADB_SR,
+ hwread(vortex->mmio, VORTEX_ADB_SR) | (0x1 << channel));
+}
+
+static void vortex_adb_dis_sr(vortex_t * vortex, int channel)
+{
+ hwwrite(vortex->mmio, VORTEX_ADB_SR,
+ hwread(vortex->mmio, VORTEX_ADB_SR) & ~(0x1 << channel));
+}
+
+static void
+vortex_adb_addroutes(vortex_t * vortex, unsigned char channel,
+ ADBRamLink * route, int rnum)
+{
+ int temp, prev, lifeboat = 0;
+
+ if ((rnum <= 0) || (route == NULL))
+ return;
+ /* Write last routes. */
+ rnum--;
+ hwwrite(vortex->mmio,
+ VORTEX_ADB_RTBASE + ((route[rnum] & ADB_MASK) << 2),
+ ROUTE_MASK);
+ while (rnum > 0) {
+ hwwrite(vortex->mmio,
+ VORTEX_ADB_RTBASE +
+ ((route[rnum - 1] & ADB_MASK) << 2), route[rnum]);
+ rnum--;
+ }
+ /* Write first route. */
+ temp =
+ hwread(vortex->mmio,
+ VORTEX_ADB_CHNBASE + (channel << 2)) & ADB_MASK;
+ if (temp == ADB_MASK) {
+ /* First entry on this channel. */
+ hwwrite(vortex->mmio, VORTEX_ADB_CHNBASE + (channel << 2),
+ route[0]);
+ vortex_adb_en_sr(vortex, channel);
+ return;
+ }
+ /* Not first entry on this channel. Need to link. */
+ do {
+ prev = temp;
+ temp =
+ hwread(vortex->mmio,
+ VORTEX_ADB_RTBASE + (temp << 2)) & ADB_MASK;
+ if ((lifeboat++) > ADB_MASK) {
+ dev_err(vortex->card->dev,
+ "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)) {
+ dev_err(vortex->card->dev,
+ "vortex_adb_delroutes: route not found! 0x%x\n",
+ route0);
+ return;
+ }
+ }
+ while (temp != (route0 & ADB_MASK));
+ temp = hwread(vortex->mmio, VORTEX_ADB_RTBASE + (temp << 2));
+ if ((temp & ADB_MASK) == route1)
+ temp = hwread(vortex->mmio, VORTEX_ADB_RTBASE + (temp << 2));
+ /* Make bridge over deleted route. */
+ hwwrite(vortex->mmio, VORTEX_ADB_RTBASE + (prev << 2), temp);
+}
+
+static void
+vortex_route(vortex_t * vortex, int en, unsigned char channel,
+ unsigned char source, unsigned char dest)
+{
+ ADBRamLink route;
+
+ route = ((source & ADB_MASK) << ADB_SHIFT) | (dest & ADB_MASK);
+ if (en) {
+ vortex_adb_addroutes(vortex, channel, &route, 1);
+ if ((source < (OFFSET_SRCOUT + NR_SRC))
+ && (source >= OFFSET_SRCOUT))
+ vortex_src_addWTD(vortex, (source - OFFSET_SRCOUT),
+ channel);
+ else if ((source < (OFFSET_MIXOUT + NR_MIXOUT))
+ && (source >= OFFSET_MIXOUT))
+ vortex_mixer_addWTD(vortex,
+ (source - OFFSET_MIXOUT), channel);
+ } else {
+ vortex_adb_delroutes(vortex, channel, route, route);
+ if ((source < (OFFSET_SRCOUT + NR_SRC))
+ && (source >= OFFSET_SRCOUT))
+ vortex_src_delWTD(vortex, (source - OFFSET_SRCOUT),
+ channel);
+ else if ((source < (OFFSET_MIXOUT + NR_MIXOUT))
+ && (source >= OFFSET_MIXOUT))
+ vortex_mixer_delWTD(vortex,
+ (source - OFFSET_MIXOUT), channel);
+ }
+}
+
+#if 0
+static void
+vortex_routes(vortex_t * vortex, int en, unsigned char channel,
+ unsigned char source, unsigned char dest0, unsigned char dest1)
+{
+ ADBRamLink route[2];
+
+ route[0] = ((source & ADB_MASK) << ADB_SHIFT) | (dest0 & ADB_MASK);
+ route[1] = ((source & ADB_MASK) << ADB_SHIFT) | (dest1 & ADB_MASK);
+
+ if (en) {
+ vortex_adb_addroutes(vortex, channel, route, 2);
+ if ((source < (OFFSET_SRCOUT + NR_SRC))
+ && (source >= (OFFSET_SRCOUT)))
+ vortex_src_addWTD(vortex, (source - OFFSET_SRCOUT),
+ channel);
+ else if ((source < (OFFSET_MIXOUT + NR_MIXOUT))
+ && (source >= (OFFSET_MIXOUT)))
+ vortex_mixer_addWTD(vortex,
+ (source - OFFSET_MIXOUT), channel);
+ } else {
+ vortex_adb_delroutes(vortex, channel, route[0], route[1]);
+ if ((source < (OFFSET_SRCOUT + NR_SRC))
+ && (source >= (OFFSET_SRCOUT)))
+ vortex_src_delWTD(vortex, (source - OFFSET_SRCOUT),
+ channel);
+ else if ((source < (OFFSET_MIXOUT + NR_MIXOUT))
+ && (source >= (OFFSET_MIXOUT)))
+ vortex_mixer_delWTD(vortex,
+ (source - OFFSET_MIXOUT), channel);
+ }
+}
+
+#endif
+/* Route two sources to same target. Sources must be of same class !!! */
+static void
+vortex_routeLRT(vortex_t * vortex, int en, unsigned char ch,
+ unsigned char source0, unsigned char source1,
+ unsigned char dest)
+{
+ ADBRamLink route[2];
+
+ route[0] = ((source0 & ADB_MASK) << ADB_SHIFT) | (dest & ADB_MASK);
+ route[1] = ((source1 & ADB_MASK) << ADB_SHIFT) | (dest & ADB_MASK);
+
+ if (dest < 0x10)
+ route[1] = (route[1] & ~ADB_MASK) | (dest + 0x20); /* fifo A */
+
+ if (en) {
+ vortex_adb_addroutes(vortex, ch, route, 2);
+ if ((source0 < (OFFSET_SRCOUT + NR_SRC))
+ && (source0 >= OFFSET_SRCOUT)) {
+ vortex_src_addWTD(vortex,
+ (source0 - OFFSET_SRCOUT), ch);
+ vortex_src_addWTD(vortex,
+ (source1 - OFFSET_SRCOUT), ch);
+ } else if ((source0 < (OFFSET_MIXOUT + NR_MIXOUT))
+ && (source0 >= OFFSET_MIXOUT)) {
+ vortex_mixer_addWTD(vortex,
+ (source0 - OFFSET_MIXOUT), ch);
+ vortex_mixer_addWTD(vortex,
+ (source1 - OFFSET_MIXOUT), ch);
+ }
+ } else {
+ vortex_adb_delroutes(vortex, ch, route[0], route[1]);
+ if ((source0 < (OFFSET_SRCOUT + NR_SRC))
+ && (source0 >= OFFSET_SRCOUT)) {
+ vortex_src_delWTD(vortex,
+ (source0 - OFFSET_SRCOUT), ch);
+ vortex_src_delWTD(vortex,
+ (source1 - OFFSET_SRCOUT), ch);
+ } else if ((source0 < (OFFSET_MIXOUT + NR_MIXOUT))
+ && (source0 >= OFFSET_MIXOUT)) {
+ vortex_mixer_delWTD(vortex,
+ (source0 - OFFSET_MIXOUT), ch);
+ vortex_mixer_delWTD(vortex,
+ (source1 - OFFSET_MIXOUT), ch);
+ }
+ }
+}
+
+/* Connection stuff */
+
+// Connect adbdma to src('s).
+static void
+vortex_connection_adbdma_src(vortex_t * vortex, int en, unsigned char ch,
+ unsigned char adbdma, unsigned char src)
+{
+ vortex_route(vortex, en, ch, ADB_DMA(adbdma), ADB_SRCIN(src));
+}
+
+// Connect SRC to mixin.
+static void
+vortex_connection_src_mixin(vortex_t * vortex, int en,
+ unsigned char channel, unsigned char src,
+ unsigned char mixin)
+{
+ vortex_route(vortex, en, channel, ADB_SRCOUT(src), ADB_MIXIN(mixin));
+}
+
+// Connect mixin with mix output.
+static void
+vortex_connection_mixin_mix(vortex_t * vortex, int en, unsigned char mixin,
+ unsigned char mix, int a)
+{
+ if (en) {
+ vortex_mix_enableinput(vortex, mix, mixin);
+ vortex_mix_setinputvolumebyte(vortex, mix, mixin, MIX_DEFIGAIN); // added to original code.
+ } else
+ vortex_mix_disableinput(vortex, mix, mixin, a);
+}
+
+// Connect absolut address to mixin.
+static void
+vortex_connection_adb_mixin(vortex_t * vortex, int en,
+ unsigned char channel, unsigned char source,
+ unsigned char mixin)
+{
+ vortex_route(vortex, en, channel, source, ADB_MIXIN(mixin));
+}
+
+static void
+vortex_connection_src_adbdma(vortex_t * vortex, int en, unsigned char ch,
+ unsigned char src, unsigned char adbdma)
+{
+ vortex_route(vortex, en, ch, ADB_SRCOUT(src), ADB_DMA(adbdma));
+}
+
+static void
+vortex_connection_src_src_adbdma(vortex_t * vortex, int en,
+ unsigned char ch, unsigned char src0,
+ unsigned char src1, unsigned char adbdma)
+{
+
+ vortex_routeLRT(vortex, en, ch, ADB_SRCOUT(src0), ADB_SRCOUT(src1),
+ ADB_DMA(adbdma));
+}
+
+// mix to absolut address.
+static void
+vortex_connection_mix_adb(vortex_t * vortex, int en, unsigned char ch,
+ unsigned char mix, unsigned char dest)
+{
+ vortex_route(vortex, en, ch, ADB_MIXOUT(mix), dest);
+ vortex_mix_setvolumebyte(vortex, mix, MIX_DEFOGAIN); // added to original code.
+}
+
+// mixer to src.
+static void
+vortex_connection_mix_src(vortex_t * vortex, int en, unsigned char ch,
+ unsigned char mix, unsigned char src)
+{
+ vortex_route(vortex, en, ch, ADB_MIXOUT(mix), ADB_SRCIN(src));
+ vortex_mix_setvolumebyte(vortex, mix, MIX_DEFOGAIN); // added to original code.
+}
+
+#if 0
+static void
+vortex_connection_adbdma_src_src(vortex_t * vortex, int en,
+ unsigned char channel,
+ unsigned char adbdma, unsigned char src0,
+ unsigned char src1)
+{
+ vortex_routes(vortex, en, channel, ADB_DMA(adbdma),
+ ADB_SRCIN(src0), ADB_SRCIN(src1));
+}
+
+// Connect two mix to AdbDma.
+static void
+vortex_connection_mix_mix_adbdma(vortex_t * vortex, int en,
+ unsigned char ch, unsigned char mix0,
+ unsigned char mix1, unsigned char adbdma)
+{
+
+ ADBRamLink routes[2];
+ routes[0] =
+ (((mix0 +
+ OFFSET_MIXOUT) & ADB_MASK) << ADB_SHIFT) | (adbdma & ADB_MASK);
+ routes[1] =
+ (((mix1 + OFFSET_MIXOUT) & ADB_MASK) << ADB_SHIFT) | ((adbdma +
+ 0x20) &
+ ADB_MASK);
+ if (en) {
+ vortex_adb_addroutes(vortex, ch, routes, 0x2);
+ vortex_mixer_addWTD(vortex, mix0, ch);
+ vortex_mixer_addWTD(vortex, mix1, ch);
+ } else {
+ vortex_adb_delroutes(vortex, ch, routes[0], routes[1]);
+ vortex_mixer_delWTD(vortex, mix0, ch);
+ vortex_mixer_delWTD(vortex, mix1, ch);
+ }
+}
+#endif
+
+/* CODEC connect. */
+
+static void
+vortex_connect_codecplay(vortex_t * vortex, int en, unsigned char mixers[])
+{
+#ifdef CHIP_AU8820
+ vortex_connection_mix_adb(vortex, en, 0x11, mixers[0], ADB_CODECOUT(0));
+ vortex_connection_mix_adb(vortex, en, 0x11, mixers[1], ADB_CODECOUT(1));
+#else
+#if 1
+ // Connect front channels through EQ.
+ vortex_connection_mix_adb(vortex, en, 0x11, mixers[0], ADB_EQIN(0));
+ vortex_connection_mix_adb(vortex, en, 0x11, mixers[1], ADB_EQIN(1));
+ /* Lower volume, since EQ has some gain. */
+ vortex_mix_setvolumebyte(vortex, mixers[0], 0);
+ vortex_mix_setvolumebyte(vortex, mixers[1], 0);
+ vortex_route(vortex, en, 0x11, ADB_EQOUT(0), ADB_CODECOUT(0));
+ vortex_route(vortex, en, 0x11, ADB_EQOUT(1), ADB_CODECOUT(1));
+
+ /* Check if reg 0x28 has SDAC bit set. */
+ if (VORTEX_IS_QUAD(vortex)) {
+ /* Rear channel. Note: ADB_CODECOUT(0+2) and (1+2) is for AC97 modem */
+ vortex_connection_mix_adb(vortex, en, 0x11, mixers[2],
+ ADB_CODECOUT(0 + 4));
+ vortex_connection_mix_adb(vortex, en, 0x11, mixers[3],
+ ADB_CODECOUT(1 + 4));
+ /* pr_debug( "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 const 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 int
+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);
+ /*
+ pr_debug(
+ "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);
+ /*
+ pr_debug(
+ "vortex: ResManager: type %d in %d\n",
+ restype, i);
+ */
+ return i;
+ }
+ }
+ }
+ dev_err(vortex->card->dev,
+ "FATAL: ResManager: resource type %d exhausted.\n",
+ restype);
+ return -ENOMEM;
+}
+
+/* Default Connections */
+
+static void vortex_connect_default(vortex_t * vortex, int en)
+{
+ // Connect AC97 codec.
+ vortex->mixplayb[0] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXOUT);
+ vortex->mixplayb[1] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXOUT);
+ if (VORTEX_IS_QUAD(vortex)) {
+ vortex->mixplayb[2] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXOUT);
+ vortex->mixplayb[3] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXOUT);
+ }
+ vortex_connect_codecplay(vortex, en, vortex->mixplayb);
+
+ vortex->mixcapt[0] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXIN);
+ vortex->mixcapt[1] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXIN);
+ vortex_connect_codecrec(vortex, en, MIX_CAPT(0), MIX_CAPT(1));
+
+ // Connect SPDIF
+#ifndef CHIP_AU8820
+ vortex->mixspdif[0] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXOUT);
+ vortex->mixspdif[1] = vortex_adb_checkinout(vortex, vortex->fixed_res, en,
+ VORTEX_RESOURCE_MIXOUT);
+ vortex_connection_mix_adb(vortex, en, 0x14, vortex->mixspdif[0],
+ ADB_SPDIFOUT(0));
+ vortex_connection_mix_adb(vortex, en, 0x14, vortex->mixspdif[1],
+ ADB_SPDIFOUT(1));
+#endif
+ // Connect WT
+#ifndef CHIP_AU8810
+ vortex_wt_connect(vortex, en);
+#endif
+ // A3D (crosstalk canceler and A3D slices). AU8810 disabled for now.
+#ifndef CHIP_AU8820
+ vortex_Vort3D_connect(vortex, en);
+#endif
+ // Connect I2S
+
+ // Connect DSP interface for SQ3500 turbo (not here i think...)
+
+ // Connect AC98 modem codec
+
+}
+
+/*
+ Allocate nr_ch pcm audio routes if dma < 0. If dma >= 0, existing routes
+ are deallocated.
+ dma: DMA engine routes to be deallocated when dma >= 0.
+ nr_ch: Number of channels to be de/allocated.
+ dir: direction of stream. Uses same values as substream->stream.
+ type: Type of audio output/source (codec, spdif, i2s, dsp, etc)
+ Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0.
+*/
+static int
+vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
+ int type, int subdev)
+{
+ stream_t *stream;
+ int i, en;
+ struct pcm_vol *p;
+
+ if (dma >= 0) {
+ en = 0;
+ vortex_adb_checkinout(vortex,
+ vortex->dma_adb[dma].resources, en,
+ VORTEX_RESOURCE_DMA);
+ } else {
+ en = 1;
+ dma = vortex_adb_checkinout(vortex, NULL, en,
+ VORTEX_RESOURCE_DMA);
+ if (dma < 0)
+ return -EBUSY;
+ }
+
+ stream = &vortex->dma_adb[dma];
+ stream->dma = dma;
+ stream->dir = dir;
+ stream->type = type;
+
+ /* PLAYBACK ROUTES. */
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ int src[4], mix[4], ch_top;
+#ifndef CHIP_AU8820
+ int a3d = 0;
+#endif
+ /* Get SRC and MIXER hardware resources. */
+ if (stream->type != VORTEX_PCM_SPDIF) {
+ for (i = 0; i < nr_ch; i++) {
+ src[i] = vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_SRC);
+ if (src[i] < 0) {
+ memset(stream->resources, 0,
+ sizeof(stream->resources));
+ return -EBUSY;
+ }
+ if (stream->type != VORTEX_PCM_A3D) {
+ mix[i] = vortex_adb_checkinout(vortex,
+ stream->resources,
+ en,
+ VORTEX_RESOURCE_MIXIN);
+ if (mix[i] < 0) {
+ memset(stream->resources,
+ 0,
+ sizeof(stream->resources));
+ return -EBUSY;
+ }
+ }
+ }
+ }
+#ifndef CHIP_AU8820
+ if (stream->type == VORTEX_PCM_A3D) {
+ a3d = vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_A3D);
+ if (a3d < 0) {
+ memset(stream->resources, 0,
+ sizeof(stream->resources));
+ dev_err(vortex->card->dev,
+ "out of A3D sources. Sorry\n");
+ return -EBUSY;
+ }
+ /* (De)Initialize A3D hardware source. */
+ vortex_Vort3D_InitializeSource(&vortex->a3d[a3d], en,
+ vortex);
+ }
+ /* Make SPDIF out exclusive to "spdif" device when in use. */
+ if ((stream->type == VORTEX_PCM_SPDIF) && (en)) {
+ vortex_route(vortex, 0, 0x14,
+ ADB_MIXOUT(vortex->mixspdif[0]),
+ ADB_SPDIFOUT(0));
+ vortex_route(vortex, 0, 0x14,
+ ADB_MIXOUT(vortex->mixspdif[1]),
+ ADB_SPDIFOUT(1));
+ }
+#endif
+ /* Make playback routes. */
+ for (i = 0; i < nr_ch; i++) {
+ if (stream->type == VORTEX_PCM_ADB) {
+ vortex_connection_adbdma_src(vortex, en,
+ src[nr_ch - 1],
+ dma,
+ src[i]);
+ vortex_connection_src_mixin(vortex, en,
+ 0x11, src[i],
+ mix[i]);
+ vortex_connection_mixin_mix(vortex, en,
+ mix[i],
+ MIX_PLAYB(i), 0);
+#ifndef CHIP_AU8820
+ vortex_connection_mixin_mix(vortex, en,
+ mix[i],
+ MIX_SPDIF(i % 2), 0);
+ vortex_mix_setinputvolumebyte(vortex,
+ MIX_SPDIF(i % 2),
+ mix[i],
+ MIX_DEFIGAIN);
+#endif
+ }
+#ifndef CHIP_AU8820
+ if (stream->type == VORTEX_PCM_A3D) {
+ vortex_connection_adbdma_src(vortex, en,
+ src[nr_ch - 1],
+ dma,
+ src[i]);
+ vortex_route(vortex, en, 0x11, ADB_SRCOUT(src[i]), ADB_A3DIN(a3d));
+ /* XTalk test. */
+ //vortex_route(vortex, en, 0x11, dma, ADB_XTALKIN(i?9:4));
+ //vortex_route(vortex, en, 0x11, ADB_SRCOUT(src[i]), ADB_XTALKIN(i?4:9));
+ }
+ if (stream->type == VORTEX_PCM_SPDIF)
+ vortex_route(vortex, en, 0x14,
+ ADB_DMA(stream->dma),
+ ADB_SPDIFOUT(i));
+#endif
+ }
+ if (stream->type != VORTEX_PCM_SPDIF && stream->type != VORTEX_PCM_A3D) {
+ ch_top = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+ for (i = nr_ch; i < ch_top; i++) {
+ vortex_connection_mixin_mix(vortex, en,
+ mix[i % nr_ch],
+ MIX_PLAYB(i), 0);
+#ifndef CHIP_AU8820
+ vortex_connection_mixin_mix(vortex, en,
+ mix[i % nr_ch],
+ MIX_SPDIF(i % 2),
+ 0);
+ vortex_mix_setinputvolumebyte(vortex,
+ MIX_SPDIF(i % 2),
+ mix[i % nr_ch],
+ MIX_DEFIGAIN);
+#endif
+ }
+ if (stream->type == VORTEX_PCM_ADB && en) {
+ p = &vortex->pcm_vol[subdev];
+ p->dma = dma;
+ for (i = 0; i < nr_ch; i++)
+ p->mixin[i] = mix[i];
+ for (i = 0; i < ch_top; i++)
+ p->vol[i] = 0;
+ }
+ }
+#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];
+
+ if (nr_ch < 1)
+ return -EINVAL;
+
+ /* Get SRC and MIXER hardware resources. */
+ for (i = 0; i < nr_ch; i++) {
+ mix[i] = vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_MIXOUT);
+ if (mix[i] < 0) {
+ memset(stream->resources, 0,
+ sizeof(stream->resources));
+ return -EBUSY;
+ }
+ src[i] = vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_SRC);
+ if (src[i] < 0) {
+ memset(stream->resources, 0,
+ sizeof(stream->resources));
+ return -EBUSY;
+ }
+ }
+
+ /* Make capture routes. */
+ vortex_connection_mixin_mix(vortex, en, MIX_CAPT(0), mix[0], 0);
+ vortex_connection_mix_src(vortex, en, 0x11, mix[0], src[0]);
+ if (nr_ch == 1) {
+ vortex_connection_mixin_mix(vortex, en,
+ MIX_CAPT(1), mix[0], 0);
+ vortex_connection_src_adbdma(vortex, en,
+ src[0],
+ src[0], dma);
+ } else {
+ vortex_connection_mixin_mix(vortex, en,
+ MIX_CAPT(1), mix[1], 0);
+ vortex_connection_mix_src(vortex, en, 0x11, mix[1],
+ src[1]);
+ vortex_connection_src_src_adbdma(vortex, en,
+ src[1], src[0],
+ src[1], dma);
+ }
+ }
+ vortex->dma_adb[dma].nr_ch = nr_ch;
+
+#if 0
+ /* AC97 Codec channel setup. FIXME: this has no effect on some cards !! */
+ if (nr_ch < 4) {
+ /* Copy stereo to rear channel (surround) */
+ snd_ac97_write_cache(vortex->codec,
+ AC97_SIGMATEL_DAC2INVERT,
+ snd_ac97_read(vortex->codec,
+ AC97_SIGMATEL_DAC2INVERT)
+ | 4);
+ } else {
+ /* Allow separate front and rear channels. */
+ snd_ac97_write_cache(vortex->codec,
+ AC97_SIGMATEL_DAC2INVERT,
+ snd_ac97_read(vortex->codec,
+ AC97_SIGMATEL_DAC2INVERT)
+ & ~((u32)
+ 4));
+ }
+#endif
+ return dma;
+}
+
+/*
+ Set the SampleRate of the SRC's attached to the given DMA engine.
+ */
+static void
+vortex_adb_setsrc(vortex_t * vortex, int adbdma, unsigned int rate, int dir)
+{
+ stream_t *stream = &(vortex->dma_adb[adbdma]);
+ int i, cvrt;
+
+ /* dir=1:play ; dir=0:rec */
+ if (dir)
+ cvrt = SRC_RATIO(rate, 48000);
+ else
+ cvrt = SRC_RATIO(48000, rate);
+
+ /* Setup SRC's */
+ for (i = 0; i < NR_SRC; i++) {
+ if (stream->resources[VORTEX_RESOURCE_SRC] & (1 << i))
+ vortex_src_setupchannel(vortex, i, cvrt, 0, 0, i, dir, 1, cvrt, dir);
+ }
+}
+
+// Timer and ISR functions.
+
+static void vortex_settimer(vortex_t * vortex, int period)
+{
+ //set the timer period to <period> 48000ths of a second.
+ hwwrite(vortex->mmio, VORTEX_IRQ_STAT, period);
+}
+
+#if 0
+static void vortex_enable_timer_int(vortex_t * card)
+{
+ hwwrite(card->mmio, VORTEX_IRQ_CTRL,
+ hwread(card->mmio, VORTEX_IRQ_CTRL) | IRQ_TIMER | 0x60);
+}
+
+static void vortex_disable_timer_int(vortex_t * card)
+{
+ hwwrite(card->mmio, VORTEX_IRQ_CTRL,
+ hwread(card->mmio, VORTEX_IRQ_CTRL) & ~IRQ_TIMER);
+}
+
+#endif
+static void vortex_enable_int(vortex_t * card)
+{
+ // CAsp4ISR__EnableVortexInt_void_
+ hwwrite(card->mmio, VORTEX_CTRL,
+ hwread(card->mmio, VORTEX_CTRL) | CTRL_IRQ_ENABLE);
+ hwwrite(card->mmio, VORTEX_IRQ_CTRL,
+ (hwread(card->mmio, VORTEX_IRQ_CTRL) & 0xffffefc0) | 0x24);
+}
+
+static void vortex_disable_int(vortex_t * card)
+{
+ hwwrite(card->mmio, VORTEX_CTRL,
+ hwread(card->mmio, VORTEX_CTRL) & ~CTRL_IRQ_ENABLE);
+}
+
+static irqreturn_t vortex_interrupt(int irq, void *dev_id)
+{
+ vortex_t *vortex = dev_id;
+ int i, handled;
+ u32 source;
+
+ //check if the interrupt is ours.
+ if (!(hwread(vortex->mmio, VORTEX_STAT) & 0x1))
+ return IRQ_NONE;
+
+ // This is the Interrupt 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) {
+ dev_err(vortex->card->dev, "missing irq source\n");
+ return IRQ_NONE;
+ }
+
+ handled = 0;
+ // Attend every interrupt source.
+ if (unlikely(source & IRQ_ERR_MASK)) {
+ if (source & IRQ_FATAL) {
+ dev_err(vortex->card->dev, "IRQ fatal error\n");
+ }
+ if (source & IRQ_PARITY) {
+ dev_err(vortex->card->dev, "IRQ parity error\n");
+ }
+ if (source & IRQ_REG) {
+ dev_err(vortex->card->dev, "IRQ reg error\n");
+ }
+ if (source & IRQ_FIFO) {
+ dev_err(vortex->card->dev, "IRQ fifo error\n");
+ }
+ if (source & IRQ_DMA) {
+ dev_err(vortex->card->dev, "IRQ dma error\n");
+ }
+ handled = 1;
+ }
+ if (source & IRQ_PCMOUT) {
+ /* ALSA period acknowledge. */
+ spin_lock(&vortex->lock);
+ for (i = 0; i < NR_ADB; i++) {
+ if (vortex->dma_adb[i].fifo_status == FIFO_START) {
+ if (!vortex_adbdma_bufshift(vortex, i))
+ continue;
+ spin_unlock(&vortex->lock);
+ snd_pcm_period_elapsed(vortex->dma_adb[i].
+ substream);
+ spin_lock(&vortex->lock);
+ }
+ }
+#ifndef CHIP_AU8810
+ for (i = 0; i < NR_WT; i++) {
+ if (vortex->dma_wt[i].fifo_status == FIFO_START) {
+ /* FIXME: we ignore the return value from
+ * vortex_wtdma_bufshift() below as the delta
+ * calculation seems not working for wavetable
+ * by some reason
+ */
+ vortex_wtdma_bufshift(vortex, i);
+ spin_unlock(&vortex->lock);
+ snd_pcm_period_elapsed(vortex->dma_wt[i].
+ substream);
+ spin_lock(&vortex->lock);
+ }
+ }
+#endif
+ spin_unlock(&vortex->lock);
+ handled = 1;
+ }
+ //Acknowledge the Timer interrupt
+ if (source & IRQ_TIMER) {
+ hwread(vortex->mmio, VORTEX_IRQ_STAT);
+ handled = 1;
+ }
+ if ((source & IRQ_MIDI) && vortex->rmidi) {
+ snd_mpu401_uart_interrupt(vortex->irq,
+ vortex->rmidi->private_data);
+ handled = 1;
+ }
+
+ if (!handled) {
+ dev_err(vortex->card->dev, "unknown irq source %x\n", source);
+ }
+ return IRQ_RETVAL(handled);
+}
+
+/* Codec */
+
+#define POLL_COUNT 1000
+static void vortex_codec_init(vortex_t * vortex)
+{
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ /* the windows driver writes -i, so we write -i */
+ hwwrite(vortex->mmio, (VORTEX_CODEC_CHN + (i << 2)), -i);
+ msleep(2);
+ }
+ if (0) {
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x8068);
+ msleep(1);
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00e8);
+ msleep(1);
+ } else {
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00a8);
+ msleep(2);
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x80a8);
+ msleep(2);
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x80e8);
+ msleep(2);
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x80a8);
+ msleep(2);
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00a8);
+ msleep(2);
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0x00e8);
+ }
+ for (i = 0; i < 32; i++) {
+ hwwrite(vortex->mmio, (VORTEX_CODEC_CHN + (i << 2)), -i);
+ msleep(5);
+ }
+ hwwrite(vortex->mmio, VORTEX_CODEC_CTRL, 0xe8);
+ msleep(1);
+ /* Enable codec channels 0 and 1. */
+ hwwrite(vortex->mmio, VORTEX_CODEC_EN,
+ hwread(vortex->mmio, VORTEX_CODEC_EN) | EN_CODEC);
+}
+
+static void
+vortex_codec_write(struct snd_ac97 * codec, unsigned short addr, unsigned short data)
+{
+
+ vortex_t *card = (vortex_t *) codec->private_data;
+ unsigned int lifeboat = 0;
+
+ /* wait for transactions to clear */
+ while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
+ udelay(100);
+ if (lifeboat++ > POLL_COUNT) {
+ dev_err(card->card->dev, "ac97 codec stuck busy\n");
+ return;
+ }
+ }
+ /* write register */
+ hwwrite(card->mmio, VORTEX_CODEC_IO,
+ ((addr << VORTEX_CODEC_ADDSHIFT) & VORTEX_CODEC_ADDMASK) |
+ ((data << VORTEX_CODEC_DATSHIFT) & VORTEX_CODEC_DATMASK) |
+ VORTEX_CODEC_WRITE |
+ (codec->num << VORTEX_CODEC_ID_SHIFT) );
+
+ /* Flush Caches. */
+ hwread(card->mmio, VORTEX_CODEC_IO);
+}
+
+static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short addr)
+{
+
+ vortex_t *card = (vortex_t *) codec->private_data;
+ u32 read_addr, data;
+ unsigned lifeboat = 0;
+
+ /* wait for transactions to clear */
+ while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
+ udelay(100);
+ if (lifeboat++ > POLL_COUNT) {
+ dev_err(card->card->dev, "ac97 codec stuck busy\n");
+ return 0xffff;
+ }
+ }
+ /* set up read address */
+ read_addr = ((addr << VORTEX_CODEC_ADDSHIFT) & VORTEX_CODEC_ADDMASK) |
+ (codec->num << VORTEX_CODEC_ID_SHIFT) ;
+ hwwrite(card->mmio, VORTEX_CODEC_IO, read_addr);
+
+ /* wait for address */
+ do {
+ udelay(100);
+ data = hwread(card->mmio, VORTEX_CODEC_IO);
+ if (lifeboat++ > POLL_COUNT) {
+ dev_err(card->card->dev,
+ "ac97 address never arrived\n");
+ return 0xffff;
+ }
+ } while ((data & VORTEX_CODEC_ADDMASK) !=
+ (addr << VORTEX_CODEC_ADDSHIFT));
+
+ /* return data. */
+ return (u16) (data & VORTEX_CODEC_DATMASK);
+}
+
+/* SPDIF support */
+
+static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode)
+{
+ int i, this_38 = 0, this_04 = 0, this_08 = 0, this_0c = 0;
+
+ /* CAsp4Spdif::InitializeSpdifHardware(void) */
+ hwwrite(vortex->mmio, VORTEX_SPDIF_FLAGS,
+ hwread(vortex->mmio, VORTEX_SPDIF_FLAGS) & 0xfff3fffd);
+ //for (i=0x291D4; i<0x29200; i+=4)
+ for (i = 0; i < 11; i++)
+ hwwrite(vortex->mmio, VORTEX_SPDIF_CFG1 + (i << 2), 0);
+ //hwwrite(vortex->mmio, 0x29190, hwread(vortex->mmio, 0x29190) | 0xc0000);
+ hwwrite(vortex->mmio, VORTEX_CODEC_EN,
+ hwread(vortex->mmio, VORTEX_CODEC_EN) | EN_SPDIF);
+
+ /* CAsp4Spdif::ProgramSRCInHardware(enum SPDIF_SR,enum SPDIFMODE) */
+ if (this_04 && this_08) {
+ int edi;
+
+ i = (((0x5DC00000 / spdif_sr) + 1) >> 1);
+ if (i > 0x800) {
+ if (i < 0x1ffff)
+ edi = (i >> 1);
+ else
+ edi = 0x1ffff;
+ } else {
+ 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)
+{
+
+ dev_info(vortex->card->dev, "init started\n");
+ /* Hardware Init. */
+ hwwrite(vortex->mmio, VORTEX_CTRL, 0xffffffff);
+ msleep(5);
+ hwwrite(vortex->mmio, VORTEX_CTRL,
+ hwread(vortex->mmio, VORTEX_CTRL) & 0xffdfffff);
+ msleep(5);
+ /* Reset IRQ flags */
+ hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffffffff);
+ hwread(vortex->mmio, VORTEX_IRQ_STAT);
+
+ vortex_codec_init(vortex);
+
+#ifdef CHIP_AU8830
+ hwwrite(vortex->mmio, VORTEX_CTRL,
+ hwread(vortex->mmio, VORTEX_CTRL) | 0x1000000);
+#endif
+
+ /* Init audio engine. */
+ vortex_adbdma_init(vortex);
+ hwwrite(vortex->mmio, VORTEX_ENGINE_CTRL, 0x0); //, 0xc83c7e58, 0xc5f93e58
+ vortex_adb_init(vortex);
+ /* Init processing blocks. */
+ vortex_fifo_init(vortex);
+ vortex_mixer_init(vortex);
+ vortex_srcblock_init(vortex);
+#ifndef CHIP_AU8820
+ vortex_eq_init(vortex);
+ vortex_spdif_init(vortex, 48000, 1);
+ vortex_Vort3D_enable(vortex);
+#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);
+
+ dev_info(vortex->card->dev, "init.... done.\n");
+ spin_lock_init(&vortex->lock);
+
+ return 0;
+}
+
+static int vortex_core_shutdown(vortex_t * vortex)
+{
+
+ dev_info(vortex->card->dev, "shutdown started\n");
+#ifndef CHIP_AU8820
+ vortex_eq_free(vortex);
+ vortex_Vort3D_disable(vortex);
+#endif
+ //vortex_disable_timer_int(vortex);
+ vortex_disable_int(vortex);
+ vortex_connect_default(vortex, 0);
+ /* Reset all DMA fifos. */
+ vortex_fifo_init(vortex);
+ /* Erase all audio routes. */
+ vortex_adb_init(vortex);
+
+ /* Disable MPU401 */
+ //hwwrite(vortex->mmio, VORTEX_IRQ_CTRL, hwread(vortex->mmio, VORTEX_IRQ_CTRL) & ~IRQ_MIDI);
+ //hwwrite(vortex->mmio, VORTEX_CTRL, hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_EN);
+
+ hwwrite(vortex->mmio, VORTEX_IRQ_CTRL, 0);
+ hwwrite(vortex->mmio, VORTEX_CTRL, 0);
+ msleep(5);
+ hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffff);
+
+ dev_info(vortex->card->dev, "shutdown.... done.\n");
+ return 0;
+}
+
+/* Alsa support. */
+
+static int vortex_alsafmt_aspfmt(snd_pcm_format_t alsafmt, vortex_t *v)
+{
+ 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;
+ dev_err(v->card->dev,
+ "format unsupported %d\n", alsafmt);
+ break;
+ }
+ return fmt;
+}
+
+/* Some not yet useful translations. */
+#if 0
+typedef enum {
+ ASPFMTLINEAR16 = 0, /* 0x8 */
+ ASPFMTLINEAR8, /* 0x1 */
+ ASPFMTULAW, /* 0x2 */
+ ASPFMTALAW, /* 0x3 */
+ ASPFMTSPORT, /* ? */
+ ASPFMTSPDIF, /* ? */
+} ASPENCODING;
+
+static int
+vortex_translateformat(vortex_t * vortex, char bits, char nch, int encod)
+{
+ int a, this_194;
+
+ if ((bits != 8) && (bits != 16))
+ return -1;
+
+ switch (encod) {
+ case 0:
+ if (bits == 0x10)
+ a = 8; // 16 bit
+ break;
+ case 1:
+ if (bits == 8)
+ a = 1; // 8 bit
+ break;
+ case 2:
+ a = 2; // U_LAW
+ break;
+ case 3:
+ a = 3; // A_LAW
+ break;
+ }
+ switch (nch) {
+ case 1:
+ this_194 = 0;
+ break;
+ case 2:
+ this_194 = 1;
+ break;
+ case 4:
+ this_194 = 1;
+ break;
+ case 6:
+ this_194 = 1;
+ break;
+ }
+ return (a);
+}
+
+static void vortex_cdmacore_setformat(vortex_t * vortex, int bits, int nch)
+{
+ short int d, this_148;
+
+ d = ((bits >> 3) * nch);
+ this_148 = 0xbb80 / d;
+}
+#endif
diff --git a/sound/pci/au88x0/au88x0_eq.c b/sound/pci/au88x0/au88x0_eq.c
new file mode 100644
index 000000000..71c13100d
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_eq.c
@@ -0,0 +1,922 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/***************************************************************************
+ * 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.
+ *
+ ****************************************************************************/
+
+/*
+ */
+
+/*
+ The Aureal Hardware EQ is found on AU8810 and AU8830 chips only.
+ it has 4 inputs (2 for general mix, 2 for A3D) and 2 outputs (supposed
+ to be routed to the codec).
+*/
+
+#include "au88x0.h"
+#include "au88x0_eq.h"
+#include "au88x0_eqdata.c"
+
+#define VORTEX_EQ_BASE 0x2b000
+#define VORTEX_EQ_DEST (VORTEX_EQ_BASE + 0x410)
+#define VORTEX_EQ_SOURCE (VORTEX_EQ_BASE + 0x430)
+#define VORTEX_EQ_CTRL (VORTEX_EQ_BASE + 0x440)
+
+#define VORTEX_BAND_COEFF_SIZE 0x30
+
+/* CEqHw.s */
+static void vortex_EqHw_SetTimeConsts(vortex_t * vortex, u16 gain, u16 level)
+{
+ hwwrite(vortex->mmio, 0x2b3c4, gain);
+ hwwrite(vortex->mmio, 0x2b3c8, level);
+}
+
+static inline u16 sign_invert(u16 a)
+{
+ /* -(-32768) -> -32768 so we do -(-32768) -> 32767 to make the result positive */
+ if (a == (u16)-32768)
+ return 32767;
+ else
+ return -a;
+}
+
+static void vortex_EqHw_SetLeftCoefs(vortex_t *vortex, const u16 coefs[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int i = 0, n /*esp2c */;
+
+ for (n = 0; n < eqhw->this04; n++) {
+ hwwrite(vortex->mmio, 0x2b000 + n * 0x30, coefs[i + 0]);
+ hwwrite(vortex->mmio, 0x2b004 + n * 0x30, coefs[i + 1]);
+
+ if (eqhw->this08 == 0) {
+ hwwrite(vortex->mmio, 0x2b008 + n * 0x30, coefs[i + 2]);
+ hwwrite(vortex->mmio, 0x2b00c + n * 0x30, coefs[i + 3]);
+ hwwrite(vortex->mmio, 0x2b010 + n * 0x30, coefs[i + 4]);
+ } else {
+ hwwrite(vortex->mmio, 0x2b008 + n * 0x30, sign_invert(coefs[2 + i]));
+ hwwrite(vortex->mmio, 0x2b00c + n * 0x30, sign_invert(coefs[3 + i]));
+ hwwrite(vortex->mmio, 0x2b010 + n * 0x30, sign_invert(coefs[4 + i]));
+ }
+ i += 5;
+ }
+}
+
+static void vortex_EqHw_SetRightCoefs(vortex_t *vortex, const u16 coefs[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int i = 0, n /*esp2c */;
+
+ for (n = 0; n < eqhw->this04; n++) {
+ hwwrite(vortex->mmio, 0x2b1e0 + n * 0x30, coefs[0 + i]);
+ hwwrite(vortex->mmio, 0x2b1e4 + n * 0x30, coefs[1 + i]);
+
+ if (eqhw->this08 == 0) {
+ hwwrite(vortex->mmio, 0x2b1e8 + n * 0x30, coefs[2 + i]);
+ hwwrite(vortex->mmio, 0x2b1ec + n * 0x30, coefs[3 + i]);
+ hwwrite(vortex->mmio, 0x2b1f0 + n * 0x30, coefs[4 + i]);
+ } else {
+ hwwrite(vortex->mmio, 0x2b1e8 + n * 0x30, sign_invert(coefs[2 + i]));
+ hwwrite(vortex->mmio, 0x2b1ec + n * 0x30, sign_invert(coefs[3 + i]));
+ hwwrite(vortex->mmio, 0x2b1f0 + n * 0x30, sign_invert(coefs[4 + i]));
+ }
+ i += 5;
+ }
+
+}
+
+static void vortex_EqHw_SetLeftStates(vortex_t *vortex, const u16 a[], const u16 b[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int i = 0, ebx;
+
+ hwwrite(vortex->mmio, 0x2b3fc, a[0]);
+ hwwrite(vortex->mmio, 0x2b400, a[1]);
+
+ for (ebx = 0; ebx < eqhw->this04; ebx++) {
+ hwwrite(vortex->mmio, 0x2b014 + (i * 0xc), b[i]);
+ hwwrite(vortex->mmio, 0x2b018 + (i * 0xc), b[1 + i]);
+ hwwrite(vortex->mmio, 0x2b01c + (i * 0xc), b[2 + i]);
+ hwwrite(vortex->mmio, 0x2b020 + (i * 0xc), b[3 + i]);
+ i += 4;
+ }
+}
+
+static void vortex_EqHw_SetRightStates(vortex_t *vortex, const u16 a[], const u16 b[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int i = 0, ebx;
+
+ hwwrite(vortex->mmio, 0x2b404, a[0]);
+ hwwrite(vortex->mmio, 0x2b408, a[1]);
+
+ for (ebx = 0; ebx < eqhw->this04; ebx++) {
+ hwwrite(vortex->mmio, 0x2b1f4 + (i * 0xc), b[i]);
+ hwwrite(vortex->mmio, 0x2b1f8 + (i * 0xc), b[1 + i]);
+ hwwrite(vortex->mmio, 0x2b1fc + (i * 0xc), b[2 + i]);
+ hwwrite(vortex->mmio, 0x2b200 + (i * 0xc), b[3 + i]);
+ i += 4;
+ }
+}
+
+#if 0
+static void vortex_EqHw_GetTimeConsts(vortex_t * vortex, u16 * a, u16 * b)
+{
+ *a = hwread(vortex->mmio, 0x2b3c4);
+ *b = hwread(vortex->mmio, 0x2b3c8);
+}
+
+static void vortex_EqHw_GetLeftCoefs(vortex_t * vortex, u16 a[])
+{
+
+}
+
+static void vortex_EqHw_GetRightCoefs(vortex_t * vortex, u16 a[])
+{
+
+}
+
+static void vortex_EqHw_GetLeftStates(vortex_t * vortex, u16 * a, u16 b[])
+{
+
+}
+
+static void vortex_EqHw_GetRightStates(vortex_t * vortex, u16 * a, u16 b[])
+{
+
+}
+
+#endif
+/* Mix Gains */
+static void vortex_EqHw_SetBypassGain(vortex_t * vortex, u16 a, u16 b)
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ if (eqhw->this08 == 0) {
+ hwwrite(vortex->mmio, 0x2b3d4, a);
+ hwwrite(vortex->mmio, 0x2b3ec, b);
+ } else {
+ hwwrite(vortex->mmio, 0x2b3d4, sign_invert(a));
+ hwwrite(vortex->mmio, 0x2b3ec, sign_invert(b));
+ }
+}
+
+static void vortex_EqHw_SetA3DBypassGain(vortex_t * vortex, u16 a, u16 b)
+{
+
+ hwwrite(vortex->mmio, 0x2b3e0, a);
+ hwwrite(vortex->mmio, 0x2b3f8, b);
+}
+
+#if 0
+static void vortex_EqHw_SetCurrBypassGain(vortex_t * vortex, u16 a, u16 b)
+{
+
+ hwwrite(vortex->mmio, 0x2b3d0, a);
+ hwwrite(vortex->mmio, 0x2b3e8, b);
+}
+
+static void vortex_EqHw_SetCurrA3DBypassGain(vortex_t * vortex, u16 a, u16 b)
+{
+
+ hwwrite(vortex->mmio, 0x2b3dc, a);
+ hwwrite(vortex->mmio, 0x2b3f4, b);
+}
+
+#endif
+static void
+vortex_EqHw_SetLeftGainsSingleTarget(vortex_t * vortex, u16 index, u16 b)
+{
+ hwwrite(vortex->mmio, 0x2b02c + (index * 0x30), b);
+}
+
+static void
+vortex_EqHw_SetRightGainsSingleTarget(vortex_t * vortex, u16 index, u16 b)
+{
+ hwwrite(vortex->mmio, 0x2b20c + (index * 0x30), b);
+}
+
+static void vortex_EqHw_SetLeftGainsTarget(vortex_t *vortex, const u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx;
+
+ for (ebx = 0; ebx < eqhw->this04; ebx++) {
+ hwwrite(vortex->mmio, 0x2b02c + ebx * 0x30, a[ebx]);
+ }
+}
+
+static void vortex_EqHw_SetRightGainsTarget(vortex_t *vortex, const u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx;
+
+ for (ebx = 0; ebx < eqhw->this04; ebx++) {
+ hwwrite(vortex->mmio, 0x2b20c + ebx * 0x30, a[ebx]);
+ }
+}
+
+static void vortex_EqHw_SetLeftGainsCurrent(vortex_t *vortex, const u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx;
+
+ for (ebx = 0; ebx < eqhw->this04; ebx++) {
+ hwwrite(vortex->mmio, 0x2b028 + ebx * 0x30, a[ebx]);
+ }
+}
+
+static void vortex_EqHw_SetRightGainsCurrent(vortex_t *vortex, const u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx;
+
+ for (ebx = 0; ebx < eqhw->this04; ebx++) {
+ hwwrite(vortex->mmio, 0x2b208 + ebx * 0x30, a[ebx]);
+ }
+}
+
+#if 0
+static void vortex_EqHw_GetLeftGainsTarget(vortex_t * vortex, u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx = 0;
+
+ if (eqhw->this04 < 0)
+ return;
+
+ do {
+ a[ebx] = hwread(vortex->mmio, 0x2b02c + ebx * 0x30);
+ ebx++;
+ }
+ while (ebx < eqhw->this04);
+}
+
+static void vortex_EqHw_GetRightGainsTarget(vortex_t * vortex, u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx = 0;
+
+ if (eqhw->this04 < 0)
+ return;
+
+ do {
+ a[ebx] = hwread(vortex->mmio, 0x2b20c + ebx * 0x30);
+ ebx++;
+ }
+ while (ebx < eqhw->this04);
+}
+
+static void vortex_EqHw_GetLeftGainsCurrent(vortex_t * vortex, u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx = 0;
+
+ if (eqhw->this04 < 0)
+ return;
+
+ do {
+ a[ebx] = hwread(vortex->mmio, 0x2b028 + ebx * 0x30);
+ ebx++;
+ }
+ while (ebx < eqhw->this04);
+}
+
+static void vortex_EqHw_GetRightGainsCurrent(vortex_t * vortex, u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx = 0;
+
+ if (eqhw->this04 < 0)
+ return;
+
+ do {
+ a[ebx] = hwread(vortex->mmio, 0x2b208 + ebx * 0x30);
+ ebx++;
+ }
+ while (ebx < eqhw->this04);
+}
+
+#endif
+/* EQ band levels settings */
+static void vortex_EqHw_SetLevels(vortex_t *vortex, const u16 peaks[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int i;
+
+ /* set left peaks */
+ for (i = 0; i < eqhw->this04; i++) {
+ hwwrite(vortex->mmio, 0x2b024 + i * VORTEX_BAND_COEFF_SIZE, peaks[i]);
+ }
+
+ hwwrite(vortex->mmio, 0x2b3cc, peaks[eqhw->this04]);
+ hwwrite(vortex->mmio, 0x2b3d8, peaks[eqhw->this04 + 1]);
+
+ /* set right peaks */
+ for (i = 0; i < eqhw->this04; i++) {
+ hwwrite(vortex->mmio, 0x2b204 + i * VORTEX_BAND_COEFF_SIZE,
+ peaks[i + (eqhw->this04 + 2)]);
+ }
+
+ hwwrite(vortex->mmio, 0x2b3e4, peaks[2 + (eqhw->this04 * 2)]);
+ hwwrite(vortex->mmio, 0x2b3f0, peaks[3 + (eqhw->this04 * 2)]);
+}
+
+#if 0
+static void vortex_EqHw_GetLevels(vortex_t * vortex, u16 a[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int ebx;
+
+ if (eqhw->this04 < 0)
+ return;
+
+ ebx = 0;
+ do {
+ a[ebx] = hwread(vortex->mmio, 0x2b024 + ebx * 0x30);
+ ebx++;
+ }
+ while (ebx < eqhw->this04);
+
+ a[eqhw->this04] = hwread(vortex->mmio, 0x2b3cc);
+ a[eqhw->this04 + 1] = hwread(vortex->mmio, 0x2b3d8);
+
+ ebx = 0;
+ do {
+ a[ebx + (eqhw->this04 + 2)] =
+ hwread(vortex->mmio, 0x2b204 + ebx * 0x30);
+ ebx++;
+ }
+ while (ebx < eqhw->this04);
+
+ a[2 + (eqhw->this04 * 2)] = hwread(vortex->mmio, 0x2b3e4);
+ a[3 + (eqhw->this04 * 2)] = hwread(vortex->mmio, 0x2b3f0);
+}
+
+#endif
+/* Global Control */
+static void vortex_EqHw_SetControlReg(vortex_t * vortex, u32 reg)
+{
+ hwwrite(vortex->mmio, 0x2b440, reg);
+}
+
+static void vortex_EqHw_SetSampleRate(vortex_t * vortex, u32 sr)
+{
+ hwwrite(vortex->mmio, 0x2b440, ((sr & 0x1f) << 3) | 0xb800);
+}
+
+#if 0
+static void vortex_EqHw_GetControlReg(vortex_t * vortex, u32 *reg)
+{
+ *reg = hwread(vortex->mmio, 0x2b440);
+}
+
+static void vortex_EqHw_GetSampleRate(vortex_t * vortex, u32 *sr)
+{
+ *sr = (hwread(vortex->mmio, 0x2b440) >> 3) & 0x1f;
+}
+
+#endif
+static void vortex_EqHw_Enable(vortex_t * vortex)
+{
+ hwwrite(vortex->mmio, VORTEX_EQ_CTRL, 0xf001);
+}
+
+static void vortex_EqHw_Disable(vortex_t * vortex)
+{
+ hwwrite(vortex->mmio, VORTEX_EQ_CTRL, 0xf000);
+}
+
+/* Reset (zero) buffers */
+static void vortex_EqHw_ZeroIO(vortex_t * vortex)
+{
+ int i;
+ for (i = 0; i < 0x8; i++)
+ hwwrite(vortex->mmio, VORTEX_EQ_DEST + (i << 2), 0x0);
+ for (i = 0; i < 0x4; i++)
+ hwwrite(vortex->mmio, VORTEX_EQ_SOURCE + (i << 2), 0x0);
+}
+
+static void vortex_EqHw_ZeroA3DIO(vortex_t * vortex)
+{
+ int i;
+ for (i = 0; i < 0x4; i++)
+ hwwrite(vortex->mmio, VORTEX_EQ_DEST + (i << 2), 0x0);
+}
+
+static void vortex_EqHw_ZeroState(vortex_t * vortex)
+{
+
+ vortex_EqHw_SetControlReg(vortex, 0);
+ vortex_EqHw_ZeroIO(vortex);
+ hwwrite(vortex->mmio, 0x2b3c0, 0);
+
+ vortex_EqHw_SetTimeConsts(vortex, 0, 0);
+
+ vortex_EqHw_SetLeftCoefs(vortex, asEqCoefsZeros);
+ vortex_EqHw_SetRightCoefs(vortex, asEqCoefsZeros);
+
+ vortex_EqHw_SetLeftGainsCurrent(vortex, eq_gains_zero);
+ vortex_EqHw_SetRightGainsCurrent(vortex, eq_gains_zero);
+ vortex_EqHw_SetLeftGainsTarget(vortex, eq_gains_zero);
+ vortex_EqHw_SetRightGainsTarget(vortex, eq_gains_zero);
+
+ vortex_EqHw_SetBypassGain(vortex, 0, 0);
+ //vortex_EqHw_SetCurrBypassGain(vortex, 0, 0);
+ vortex_EqHw_SetA3DBypassGain(vortex, 0, 0);
+ //vortex_EqHw_SetCurrA3DBypassGain(vortex, 0, 0);
+ vortex_EqHw_SetLeftStates(vortex, eq_states_zero, asEqOutStateZeros);
+ vortex_EqHw_SetRightStates(vortex, eq_states_zero, asEqOutStateZeros);
+ vortex_EqHw_SetLevels(vortex, (u16 *) eq_levels);
+}
+
+/* Program coeficients as pass through */
+static void vortex_EqHw_ProgramPipe(vortex_t * vortex)
+{
+ vortex_EqHw_SetTimeConsts(vortex, 0, 0);
+
+ vortex_EqHw_SetLeftCoefs(vortex, asEqCoefsPipes);
+ vortex_EqHw_SetRightCoefs(vortex, asEqCoefsPipes);
+
+ vortex_EqHw_SetLeftGainsCurrent(vortex, eq_gains_current);
+ vortex_EqHw_SetRightGainsCurrent(vortex, eq_gains_current);
+ vortex_EqHw_SetLeftGainsTarget(vortex, eq_gains_current);
+ vortex_EqHw_SetRightGainsTarget(vortex, eq_gains_current);
+}
+
+/* Program EQ block as 10 band Equalizer */
+static void
+vortex_EqHw_Program10Band(vortex_t * vortex, auxxEqCoeffSet_t * coefset)
+{
+
+ vortex_EqHw_SetTimeConsts(vortex, 0xc, 0x7fe0);
+
+ vortex_EqHw_SetLeftCoefs(vortex, coefset->LeftCoefs);
+ vortex_EqHw_SetRightCoefs(vortex, coefset->RightCoefs);
+
+ vortex_EqHw_SetLeftGainsCurrent(vortex, coefset->LeftGains);
+
+ vortex_EqHw_SetRightGainsTarget(vortex, coefset->RightGains);
+ vortex_EqHw_SetLeftGainsTarget(vortex, coefset->LeftGains);
+
+ vortex_EqHw_SetRightGainsCurrent(vortex, coefset->RightGains);
+}
+
+/* Read all EQ peaks. (think VU meter) */
+static void vortex_EqHw_GetTenBandLevels(vortex_t * vortex, u16 peaks[])
+{
+ eqhw_t *eqhw = &(vortex->eq.this04);
+ int i;
+
+ if (eqhw->this04 <= 0)
+ return;
+
+ for (i = 0; i < eqhw->this04; i++)
+ peaks[i] = hwread(vortex->mmio, 0x2B024 + i * 0x30);
+ for (i = 0; i < eqhw->this04; i++)
+ peaks[i + eqhw->this04] =
+ hwread(vortex->mmio, 0x2B204 + i * 0x30);
+}
+
+/* CEqlzr.s */
+
+static int vortex_Eqlzr_GetLeftGain(vortex_t * vortex, u16 index, u16 * gain)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ if (eq->this28) {
+ *gain = eq->this130[index];
+ return 0;
+ }
+ return 1;
+}
+
+static void vortex_Eqlzr_SetLeftGain(vortex_t * vortex, u16 index, u16 gain)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ if (eq->this28 == 0)
+ return;
+
+ eq->this130[index] = gain;
+ if (eq->this54)
+ return;
+
+ vortex_EqHw_SetLeftGainsSingleTarget(vortex, index, gain);
+}
+
+static int vortex_Eqlzr_GetRightGain(vortex_t * vortex, u16 index, u16 * gain)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ if (eq->this28) {
+ *gain = eq->this130[index + eq->this10];
+ return 0;
+ }
+ return 1;
+}
+
+static void vortex_Eqlzr_SetRightGain(vortex_t * vortex, u16 index, u16 gain)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ if (eq->this28 == 0)
+ return;
+
+ eq->this130[index + eq->this10] = gain;
+ if (eq->this54)
+ return;
+
+ vortex_EqHw_SetRightGainsSingleTarget(vortex, index, gain);
+}
+
+#if 0
+static int
+vortex_Eqlzr_GetAllBands(vortex_t * vortex, u16 * gains, s32 *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, const u16 gains[], s32 count)
+{
+ eqlzr_t *eq = &(vortex->eq);
+ int i;
+
+ if (((eq->this10) * 2 != count) || (eq->this28 == 0))
+ return 1;
+
+ for (i = 0; i < count; i++) {
+ eq->this130[i] = gains[i];
+ }
+
+ if (eq->this54)
+ return 0;
+ return vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex);
+}
+
+static void
+vortex_Eqlzr_SetA3dBypassGain(vortex_t * vortex, u32 a, u32 b)
+{
+ eqlzr_t *eq = &(vortex->eq);
+ u32 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);
+ u32 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, u32 bp)
+{
+ eqlzr_t *eq = &(vortex->eq);
+
+ if ((eq->this28) && (bp == 0)) {
+ /* EQ enabled */
+ vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex);
+ vortex_EqHw_SetBypassGain(vortex, eq->this08, eq->this08);
+ } else {
+ /* EQ disabled. */
+ vortex_EqHw_SetLeftGainsTarget(vortex, eq->this14_array);
+ vortex_EqHw_SetRightGainsTarget(vortex, eq->this14_array);
+ 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_array, 0, sizeof(eq->this14_array));
+
+ /* 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 */
+#define snd_vortex_eqtoggle_info snd_ctl_boolean_mono_info
+
+static int
+snd_vortex_eqtoggle_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *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(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *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 const struct snd_kcontrol_new vortex_eqtoggle_kcontrol = {
+ .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(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *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(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ int i = kcontrol->private_value;
+ u16 gainL = 0, gainR = 0;
+
+ 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(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ int changed = 0, i = kcontrol->private_value;
+ u16 gainL = 0, gainR = 0;
+
+ 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 const struct snd_kcontrol_new vortex_eq_kcontrol = {
+ .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(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *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(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ int i, count = 0;
+ u16 peaks[20];
+
+ vortex_Eqlzr_GetAllPeaks(vortex, peaks, &count);
+ if (count != 20) {
+ dev_err(vortex->card->dev,
+ "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 const struct snd_kcontrol_new vortex_levels_kcontrol = {
+ .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 const char * const EqBandLabels[10] = {
+ "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)
+{
+ struct snd_kcontrol *kcontrol;
+ int err, i;
+
+ vortex_Eqlzr_init(vortex);
+
+ kcontrol = snd_ctl_new1(&vortex_eqtoggle_kcontrol, vortex);
+ if (!kcontrol)
+ return -ENOMEM;
+ kcontrol->private_value = 0;
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
+ return err;
+
+ /* EQ gain controls */
+ for (i = 0; i < 10; i++) {
+ kcontrol = snd_ctl_new1(&vortex_eq_kcontrol, vortex);
+ if (!kcontrol)
+ return -ENOMEM;
+ snprintf(kcontrol->id.name, sizeof(kcontrol->id.name),
+ "%s Playback Volume", EqBandLabels[i]);
+ kcontrol->private_value = i;
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
+ return err;
+ //vortex->eqctrl[i] = kcontrol;
+ }
+ /* EQ band levels */
+ kcontrol = snd_ctl_new1(&vortex_levels_kcontrol, vortex);
+ if (!kcontrol)
+ return -ENOMEM;
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int vortex_eq_free(vortex_t * vortex)
+{
+ /*
+ //FIXME: segfault because vortex->eqctrl[i] == 4
+ int i;
+ for (i=0; i<10; i++) {
+ if (vortex->eqctrl[i])
+ snd_ctl_remove(vortex->card, vortex->eqctrl[i]);
+ }
+ */
+ vortex_Eqlzr_shutdown(vortex);
+ return 0;
+}
+
+/* End */
diff --git a/sound/pci/au88x0/au88x0_eq.h b/sound/pci/au88x0/au88x0_eq.h
new file mode 100644
index 000000000..797cdae1d
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_eq.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#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[10]; //0xd0
+ u16 RightGains[10]; //0xe4
+} auxxEqCoeffSet_t;
+
+typedef struct {
+ s32 this04; /* How many filters for each side (default = 10) */
+ s32 this08; /* inited to cero. Stereo flag? */
+} eqhw_t;
+
+typedef struct {
+ eqhw_t this04; /* CHwEq */
+ u16 this08; /* Bad codec flag ? SetBypassGain: bypass gain */
+ u16 this0a;
+ u16 this0c; /* SetBypassGain: bypass gain when this28 is not set. */
+ u16 this0e;
+
+ s32 this10; /* How many gains are used for each side (right or left). */
+ u16 this14_array[10]; /* SetLeftGainsTarget: Left (and right?) EQ gains */
+ s32 this28; /* flag related to EQ enabled or not. Gang flag ? */
+ s32 this54; /* SetBypass */
+ s32 this58;
+ s32 this5c;
+ /*0x60 */ auxxEqCoeffSet_t coefset;
+ /* 50 u16 word each channel. */
+ u16 this130[20]; /* Left and Right gains */
+} eqlzr_t;
+
+#endif
diff --git a/sound/pci/au88x0/au88x0_eqdata.c b/sound/pci/au88x0/au88x0_eqdata.c
new file mode 100644
index 000000000..a74f266f0
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_eqdata.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Data structs */
+
+static const 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 const 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 const 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 const 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 const u16 eq_gains_zero[10] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+/* _rodatab7c: ProgramPipe */
+static const u16 eq_gains_current[12] = {
+ 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+ 0x7fff,
+ 0x7fff, 0x7fff, 0x7fff
+};
+
+/* _rodatab78 */
+static const u16 eq_states_zero[2] = { 0x0000, 0x0000 };
+
+static const 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 const u16 eq_levels[64] = {
+ 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, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c
new file mode 100644
index 000000000..51c154e34
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_game.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Manuel Jander.
+ *
+ * Based on the work of:
+ * Vojtech Pavlik
+ * Raymond Ingles
+ *
+ * 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 <linux/time.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "au88x0.h"
+#include <linux/gameport.h>
+#include <linux/export.h>
+
+#if IS_REACHABLE(CONFIG_GAMEPORT)
+
+#define VORTEX_GAME_DWAIT 20 /* 20 ms */
+
+static unsigned char vortex_game_read(struct gameport *gameport)
+{
+ vortex_t *vortex = gameport_get_port_data(gameport);
+ return hwread(vortex->mmio, VORTEX_GAME_LEGACY);
+}
+
+static void vortex_game_trigger(struct gameport *gameport)
+{
+ vortex_t *vortex = gameport_get_port_data(gameport);
+ hwwrite(vortex->mmio, VORTEX_GAME_LEGACY, 0xff);
+}
+
+static int
+vortex_game_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+ vortex_t *vortex = gameport_get_port_data(gameport);
+ int i;
+
+ *buttons = (~hwread(vortex->mmio, VORTEX_GAME_LEGACY) >> 4) & 0xf;
+
+ for (i = 0; i < 4; i++) {
+ axes[i] =
+ hwread(vortex->mmio, VORTEX_GAME_AXIS + (i * AXIS_SIZE));
+ if (axes[i] == AXIS_RANGE)
+ axes[i] = -1;
+ }
+ return 0;
+}
+
+static int vortex_game_open(struct gameport *gameport, int mode)
+{
+ vortex_t *vortex = gameport_get_port_data(gameport);
+
+ switch (mode) {
+ case GAMEPORT_MODE_COOKED:
+ hwwrite(vortex->mmio, VORTEX_CTRL2,
+ hwread(vortex->mmio,
+ VORTEX_CTRL2) | CTRL2_GAME_ADCMODE);
+ msleep(VORTEX_GAME_DWAIT);
+ return 0;
+ case GAMEPORT_MODE_RAW:
+ hwwrite(vortex->mmio, VORTEX_CTRL2,
+ hwread(vortex->mmio,
+ VORTEX_CTRL2) & ~CTRL2_GAME_ADCMODE);
+ return 0;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int vortex_gameport_register(vortex_t *vortex)
+{
+ struct gameport *gp;
+
+ vortex->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ dev_err(vortex->card->dev,
+ "cannot allocate memory for gameport\n");
+ return -ENOMEM;
+ }
+
+ gameport_set_name(gp, "AU88x0 Gameport");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(vortex->pci_dev));
+ gameport_set_dev_parent(gp, &vortex->pci_dev->dev);
+
+ gp->read = vortex_game_read;
+ gp->trigger = vortex_game_trigger;
+ gp->cooked_read = vortex_game_cooked_read;
+ gp->open = vortex_game_open;
+
+ gameport_set_port_data(gp, vortex);
+ gp->fuzz = 64;
+
+ gameport_register_port(gp);
+
+ return 0;
+}
+
+static void vortex_gameport_unregister(vortex_t * vortex)
+{
+ if (vortex->gameport) {
+ gameport_unregister_port(vortex->gameport);
+ vortex->gameport = NULL;
+ }
+}
+
+#else
+static inline int vortex_gameport_register(vortex_t * vortex) { return -ENOSYS; }
+static inline void vortex_gameport_unregister(vortex_t * vortex) { }
+#endif
diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c
new file mode 100644
index 000000000..aeba684b8
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_mixer.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Vortex Mixer support.
+ *
+ * There is much more than just the AC97 mixer...
+ *
+ */
+
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "au88x0.h"
+
+static int remove_ctl(struct snd_card *card, const char *name)
+{
+ struct snd_ctl_elem_id id;
+ memset(&id, 0, sizeof(id));
+ strcpy(id.name, name);
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return snd_ctl_remove_id(card, &id);
+}
+
+static int snd_vortex_mixer(vortex_t *vortex)
+{
+ struct snd_ac97_bus *pbus;
+ struct snd_ac97_template ac97;
+ int err;
+ static const struct snd_ac97_bus_ops ops = {
+ .write = vortex_codec_write,
+ .read = vortex_codec_read,
+ };
+
+ err = snd_ac97_bus(vortex->card, 0, &ops, NULL, &pbus);
+ if (err < 0)
+ return err;
+ memset(&ac97, 0, sizeof(ac97));
+ // Initialize AC97 codec stuff.
+ ac97.private_data = vortex;
+ ac97.scaps = AC97_SCAP_NO_SPDIF;
+ err = snd_ac97_mixer(pbus, &ac97, &vortex->codec);
+ vortex->isquad = ((vortex->codec == NULL) ? 0 : (vortex->codec->ext_id&0x80));
+ remove_ctl(vortex->card, "Master Mono Playback Volume");
+ remove_ctl(vortex->card, "Master Mono Playback Switch");
+ return err;
+}
diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c
new file mode 100644
index 000000000..164f6b703
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_mpu401.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Routines for control of MPU-401 in UART mode
+ *
+ * Modified for the Aureal Vortex based Soundcards
+ * by Manuel Jander (mjande@embedded.cl).
+ */
+
+#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 snd_vortex_midi(vortex_t *vortex)
+{
+ struct snd_rawmidi *rmidi;
+ int temp, mode;
+ struct snd_mpu401 *mpu;
+ unsigned long 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);
+
+ /* Check if anything is OK. */
+ temp = hwread(vortex->mmio, VORTEX_MIDI_DATA);
+ if (temp != MPU401_ACK /*0xfe */ ) {
+ dev_err(vortex->card->dev, "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
+ temp = snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_MPU401, 0x330,
+ MPU401_INFO_IRQ_HOOK, -1, &rmidi);
+ if (temp) {
+ 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);
+ temp = snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_AUREAL, port,
+ MPU401_INFO_INTEGRATED | MPU401_INFO_MMIO |
+ MPU401_INFO_IRQ_HOOK, -1, &rmidi);
+ if (temp) {
+ hwwrite(vortex->mmio, VORTEX_CTRL,
+ (hwread(vortex->mmio, VORTEX_CTRL) &
+ ~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
+ return temp;
+ }
+ mpu = rmidi->private_data;
+ mpu->cport = (unsigned long)(vortex->mmio + VORTEX_MIDI_CMD);
+#endif
+ /* Overwrite MIDI name */
+ snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI %d", CARD_NAME_SHORT , vortex->card->number);
+
+ vortex->rmidi = rmidi;
+ return 0;
+}
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
new file mode 100644
index 000000000..546f71220
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -0,0 +1,682 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+/*
+ * Vortex PCM ALSA driver.
+ *
+ * Supports ADB and WT DMA. Unfortunately, WT channels do not run yet.
+ * It remains stuck,and DMA transfers do not happen.
+ */
+#include <sound/asoundef.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "au88x0.h"
+
+#define VORTEX_PCM_TYPE(x) (x->name[40])
+
+/* hardware definition */
+static const struct snd_pcm_hardware 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,
+ .channels_max = 2,
+ .buffer_bytes_max = 0x10000,
+ .period_bytes_min = 0x20,
+ .period_bytes_max = 0x1000,
+ .periods_min = 2,
+ .periods_max = 1024,
+};
+
+#ifndef CHIP_AU8820
+static const struct snd_pcm_hardware 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 const struct snd_pcm_hardware 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 const struct snd_pcm_hardware 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
+#ifdef CHIP_AU8830
+static const unsigned int au8830_channels[3] = {
+ 1, 2, 4,
+};
+
+static const struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = {
+ .count = ARRAY_SIZE(au8830_channels),
+ .list = au8830_channels,
+ .mask = 0,
+};
+#endif
+
+static void vortex_notify_pcm_vol_change(struct snd_card *card,
+ struct snd_kcontrol *kctl, int activate)
+{
+ if (activate)
+ kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ else
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &(kctl->id));
+}
+
+/* open callback */
+static int snd_vortex_pcm_open(struct snd_pcm_substream *substream)
+{
+ vortex_t *vortex = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ /* Force equal size periods */
+ err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ return err;
+ /* Avoid PAGE_SIZE boundary to fall inside of a period. */
+ err = snd_pcm_hw_constraint_pow2(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES);
+ if (err < 0)
+ return err;
+
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 64);
+
+ 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;
+#ifdef CHIP_AU8830
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ VORTEX_IS_QUAD(vortex) &&
+ VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
+ runtime->hw.channels_max = 4;
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &hw_constraints_au8830_channels);
+ }
+#endif
+ 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(struct snd_pcm_substream *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(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ vortex_t *chip = snd_pcm_substream_chip(substream);
+ stream_t *stream = (stream_t *) (substream->runtime->private_data);
+
+ /*
+ pr_info( "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params),
+ params_period_bytes(hw_params), params_channels(hw_params));
+ */
+ spin_lock_irq(&chip->lock);
+ // Make audio routes and config buffer DMA.
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+ int dma, type = VORTEX_PCM_TYPE(substream->pcm);
+ /* Dealloc any routes. */
+ if (stream != NULL)
+ vortex_adb_allocroute(chip, stream->dma,
+ stream->nr_ch, stream->dir,
+ stream->type,
+ substream->number);
+ /* Alloc routes. */
+ dma =
+ vortex_adb_allocroute(chip, -1,
+ params_channels(hw_params),
+ substream->stream, type,
+ substream->number);
+ if (dma < 0) {
+ spin_unlock_irq(&chip->lock);
+ return dma;
+ }
+ stream = substream->runtime->private_data = &chip->dma_adb[dma];
+ stream->substream = substream;
+ /* Setup Buffers. */
+ vortex_adbdma_setbuffers(chip, dma,
+ params_period_bytes(hw_params),
+ params_periods(hw_params));
+ if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
+ chip->pcm_vol[substream->number].active = 1;
+ vortex_notify_pcm_vol_change(chip->card,
+ chip->pcm_vol[substream->number].kctl, 1);
+ }
+ }
+#ifndef CHIP_AU8810
+ else {
+ /* if (stream != NULL)
+ vortex_wt_allocroute(chip, substream->number, 0); */
+ vortex_wt_allocroute(chip, substream->number,
+ params_channels(hw_params));
+ stream = substream->runtime->private_data =
+ &chip->dma_wt[substream->number];
+ stream->dma = substream->number;
+ stream->substream = substream;
+ vortex_wtdma_setbuffers(chip, substream->number,
+ params_period_bytes(hw_params),
+ params_periods(hw_params));
+ }
+#endif
+ spin_unlock_irq(&chip->lock);
+ return 0;
+}
+
+/* hw_free callback */
+static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ vortex_t *chip = snd_pcm_substream_chip(substream);
+ stream_t *stream = (stream_t *) (substream->runtime->private_data);
+
+ spin_lock_irq(&chip->lock);
+ // Delete audio routes.
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+ if (stream != NULL) {
+ if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
+ chip->pcm_vol[substream->number].active = 0;
+ vortex_notify_pcm_vol_change(chip->card,
+ chip->pcm_vol[substream->number].kctl,
+ 0);
+ }
+ vortex_adb_allocroute(chip, stream->dma,
+ stream->nr_ch, stream->dir,
+ stream->type,
+ substream->number);
+ }
+ }
+#ifndef CHIP_AU8810
+ else {
+ if (stream != NULL)
+ vortex_wt_allocroute(chip, stream->dma, 0);
+ }
+#endif
+ substream->runtime->private_data = NULL;
+ spin_unlock_irq(&chip->lock);
+
+ return 0;
+}
+
+/* prepare callback */
+static int snd_vortex_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ vortex_t *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *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, chip);
+ spin_lock_irq(&chip->lock);
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+ vortex_adbdma_setmode(chip, dma, 1, dir, fmt,
+ runtime->channels == 1 ? 0 : 1, 0);
+ vortex_adbdma_setstartbuffer(chip, dma, 0);
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_SPDIF)
+ vortex_adb_setsrc(chip, dma, runtime->rate, dir);
+ }
+#ifndef CHIP_AU8810
+ else {
+ vortex_wtdma_setmode(chip, dma, 1, fmt, 0, 0);
+ // FIXME: Set rate (i guess using vortex_wt_writereg() somehow).
+ vortex_wtdma_setstartbuffer(chip, dma, 0);
+ }
+#endif
+ spin_unlock_irq(&chip->lock);
+ return 0;
+}
+
+/* trigger callback */
+static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ vortex_t *chip = snd_pcm_substream_chip(substream);
+ stream_t *stream = (stream_t *) substream->runtime->private_data;
+ int dma = stream->dma;
+
+ spin_lock(&chip->lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ // do something to start the PCM engine
+ //printk(KERN_INFO "vortex: start %d\n", dma);
+ stream->fifo_enabled = 1;
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+ vortex_adbdma_resetup(chip, dma);
+ vortex_adbdma_startfifo(chip, dma);
+ }
+#ifndef CHIP_AU8810
+ else {
+ dev_info(chip->card->dev, "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_stopfifo(chip, dma);
+#ifndef CHIP_AU8810
+ else {
+ dev_info(chip->card->dev, "wt stop %d\n", dma);
+ vortex_wtdma_stopfifo(chip, dma);
+ }
+#endif
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ //printk(KERN_INFO "vortex: pause %d\n", dma);
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
+ vortex_adbdma_pausefifo(chip, dma);
+#ifndef CHIP_AU8810
+ else
+ vortex_wtdma_pausefifo(chip, dma);
+#endif
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ //printk(KERN_INFO "vortex: resume %d\n", dma);
+ if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
+ vortex_adbdma_resumefifo(chip, dma);
+#ifndef CHIP_AU8810
+ else
+ vortex_wtdma_resumefifo(chip, dma);
+#endif
+ break;
+ default:
+ spin_unlock(&chip->lock);
+ return -EINVAL;
+ }
+ spin_unlock(&chip->lock);
+ return 0;
+}
+
+/* pointer callback */
+static snd_pcm_uframes_t snd_vortex_pcm_pointer(struct snd_pcm_substream *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);
+ current_ptr = bytes_to_frames(substream->runtime, current_ptr);
+ if (current_ptr >= substream->runtime->buffer_size)
+ current_ptr = 0;
+ return current_ptr;
+}
+
+/* operators */
+static const struct snd_pcm_ops snd_vortex_playback_ops = {
+ .open = snd_vortex_pcm_open,
+ .close = snd_vortex_pcm_close,
+ .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,
+};
+
+/*
+* definitions of capture are omitted here...
+*/
+
+static const char * const vortex_pcm_prettyname[VORTEX_PCM_LAST] = {
+ CARD_NAME " ADB",
+ CARD_NAME " SPDIF",
+ CARD_NAME " A3D",
+ CARD_NAME " WT",
+ CARD_NAME " I2S",
+};
+static const char * const vortex_pcm_name[VORTEX_PCM_LAST] = {
+ "adb",
+ "spdif",
+ "a3d",
+ "wt",
+ "i2s",
+};
+
+/* SPDIF kcontrol */
+
+static int snd_vortex_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_vortex_spdif_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.iec958.status[0] = 0xff;
+ ucontrol->value.iec958.status[1] = 0xff;
+ ucontrol->value.iec958.status[2] = 0xff;
+ ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
+ return 0;
+}
+
+static int snd_vortex_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.iec958.status[0] = 0x00;
+ ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL|IEC958_AES1_CON_DIGDIGCONV_ID;
+ ucontrol->value.iec958.status[2] = 0x00;
+ switch (vortex->spdif_sr) {
+ case 32000: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_32000; break;
+ case 44100: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_44100; break;
+ case 48000: ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000; break;
+ }
+ return 0;
+}
+
+static int snd_vortex_spdif_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ int spdif_sr = 48000;
+ switch (ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) {
+ case IEC958_AES3_CON_FS_32000: spdif_sr = 32000; break;
+ case IEC958_AES3_CON_FS_44100: spdif_sr = 44100; break;
+ case IEC958_AES3_CON_FS_48000: spdif_sr = 48000; break;
+ }
+ if (spdif_sr == vortex->spdif_sr)
+ return 0;
+ vortex->spdif_sr = spdif_sr;
+ vortex_spdif_init(vortex, vortex->spdif_sr, 1);
+ return 1;
+}
+
+/* spdif controls */
+static const struct snd_kcontrol_new snd_vortex_mixer_spdif[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = snd_vortex_spdif_info,
+ .get = snd_vortex_spdif_get,
+ .put = snd_vortex_spdif_put,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .info = snd_vortex_spdif_info,
+ .get = snd_vortex_spdif_mask_get
+ },
+};
+
+/* subdevice PCM Volume control */
+
+static int snd_vortex_pcm_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+ uinfo->value.integer.min = -128;
+ uinfo->value.integer.max = 32;
+ return 0;
+}
+
+static int snd_vortex_pcm_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ int subdev = kcontrol->id.subdevice;
+ struct pcm_vol *p = &vortex->pcm_vol[subdev];
+ int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+ for (i = 0; i < max_chn; i++)
+ ucontrol->value.integer.value[i] = p->vol[i];
+ return 0;
+}
+
+static int snd_vortex_pcm_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ int changed = 0;
+ int mixin;
+ unsigned char vol;
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ int subdev = kcontrol->id.subdevice;
+ struct pcm_vol *p = &vortex->pcm_vol[subdev];
+ int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+ for (i = 0; i < max_chn; i++) {
+ if (p->vol[i] != ucontrol->value.integer.value[i]) {
+ p->vol[i] = ucontrol->value.integer.value[i];
+ if (p->active) {
+ switch (vortex->dma_adb[p->dma].nr_ch) {
+ case 1:
+ mixin = p->mixin[0];
+ break;
+ case 2:
+ default:
+ mixin = p->mixin[(i < 2) ? i : (i - 2)];
+ break;
+ case 4:
+ mixin = p->mixin[i];
+ break;
+ }
+ vol = p->vol[i];
+ vortex_mix_setinputvolumebyte(vortex,
+ vortex->mixplayb[i], mixin, vol);
+ }
+ changed = 1;
+ }
+ }
+ return changed;
+}
+
+static const DECLARE_TLV_DB_MINMAX(vortex_pcm_vol_db_scale, -9600, 2400);
+
+static const struct snd_kcontrol_new snd_vortex_pcm_vol = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .info = snd_vortex_pcm_vol_info,
+ .get = snd_vortex_pcm_vol_get,
+ .put = snd_vortex_pcm_vol_put,
+ .tlv = { .p = vortex_pcm_vol_db_scale },
+};
+
+/* create a pcm device */
+static int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
+{
+ struct snd_pcm *pcm;
+ struct snd_kcontrol *kctl;
+ int i;
+ int err, nr_capt;
+
+ if (!chip || 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 which can't capture. */
+ if (idx == VORTEX_PCM_ADB)
+ nr_capt = nr;
+ else
+ nr_capt = 0;
+ err = snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr,
+ nr_capt, &pcm);
+ if (err < 0)
+ return err;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s %s", CARD_NAME_SHORT, vortex_pcm_name[idx]);
+ chip->pcm[idx] = pcm;
+ // This is an evil hack, but it saves a lot of duplicated code.
+ VORTEX_PCM_TYPE(pcm) = idx;
+ pcm->private_data = chip;
+ /* set operators */
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_vortex_playback_ops);
+ if (idx == VORTEX_PCM_ADB)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_vortex_playback_ops);
+
+ /* pre-allocation of Scatter-Gather buffers */
+
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci_dev->dev, 0x10000, 0x10000);
+
+ switch (VORTEX_PCM_TYPE(pcm)) {
+ case VORTEX_PCM_ADB:
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps,
+ VORTEX_IS_QUAD(chip) ? 4 : 2,
+ 0, NULL);
+ if (err < 0)
+ return err;
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ snd_pcm_std_chmaps, 2, 0, NULL);
+ if (err < 0)
+ return err;
+ break;
+#ifdef CHIP_AU8830
+ case VORTEX_PCM_A3D:
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps, 1, 0, NULL);
+ if (err < 0)
+ return err;
+ break;
+#endif
+ }
+
+ if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) {
+ for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) {
+ kctl = snd_ctl_new1(&snd_vortex_mixer_spdif[i], chip);
+ if (!kctl)
+ return -ENOMEM;
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
+ return err;
+ }
+ }
+ if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_ADB) {
+ for (i = 0; i < NR_PCM; i++) {
+ chip->pcm_vol[i].active = 0;
+ chip->pcm_vol[i].dma = -1;
+ kctl = snd_ctl_new1(&snd_vortex_pcm_vol, chip);
+ if (!kctl)
+ return -ENOMEM;
+ chip->pcm_vol[i].kctl = kctl;
+ kctl->id.device = 0;
+ kctl->id.subdevice = i;
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
diff --git a/sound/pci/au88x0/au88x0_synth.c b/sound/pci/au88x0/au88x0_synth.c
new file mode 100644
index 000000000..84d961e7c
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_synth.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+/*
+ * 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,
+ u32 val);
+
+/* WT */
+
+/* Put 2 WT channels together for one stereo interlaced channel. */
+static void vortex_wt_setstereo(vortex_t * vortex, u32 wt, u32 stereo)
+{
+ int temp;
+
+ //temp = hwread(vortex->mmio, 0x80 + ((wt >> 0x5)<< 0xf) + (((wt & 0x1f) >> 1) << 2));
+ temp = hwread(vortex->mmio, WT_STEREO(wt));
+ temp = (temp & 0xfe) | (stereo & 1);
+ //hwwrite(vortex->mmio, 0x80 + ((wt >> 0x5)<< 0xf) + (((wt & 0x1f) >> 1) << 2), temp);
+ hwwrite(vortex->mmio, WT_STEREO(wt), temp);
+}
+
+/* Join to mixdown route. */
+static void vortex_wt_setdsout(vortex_t * vortex, u32 wt, int en)
+{
+ int temp;
+
+ /* There is one DSREG register for each bank (32 voices each). */
+ temp = hwread(vortex->mmio, WT_DSREG((wt >= 0x20) ? 1 : 0));
+ if (en)
+ temp |= (1 << (wt & 0x1f));
+ else
+ temp &= ~(1 << (wt & 0x1f));
+ hwwrite(vortex->mmio, WT_DSREG((wt >= 0x20) ? 1 : 0), temp);
+}
+
+/* Setup WT route. */
+static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
+{
+ wt_voice_t *voice = &(vortex->wt_voice[wt]);
+ int temp;
+
+ //FIXME: WT audio routing.
+ if (nr_ch) {
+ vortex_fifo_wtinitialize(vortex, wt, 1);
+ vortex_fifo_setwtvalid(vortex, wt, 1);
+ vortex_wt_setstereo(vortex, wt, nr_ch - 1);
+ } else
+ vortex_fifo_setwtvalid(vortex, wt, 0);
+
+ /* Set mixdown mode. */
+ vortex_wt_setdsout(vortex, wt, 1);
+ /* Set other parameter registers. */
+ hwwrite(vortex->mmio, WT_SRAMP(0), 0x880000);
+ //hwwrite(vortex->mmio, WT_GMODE(0), 0xffffffff);
+#ifdef CHIP_AU8830
+ hwwrite(vortex->mmio, WT_SRAMP(1), 0x880000);
+ //hwwrite(vortex->mmio, WT_GMODE(1), 0xffffffff);
+#endif
+ hwwrite(vortex->mmio, WT_PARM(wt, 0), 0);
+ hwwrite(vortex->mmio, WT_PARM(wt, 1), 0);
+ hwwrite(vortex->mmio, WT_PARM(wt, 2), 0);
+
+ temp = hwread(vortex->mmio, WT_PARM(wt, 3));
+ dev_dbg(vortex->card->dev, "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);
+
+ dev_dbg(vortex->card->dev, "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);
+ dev_dbg(vortex->card->dev, "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,
+ u16 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,
+ u32 val)
+{
+ int ecx;
+
+ if ((reg == 5) || ((reg >= 7) && (reg <= 10)) || (reg == 0xc)) {
+ if (wt >= (NR_WT / NR_WT_PB)) {
+ dev_warn(vortex->card->dev,
+ "WT SetReg: bank out of range. reg=0x%x, wt=%d\n",
+ reg, wt);
+ return 0;
+ }
+ } else {
+ if (wt >= NR_WT) {
+ dev_err(vortex->card->dev,
+ "WT SetReg: voice out of range\n");
+ return 0;
+ }
+ }
+ if (reg > 0xc)
+ return 0;
+
+ switch (reg) {
+ /* Voice specific parameters */
+ case 0: /* running */
+ /*
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_RUN(wt), (int)val);
+ */
+ hwwrite(vortex->mmio, WT_RUN(wt), val);
+ return 0xc;
+ case 1: /* param 0 */
+ /*
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_PARM(wt,0), (int)val);
+ */
+ hwwrite(vortex->mmio, WT_PARM(wt, 0), val);
+ return 0xc;
+ case 2: /* param 1 */
+ /*
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_PARM(wt,1), (int)val);
+ */
+ hwwrite(vortex->mmio, WT_PARM(wt, 1), val);
+ return 0xc;
+ case 3: /* param 2 */
+ /*
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_PARM(wt,2), (int)val);
+ */
+ hwwrite(vortex->mmio, WT_PARM(wt, 2), val);
+ return 0xc;
+ case 4: /* param 3 */
+ /*
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_PARM(wt,3), (int)val);
+ */
+ hwwrite(vortex->mmio, WT_PARM(wt, 3), val);
+ return 0xc;
+ case 6: /* mute */
+ /*
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_MUTE(wt), (int)val);
+ */
+ hwwrite(vortex->mmio, WT_MUTE(wt), val);
+ return 0xc;
+ case 0xb:
+ /* delay */
+ /*
+ pr_debug( "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;
+ /* 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;
+ }
+ /*
+ pr_debug( "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)
+{
+ u32 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]);
+ u32 eax, edx;
+
+ //FIXME: 64 bit operation.
+ eax = ((sr << 0xf) * 0x57619F1) & 0xffffffff;
+ edx = (((sr << 0xf) * 0x57619F1)) >> 0x20;
+
+ edx >>= 0xa;
+ edx <<= 1;
+ if (edx) {
+ if (edx & 0x0FFF80000)
+ eax = 0x7fff;
+ else {
+ edx <<= 0xd;
+ eax = 7;
+ while ((edx & 0x80000000) == 0) {
+ edx <<= 1;
+ eax--;
+ if (eax == 0)
+ break;
+ }
+ if (eax)
+ edx <<= 1;
+ eax <<= 0xc;
+ edx >>= 0x14;
+ eax |= edx;
+ }
+ } else
+ eax = 0;
+ voice->parm0 &= 0xffff0001;
+ voice->parm0 |= (eax & 0x7fff) << 1;
+ voice->parm1 = voice->parm0 | 1;
+ // Wt: this_1D4
+ //AuWt::WriteReg((ulong)(this_1DC<<4)+0x200, (ulong)this_1E4);
+ //AuWt::WriteReg((ulong)(this_1DC<<4)+0x204, (ulong)this_1E8);
+ hwwrite(vortex->mmio, WT_PARM(wt, 0), voice->parm0);
+ hwwrite(vortex->mmio, WT_PARM(wt, 1), voice->parm1);
+}
+#endif
+
+/* End of File */
diff --git a/sound/pci/au88x0/au88x0_wt.h b/sound/pci/au88x0/au88x0_wt.h
new file mode 100644
index 000000000..7b2cffad8
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_wt.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/***************************************************************************
+ * 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 {
+ u32 parm0; /* this_1E4 */
+ u32 parm1; /* this_1E8 */
+ u32 parm2; /* this_1EC */
+ u32 parm3; /* this_1F0 */
+ u32 this_1D0;
+} wt_voice_t;
+
+#endif /* _AU88X0_WT_H */
+
+/* End of file */
diff --git a/sound/pci/au88x0/au88x0_xtalk.c b/sound/pci/au88x0/au88x0_xtalk.c
new file mode 100644
index 000000000..27859536d
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_xtalk.c
@@ -0,0 +1,783 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/***************************************************************************
+ * au88x0_cxtalk.c
+ *
+ * Wed Nov 19 16:29:47 2003
+ * Copyright 2003 mjander
+ * mjander@users.sourceforge.org
+ ****************************************************************************/
+
+/*
+ */
+
+#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 __maybe_unused short const sXtalkWideKRightXt = 0xF25E;
+static short const sXtalkWideShiftLeftEq = 1;
+static short const sXtalkWideShiftRightEq = 1;
+static short const sXtalkWideShiftLeftXt = 0;
+static __maybe_unused 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 __maybe_unused short const sXtalkNarrowKRightXt = 0xF82E;
+static short const sXtalkNarrowShiftLeftEq = 0x3;
+static short const sXtalkNarrowShiftRightEq = 0x3;
+static short const sXtalkNarrowShiftLeftXt = 0;
+static __maybe_unused short const sXtalkNarrowShiftRightXt = 0;
+static unsigned short const wXtalkNarrowLeftDelay = 0x7;
+static unsigned short const wXtalkNarrowRightDelay = 0x7;
+
+static __maybe_unused xtalk_gains_t const asXtalkGainsDefault = {
+ 0x4000, 0x4000, 0x4000, 0x4000, 0x4000,
+ 0x4000, 0x4000, 0x4000, 0x4000, 0x4000
+};
+
+static __maybe_unused xtalk_gains_t const asXtalkGainsTest = {
+ 0x7fff, 0x8000, 0x0000, 0x0000, 0x0001,
+ 0xffff, 0x4000, 0xc000, 0x0002, 0xfffe
+};
+
+static __maybe_unused xtalk_gains_t const asXtalkGains1Chan = {
+ 0x7FFF, 0, 0, 0, 0,
+ 0x7FFF, 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
+};
+
+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 __maybe_unused xtalk_dline_t const alXtalkDlineTest = {
+ 0x0000fc18, 0xfff03e8, 0x000186a0, 0xfffe7960, 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 __maybe_unused xtalk_instate_t const asXtalkInStateTest = {
+ 0x0080, 0xff80, 0x0001, 0xffff
+};
+
+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 __maybe_unused short const sDiamondKRightXt = 0xF90E;
+static short const sDiamondShiftLeftEq = 1;
+static short const sDiamondShiftRightEq = 1;
+static short const sDiamondShiftLeftXt = 0;
+static __maybe_unused 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, 0xe8f5, 0x236c, 0xe40d, 0},
+ {0x76d5, 0xc78d, 0x05ac, 0xfa5b, 0},
+ {0x7F04, 0xC0FA, 0x0263, 0xFDA2, 0}
+};
+static xtalk_coefs_t const asXtalkWideCoefsRightEq = {
+ {0xEC4C, 0xDCE9, 0xFDC2, 0xFEEC, 0},
+ {0x5F60, 0xCBCB, 0xFC26, 0x0305, 0},
+ {0x340B, 0xe8f5, 0x236c, 0xe40d, 0},
+ {0x76d5, 0xc78d, 0x05ac, 0xfa5b, 0},
+ {0x7F04, 0xC0FA, 0x0263, 0xFDA2, 0}
+};
+static xtalk_coefs_t const asXtalkWideCoefsLeftXt = {
+ {0x55c6, 0xc97b, 0x005b, 0x0047, 0},
+ {0x6a60, 0xca20, 0xffc6, 0x0040, 0},
+ {0x6411, 0xd711, 0xfca1, 0x0190, 0},
+ {0x77dc, 0xc79e, 0xffb8, 0x000a, 0},
+ {0, 0, 0, 0, 0}
+};
+static __maybe_unused xtalk_coefs_t const asXtalkWideCoefsRightXt = {
+ {0x55c6, 0xc97b, 0x005b, 0x0047, 0},
+ {0x6a60, 0xca20, 0xffc6, 0x0040, 0},
+ {0x6411, 0xd711, 0xfca1, 0x0190, 0},
+ {0x77dc, 0xc79e, 0xffb8, 0x000a, 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 __maybe_unused 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 __maybe_unused 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 __maybe_unused 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 __maybe_unused 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 __maybe_unused xtalk_state_t const asXtalkOutStateTest = {
+ {0x7FFF, 0x0004, 0xFFFC, 0},
+ {0xFE00, 0x0008, 0xFFF8, 0x4000},
+ {0x0200, 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 __maybe_unused 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, arg_0[0]);
+ hwwrite(vortex->mmio, 0x244FC, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24500, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24504, 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, arg_0[0]);
+ hwwrite(vortex->mmio, 0x2450C, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24510, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24514, 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, arg_0[0]);
+ hwwrite(vortex->mmio, 0x2451C, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24520, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24524, 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, arg_0[0]);
+ hwwrite(vortex->mmio, 0x2452C, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24530, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24534, 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);
+ arg_0[1] = hwread(vortex->mmio, 0x244FC);
+ arg_0[2] = hwread(vortex->mmio, 0x24500);
+ arg_0[3] = hwread(vortex->mmio, 0x24504);
+}
+
+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);
+ arg_0[1] = hwread(vortex->mmio, 0x2450C);
+ arg_0[2] = hwread(vortex->mmio, 0x24510);
+ arg_0[3] = hwread(vortex->mmio, 0x24514);
+}
+
+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);
+ arg_0[1] = hwread(vortex->mmio, 0x2451C);
+ arg_0[2] = hwread(vortex->mmio, 0x24520);
+ arg_0[3] = hwread(vortex->mmio, 0x24524);
+}
+
+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);
+ arg_0[1] = hwread(vortex->mmio, 0x2452C);
+ arg_0[2] = hwread(vortex->mmio, 0x24530);
+ arg_0[3] = hwread(vortex->mmio, 0x24534);
+}
+
+#endif
+/* Gains */
+
+static void
+vortex_XtalkHw_SetGains(vortex_t * vortex, xtalk_gains_t const gains)
+{
+ int i;
+
+ for (i = 0; i < XTGAINS_SZ; i++) {
+ hwwrite(vortex->mmio, 0x244D0 + (i * 4), gains[i]);
+ }
+}
+
+static void
+vortex_XtalkHw_SetGainsAllChan(vortex_t * vortex)
+{
+ vortex_XtalkHw_SetGains(vortex, asXtalkGainsAllChan);
+}
+
+#if 0
+static void vortex_XtalkHw_GetGains(vortex_t * vortex, xtalk_gains_t gains)
+{
+ int i;
+
+ for (i = 0; i < XTGAINS_SZ; i++)
+ gains[i] = hwread(vortex->mmio, 0x244D0 + i * 4);
+}
+
+#endif
+/* Delay parameters */
+
+static void
+vortex_XtalkHw_SetDelay(vortex_t * vortex, unsigned short right,
+ unsigned short left)
+{
+ u32 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, u32 ctrl)
+{
+ hwwrite(vortex->mmio, 0x24660, ctrl);
+}
+static void vortex_XtalkHw_GetControlReg(vortex_t * vortex, u32 *ctrl)
+{
+ *ctrl = hwread(vortex->mmio, 0x24660);
+}
+#endif
+static void vortex_XtalkHw_SetSampleRate(vortex_t * vortex, u32 sr)
+{
+ u32 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, u32 *sr)
+{
+ *sr = (hwread(vortex->mmio, 0x24660) >> 3) & 0x1f;
+}
+
+#endif
+static void vortex_XtalkHw_Enable(vortex_t * vortex)
+{
+ u32 temp;
+
+ temp = (hwread(vortex->mmio, 0x24660) & 0x1FFFFFFF) | 0xC0000000;
+ temp |= 1;
+ hwwrite(vortex->mmio, 0x24660, temp);
+
+}
+
+static void vortex_XtalkHw_Disable(vortex_t * vortex)
+{
+ u32 temp;
+
+ temp = (hwread(vortex->mmio, 0x24660) & 0x1FFFFFFF) | 0xC0000000;
+ temp &= 0xfffffffe;
+ hwwrite(vortex->mmio, 0x24660, temp);
+
+}
+
+static void vortex_XtalkHw_ZeroIO(vortex_t * vortex)
+{
+ int i;
+
+ for (i = 0; i < 20; i++)
+ hwwrite(vortex->mmio, 0x24600 + (i << 2), 0);
+ for (i = 0; i < 4; i++)
+ hwwrite(vortex->mmio, 0x24650 + (i << 2), 0);
+}
+
+static void vortex_XtalkHw_ZeroState(vortex_t * vortex)
+{
+ vortex_XtalkHw_ZeroIO(vortex); // inlined
+
+ vortex_XtalkHw_SetLeftEQ(vortex, 0, 0, asXtalkCoefsZeros);
+ vortex_XtalkHw_SetRightEQ(vortex, 0, 0, asXtalkCoefsZeros);
+
+ vortex_XtalkHw_SetLeftXT(vortex, 0, 0, asXtalkCoefsZeros);
+ vortex_XtalkHw_SetRightXT(vortex, 0, 0, asXtalkCoefsZeros);
+
+ vortex_XtalkHw_SetGains(vortex, asXtalkGainsZeros); // inlined
+
+ vortex_XtalkHw_SetDelay(vortex, 0, 0); // inlined
+
+ vortex_XtalkHw_SetLeftDline(vortex, alXtalkDlineZeros); // inlined
+ vortex_XtalkHw_SetRightDline(vortex, alXtalkDlineZeros); // inlined
+ vortex_XtalkHw_SetLeftDline(vortex, alXtalkDlineZeros); // inlined
+ vortex_XtalkHw_SetRightDline(vortex, alXtalkDlineZeros); // inlined
+
+ vortex_XtalkHw_SetLeftEQStates(vortex, asXtalkInStateZeros,
+ asXtalkOutStateZeros);
+ vortex_XtalkHw_SetRightEQStates(vortex, asXtalkInStateZeros,
+ asXtalkOutStateZeros);
+ vortex_XtalkHw_SetLeftXTStates(vortex, asXtalkInStateZeros,
+ asXtalkOutStateZeros);
+ vortex_XtalkHw_SetRightXTStates(vortex, asXtalkInStateZeros,
+ asXtalkOutStateZeros);
+}
+
+static void vortex_XtalkHw_ProgramPipe(vortex_t * vortex)
+{
+
+ vortex_XtalkHw_SetLeftEQ(vortex, 0, 1, asXtalkCoefsPipe);
+ vortex_XtalkHw_SetRightEQ(vortex, 0, 1, asXtalkCoefsPipe);
+ vortex_XtalkHw_SetLeftXT(vortex, 0, 0, asXtalkCoefsZeros);
+ vortex_XtalkHw_SetRightXT(vortex, 0, 0, asXtalkCoefsZeros);
+
+ vortex_XtalkHw_SetDelay(vortex, 0, 0); // inlined
+}
+
+static void vortex_XtalkHw_ProgramXtalkWide(vortex_t * vortex)
+{
+
+ vortex_XtalkHw_SetLeftEQ(vortex, sXtalkWideKLeftEq,
+ sXtalkWideShiftLeftEq, asXtalkWideCoefsLeftEq);
+ vortex_XtalkHw_SetRightEQ(vortex, sXtalkWideKRightEq,
+ sXtalkWideShiftRightEq,
+ asXtalkWideCoefsRightEq);
+ vortex_XtalkHw_SetLeftXT(vortex, sXtalkWideKLeftXt,
+ sXtalkWideShiftLeftXt, asXtalkWideCoefsLeftXt);
+ vortex_XtalkHw_SetRightXT(vortex, sXtalkWideKLeftXt,
+ sXtalkWideShiftLeftXt,
+ asXtalkWideCoefsLeftXt);
+
+ vortex_XtalkHw_SetDelay(vortex, wXtalkWideRightDelay, wXtalkWideLeftDelay); // inlined
+}
+
+static void vortex_XtalkHw_ProgramXtalkNarrow(vortex_t * vortex)
+{
+
+ vortex_XtalkHw_SetLeftEQ(vortex, sXtalkNarrowKLeftEq,
+ sXtalkNarrowShiftLeftEq,
+ asXtalkNarrowCoefsLeftEq);
+ vortex_XtalkHw_SetRightEQ(vortex, sXtalkNarrowKRightEq,
+ sXtalkNarrowShiftRightEq,
+ asXtalkNarrowCoefsRightEq);
+ vortex_XtalkHw_SetLeftXT(vortex, sXtalkNarrowKLeftXt,
+ sXtalkNarrowShiftLeftXt,
+ asXtalkNarrowCoefsLeftXt);
+ vortex_XtalkHw_SetRightXT(vortex, sXtalkNarrowKLeftXt,
+ sXtalkNarrowShiftLeftXt,
+ asXtalkNarrowCoefsLeftXt);
+
+ vortex_XtalkHw_SetDelay(vortex, wXtalkNarrowRightDelay, wXtalkNarrowLeftDelay); // inlined
+}
+
+static void vortex_XtalkHw_ProgramDiamondXtalk(vortex_t * vortex)
+{
+
+ //sDiamondKLeftEq,sDiamondKRightXt,asDiamondCoefsLeftEq
+ vortex_XtalkHw_SetLeftEQ(vortex, sDiamondKLeftEq,
+ sDiamondShiftLeftEq, asDiamondCoefsLeftEq);
+ vortex_XtalkHw_SetRightEQ(vortex, sDiamondKRightEq,
+ sDiamondShiftRightEq, asDiamondCoefsRightEq);
+ vortex_XtalkHw_SetLeftXT(vortex, sDiamondKLeftXt,
+ sDiamondShiftLeftXt, asDiamondCoefsLeftXt);
+ vortex_XtalkHw_SetRightXT(vortex, sDiamondKLeftXt,
+ sDiamondShiftLeftXt, asDiamondCoefsLeftXt);
+
+ vortex_XtalkHw_SetDelay(vortex, wDiamondRightDelay, wDiamondLeftDelay); // inlined
+}
+
+static void vortex_XtalkHw_init(vortex_t * vortex)
+{
+ vortex_XtalkHw_ZeroState(vortex);
+}
+
+/* End of file */
diff --git a/sound/pci/au88x0/au88x0_xtalk.h b/sound/pci/au88x0/au88x0_xtalk.h
new file mode 100644
index 000000000..4791bd4fe
--- /dev/null
+++ b/sound/pci/au88x0/au88x0_xtalk.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/***************************************************************************
+ * au88x0_cxtalk.h
+ *
+ * Wed Nov 19 19:07:17 2003
+ * Copyright 2003 mjander
+ * mjander@users.sourceforge.org
+ ****************************************************************************/
+
+/*
+ */
+
+/* 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 u32 xtalk_dline_t[XTDLINE_SZ];
+typedef u16 xtalk_gains_t[XTGAINS_SZ];
+typedef u16 xtalk_instate_t[XTINST_SZ];
+typedef u16 xtalk_coefs_t[5][5];
+typedef u16 xtalk_state_t[5][4];
+
+static void vortex_XtalkHw_SetGains(vortex_t * vortex,
+ xtalk_gains_t const gains);
+static void vortex_XtalkHw_SetGainsAllChan(vortex_t * vortex);
+static void vortex_XtalkHw_SetSampleRate(vortex_t * vortex, u32 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 */