diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
commit | 2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch) | |
tree | 848558de17fb3008cdf4d861b01ac7781903ce39 /sound/pci/au88x0 | |
parent | Initial commit. (diff) | |
download | linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip |
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sound/pci/au88x0')
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 */ |