Commit 6af632be authored by Jaroslav Kysela's avatar Jaroslav Kysela

ALSA CVS update - Takashi Iwai <tiwai@suse.de>

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