diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
commit | 1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch) | |
tree | 4495d23e7b54ab5700e3839081e797c1eafe0db9 /kernel/drv/oss_envy24ht | |
download | oss4-upstream.tar.gz |
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'kernel/drv/oss_envy24ht')
-rw-r--r-- | kernel/drv/oss_envy24ht/.changelog | 1 | ||||
-rw-r--r-- | kernel/drv/oss_envy24ht/.devices | 12 | ||||
-rw-r--r-- | kernel/drv/oss_envy24ht/.name | 1 | ||||
-rw-r--r-- | kernel/drv/oss_envy24ht/.params | 5 | ||||
-rw-r--r-- | kernel/drv/oss_envy24ht/envy24ht.h | 230 | ||||
-rw-r--r-- | kernel/drv/oss_envy24ht/envy24ht_ac97.c | 137 | ||||
-rw-r--r-- | kernel/drv/oss_envy24ht/envy24ht_ap192.c | 617 | ||||
-rw-r--r-- | kernel/drv/oss_envy24ht/envy24ht_aureon.c | 2536 | ||||
-rw-r--r-- | kernel/drv/oss_envy24ht/envy24ht_julia.c | 547 | ||||
-rw-r--r-- | kernel/drv/oss_envy24ht/envy24ht_revo51.c | 938 | ||||
-rw-r--r-- | kernel/drv/oss_envy24ht/envy24ht_revo71.c | 338 | ||||
-rw-r--r-- | kernel/drv/oss_envy24ht/envy24ht_via.c.save | 324 | ||||
-rw-r--r-- | kernel/drv/oss_envy24ht/oss_envy24ht.c | 2411 | ||||
-rw-r--r-- | kernel/drv/oss_envy24ht/oss_envy24ht.man | 24 |
14 files changed, 8121 insertions, 0 deletions
diff --git a/kernel/drv/oss_envy24ht/.changelog b/kernel/drv/oss_envy24ht/.changelog new file mode 100644 index 0000000..972605e --- /dev/null +++ b/kernel/drv/oss_envy24ht/.changelog @@ -0,0 +1 @@ +800802xx by Artem Antonov: Added support for ESI Juli@ diff --git a/kernel/drv/oss_envy24ht/.devices b/kernel/drv/oss_envy24ht/.devices new file mode 100644 index 0000000..b90025d --- /dev/null +++ b/kernel/drv/oss_envy24ht/.devices @@ -0,0 +1,12 @@ +oss_envy24ht pci1412,1724 Generic ENVY24HT based sound card +oss_envy24ht pcs1412,3630 M Audio Revolution 7.1 +oss_envy24ht pcs1412,3631 M Audio Revolution 5.1 +oss_envy24ht pcs1412,6321 M Audio Audiophile 192 +oss_envy24ht pcs153b,1145 Terratec Aureon 7.1 Space +oss_envy24ht pcs153b,1147 Terratec Aureon 7.1 Sky +oss_envy24ht pcs153b,1149 Terratec PHASE 28 +oss_envy24ht pcs153b,1153 Terratec Aureon 7.1 Universe +oss_envy24ht pcs3031,4553 Ego Systems Juli@ *BETA* +oss_envy24ht pcs4933,4553 Audiotrak Prodigy 7.1 +oss_envy24ht pcs3137,4154 Audiotrak Prodigy HD2 +oss_envy24ht pcs1412,2401 Audiotrak Prodigy HD2 Advance DE diff --git a/kernel/drv/oss_envy24ht/.name b/kernel/drv/oss_envy24ht/.name new file mode 100644 index 0000000..77fd7c1 --- /dev/null +++ b/kernel/drv/oss_envy24ht/.name @@ -0,0 +1 @@ +VIA Envy24HT/PT (ICE1724) sound chipset diff --git a/kernel/drv/oss_envy24ht/.params b/kernel/drv/oss_envy24ht/.params new file mode 100644 index 0000000..4e6831b --- /dev/null +++ b/kernel/drv/oss_envy24ht/.params @@ -0,0 +1,5 @@ +int envy24ht_model = -1; +/* + * Select the Model number if the card isn't autodetected + * Values: 0 = Envy24ht 1=Envy24PT/HT-s compatible -1=Autodetect Default: -1 + */ diff --git a/kernel/drv/oss_envy24ht/envy24ht.h b/kernel/drv/oss_envy24ht/envy24ht.h new file mode 100644 index 0000000..f11029b --- /dev/null +++ b/kernel/drv/oss_envy24ht/envy24ht.h @@ -0,0 +1,230 @@ +/* + * Purpose: Common definitions for envy24ht driver files + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ +#include "ac97.h" +#include "midi_core.h" + +/* + * Various subvendor device ID's + */ +#define SSID_AUREON_SPACE 0x1145153b +#define SSID_AUREON_SKY 0x1147153b +#define SSID_AUREON_UNIVERSE 0x1153153b +#define SSID_PHASE28 0x1149153b +#define SSID_PRODIGY71 0x45534933 +#define SSID_PRODIGYHD2 0x41543137 +#define SSID_PRODIGYHD2_ADE 0x24011412 +#define SSID_JULIA 0x45533031 +#define SSID_AP192 0x36321412 + +typedef struct +{ + unsigned int dwSubVendorID; /* PCI[2C-2F], in BIG ENDIAN format */ + unsigned char bSize; /* size of EEPROM image in bytes */ + unsigned char bVersion; /* version equal 1 for this structure. */ + unsigned char bCodecConfig; /* PCI[60] */ + unsigned char bACLinkConfig; /* PCI[61] */ + unsigned char bI2SID; /* PCI[62] */ + unsigned char bSpdifConfig; /* PCI[63] */ + unsigned char bGPIOInitMask0; /* Corresponding bits need to be inited */ + /* 0 means write bit and 1 means cannot write */ + unsigned char bGPIOInitState0; /* Initial state of GPIO pins */ + unsigned char bGPIODirection0; /* GPIO Direction State */ + unsigned char bGPIOInitMask1; + unsigned char bGPIOInitState1; + unsigned char bGPIODirection1; + unsigned char bGPIOInitMask2; + unsigned char bGPIOInitState2; + unsigned char bGPIODirection2; + unsigned char bAC97RecSrc; + unsigned char bDACID[4]; /* I2S IDs for DACs [0 ~ 3] */ + unsigned char bADCID[4]; /* I2S IDs for ADCs [0 ~ 3] */ + unsigned char bDACID4; /* I2S ID for DAC#4 */ + unsigned char Reserved[32]; +} eeprom_data_t; + +typedef struct envy24ht_auxdrv envy24ht_auxdrv_t; + +typedef struct +{ + unsigned int svid; + char *product; + int nr_outs, nr_ins; /* # of analog channels */ + int flags; +#define MF_MIDI 0x00000001 /* Has MIDI port */ +#define MF_192K 0x00000002 /* Supports 192kHz */ +#define MF_SPDIFIN 0x00000004 /* Has S/PDIF input */ +#define MF_SPDIFOUT 0x00000008 /* Has S/PDIF output */ +#define MF_ENVY24PT 0x00000010 /* Envy24PT chip (no EEPROM) */ +#define MF_NOAC97 0x00000020 /* Ignore AC97 codec */ + const envy24ht_auxdrv_t *auxdrv; + unsigned char *eeprom_data; +} +card_spec; + +#define ICENSEMBLE_VENDOR_ID 0x1412 +#define ICENSEMBLE_ENVY24HT_ID 0x1724 +#define MAX_ENVY24HT_CARD 8 + +#ifdef USE_LICENSING +extern int options_data; +#endif + +#define MAX_ODEV 5 +#define MAX_IDEV 2 + +typedef struct +{ + int dev; + int open_mode; + int direction; + int fmt; + char *name; + + int channels; + volatile int is_active; + volatile int trigger_bits; + oss_native_word base; + unsigned char mask; + int dev_flags; +#define DF_MULTICH 0x00000001 +#define DF_SPDIF 0x00000002 +#define DF_AC3 0x00000004 +#define DF_DUPLEX 0x00000008 + int chmask; + int used_chmask; + + int state_bits; +} +envy24ht_portc; + +typedef struct +{ + int dta, clk; +} oss_i2c_t; + +/*****************/ + +typedef struct +{ + oss_device_t *osdev; + oss_mutex_t mutex; + oss_mutex_t low_mutex; + const envy24ht_auxdrv_t *auxdrv; + int codec_type; +#define CODEC_I2S 0 +#define CODEC_AC97 1 + int mpu1_attached, mpu2_attached; + oss_native_word ccs_base, mt_base; + int irq; + card_spec *model_data; + unsigned int subvendor; + eeprom_data_t eeprom; + char channel_names[4][10]; + unsigned short gpio_shadow_L; + unsigned char gpio_shadow_H; + + oss_i2c_t i2c; +/* + * MT mixer + */ + + int mixer_dev; + ac97_devc ac97devc; + + int nr_outdevs, nr_indevs; + envy24ht_portc play_portc[MAX_ODEV]; + envy24ht_portc rec_portc[MAX_IDEV]; + + int outportmask, inportmask; + int nr_play_channels, nr_rec_channels; + int first_dev; + + int open_count; + int speed, pending_speed, pending_speed_sel, speedbits, configured_rate_sel; + int prev_speed; /* Strictly for use by low level modules */ + int max_ratesel; + int syncsource; + + int use_src; + int ratelock; + int external_sync; + int busy_play_channels; + int busy_rec_channels; + + spdif_devc spdc; + int gains[6]; + int monitor[5]; + int recsrc; + +/* + * MIDI + */ + int midi_opened; + int midi_attached; + oss_midi_inputbyte_t midi_input_intr; + oss_midi_outputintr_t midi_output_intr; + volatile unsigned char input_byte; + int midi_dev; + +/* + * Low level stuff + */ + unsigned char dac1val[5], dac2val[11]; /* M Audio Revo 7.1 */ + unsigned short m_AC97Volume[6]; /* Terratec Aureon */ + unsigned char m_fAC97Mute[6]; /* Terratec Aureon */ + int m_DigInSource; /* Aureon */ + int m_LineInSource; /* Aureon */ + int m_SPDIFSource; /* Aureon */ + int m_ADCIndex; /* Aureon */ + int m_f1724SPDIF; /* Aureon */ + int m_SPDIFConfig; /* Aureon */ + int m_Frontjack; /* Aureon */ + unsigned char m_fDACMute[12]; /* Aureon */ + unsigned char m_DACVolume[13]; /* Aureon & Juli@ */ + unsigned short m_ADCVolume[8]; /* Aureon */ + unsigned char m_ADCMux; /* Aureon */ + unsigned char m_fSPDIFRecord; /* Aureon */ + unsigned char m_AuxMux; /* Aureon */ + unsigned int m_ClockSource; /* Aureon */ + unsigned int m_Out0Source; /* Aureon */ + + int mute; + int reclevel; + timeout_id_t timeout_id; /* Juli@ */ + + unsigned char syncstart_mask; +} +envy24ht_devc; + +struct envy24ht_auxdrv +{ + void (*card_init) (envy24ht_devc * devc); + int (*mixer_init) (envy24ht_devc * devc, int dev, int group); + void (*set_rate) (envy24ht_devc * devc); + int (*get_locked_status) (envy24ht_devc * devc); + int (*spdif_mixer_init) (envy24ht_devc * devc, int dev, int group); + int (*private1) (envy24ht_devc * devc, int value); + int (*audio_ioctl) (envy24ht_devc * devc, envy24ht_portc * portc, unsigned int cmd, + ioctl_arg arg); + void (*card_uninit) (envy24ht_devc * devc); +}; + +struct speed_sel +{ + int speed, speedbits; +}; + +void envy24ht_write_cci (envy24ht_devc * devc, int pos, int data); +int envy24ht_read_cci (envy24ht_devc * devc, int pos); diff --git a/kernel/drv/oss_envy24ht/envy24ht_ac97.c b/kernel/drv/oss_envy24ht/envy24ht_ac97.c new file mode 100644 index 0000000..46d2382 --- /dev/null +++ b/kernel/drv/oss_envy24ht/envy24ht_ac97.c @@ -0,0 +1,137 @@ +/* + * Purpose: Low level routines for AC97 based Envy24HT boards (mainly Envy24-PT). + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include "oss_envy24ht_cfg.h" + +#include "spdif.h" +#include "envy24ht.h" + +#define AKM_ADDRESS 0x10 +#if 0 +# define PRT_STATUS(v) outb(v&0xff, 0x378) +#else +# define PRT_STATUS(v) +#endif + +#if 0 +static unsigned char +i2c_read (envy24ht_devc * devc, unsigned char addr, unsigned char pos) +{ + int i; + unsigned char data; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + OUTB (devc->osdev, pos, devc->ccs_base + 0x11); /* Offset */ + OUTB (devc->osdev, addr << 1, devc->ccs_base + 0x10); /* Read address */ + + for (i = 0; i < 2000; i++) + { + unsigned char status = INB (devc->osdev, devc->ccs_base + 0x13); + if (!(status & 1)) + break; + + } + + oss_udelay (1); + data = INB (devc->osdev, devc->ccs_base + 0x12); + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + + return data; +} + +static void +i2c_write (envy24ht_devc * devc, unsigned char addr, unsigned char pos, + unsigned char data) +{ + int i; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + OUTB (devc->osdev, pos, devc->ccs_base + 0x11); /* Offset */ + OUTB (devc->osdev, data, devc->ccs_base + 0x12); /* Data */ + OUTB (devc->osdev, (addr << 1) | 1, devc->ccs_base + 0x10); /* Write address */ + + for (i = 0; i < 2000; i++) + { + unsigned char status = INB (devc->osdev, devc->ccs_base + 0x13); + if (!(status & 1)) + break; + + } + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); +} +#endif + +/*ARGSUSED*/ +static void +init_cs8415a (envy24ht_devc * devc) +{ +} + +/*ARGSUSED*/ +static void +init_wm8728 (envy24ht_devc * devc) +{ + +#if 0 + printk ("Regs="); + for (i = 0; i < 0x18; i++) + { + PRT_STATUS (2); + printk ("%02x ", i2c_read (devc, addr, i)); + PRT_STATUS (0); + } + printk ("\n"); +#endif +} + +static void +ac97_card_init (envy24ht_devc * devc) +{ + + PRT_STATUS (0); + PRT_STATUS (0x01); + PRT_STATUS (0); + + OUTW (devc->osdev, 0x000f, devc->ccs_base + 0x14); /* GPIO */ + + oss_udelay (1000); + + devc->recsrc = 0; + init_cs8415a (devc); + init_wm8728 (devc); +} + +/*ARGSUSED*/ +static int +ac97_mixer_init (envy24ht_devc * devc, int dev, int group) +{ + return 0; +} + +#if 0 +static int +ac97_private1 (envy24ht_devc * devc, int value) +{ + i2c_write (devc, AKM_ADDRESS, (value >> 8) & 0xff, value & 0xff); + return 0; +} +#endif + +envy24ht_auxdrv_t envy24ht_ac97_auxdrv = { + ac97_card_init, + ac97_mixer_init +}; diff --git a/kernel/drv/oss_envy24ht/envy24ht_ap192.c b/kernel/drv/oss_envy24ht/envy24ht_ap192.c new file mode 100644 index 0000000..9929c27 --- /dev/null +++ b/kernel/drv/oss_envy24ht/envy24ht_ap192.c @@ -0,0 +1,617 @@ +/* + * Purpose: Low level routines for M Audio Audiophile 192 + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include "oss_envy24ht_cfg.h" + +#include "spdif.h" +#include "envy24ht.h" + +static char channel_names[4][10] = { + "main", + "unused1", + "unused2", + "unused3" +}; + +#define BIT(x) (1<<(x)) +#define BIT3 BIT(3) + +/*----- SPI bus for CODEC communication. */ +/* */ +#define SPI_CLK 1 /* Clock output to CODEC's, rising edge clocks data. */ +#define SPI_DIN 2 /* Data input from the CODEC. */ +#define SPI_DOUT 3 /* Data output to the CODEC. */ +#define SPI_CS0n (1<<4) /* Selects first chip. */ +#define SPI_CS1n (1<<5) /* Selects second chip. */ + +#define SPI_CC_AK4358 0x02 /* C1:C0 for ak4358. */ +#define SPI_CC_AK4114 0x02 /* C1:C0 for ak4114. */ +#define WRITEMASK 0xffff +/*----- Revolution defines. */ +/* */ +#define ap192_AK4114 (1) /* iDevice value for AK4114 DIR. */ +#define ap192_AK4358 (2) /* iDevice value for AK4358 D/A. */ + +static unsigned int +GpioReadAll (envy24ht_devc * devc) +{ + return INW (devc->osdev, devc->ccs_base + 0x14); +} + +static void +GPIOWriteAll (envy24ht_devc * devc, unsigned int data) +{ + OUTW (devc->osdev, 0xffff, devc->ccs_base + 0x18); /* GPIO direction */ + OUTW (devc->osdev, 0x0000, devc->ccs_base + 0x16); /* GPIO write mask */ + + OUTW (devc->osdev, data, devc->ccs_base + 0x14); +} + +static void +GPIOWrite (envy24ht_devc * devc, int pos, int bit) +{ + int data = GpioReadAll (devc); + + bit = (bit != 0); + + data &= ~(1 << pos); + data |= (bit << pos); + + GPIOWriteAll (devc, data); +} + +void +ap192_Assert_CS (envy24ht_devc * devc, int iDevice) +/* +***************************************************************************** +* Assert chip select to specified GPIO-connected device. +* iDevice: ap192_AK4114=DIG, ap192_AK4358=DAC. +****************************************************************************/ +{ + unsigned int dwGPIO; /* Current GPIO's. */ + dwGPIO = GpioReadAll (devc); /* Read current GPIO's. */ + dwGPIO |= (SPI_CS0n | SPI_CS1n); /* Reset CS bits. */ + switch (iDevice) /* Select CS#. */ + { + case ap192_AK4358: + dwGPIO &= ~SPI_CS0n; + break; /* DAC */ + case ap192_AK4114: + dwGPIO &= ~SPI_CS1n; + break; /* DIG */ + default: + break; + } + GPIOWriteAll (devc, dwGPIO); /* Write hardware. */ +} + +void +ap192_DeAssert_CS (envy24ht_devc * devc) +/* +***************************************************************************** +* De-Assert all chip selects. +****************************************************************************/ +{ + unsigned int dwGPIO = GpioReadAll (devc); /* Current GPIO's. */ + dwGPIO |= (SPI_CS0n | SPI_CS1n); /* Clear CS bits. */ + GPIOWriteAll (devc, dwGPIO); /* Write back to hardware. */ +} + +/*#define _delay() oss_udelay(1) */ +#define _delay() {} + +void +ap192_WriteSpiAddr (envy24ht_devc * devc, int iDevice, unsigned char bReg) +/* +***************************************************************************** +* Write the address byte part of the SPI serial stream. +* iDevice: ap192_AK4358=DAC, ap192_AK4114=DIG, etc. +****************************************************************************/ +{ + unsigned char bHdr; + unsigned char bNum; +/* Built 8-bit packet header: C1,C0,R/W,A4,A3,A2,A1,A0. */ +/* */ + switch (iDevice) + { + case ap192_AK4358: + bHdr = SPI_CC_AK4358 << 6; + break; + case ap192_AK4114: + bHdr = SPI_CC_AK4114 << 6; + break; + default: + bHdr = 0; + break; + } + bHdr = bHdr | 0x20 | (bReg & 0x1F); /* "write" + address. */ +/* Write header to SPI. */ +/* */ + for (bNum = 0; bNum < 8; bNum++) + { + GPIOWrite (devc, SPI_CLK, 0); /* Drop clock low. */ + _delay (); + GPIOWrite (devc, SPI_DOUT, 0x080 & bHdr); /* Write data bit. */ + _delay (); + GPIOWrite (devc, SPI_CLK, 1); /* Raise clock. */ + _delay (); + bHdr <<= 1; /* Next bit. */ + } +} + +void +ap192_WriteSpiReg (envy24ht_devc * devc, int iDevice, unsigned char bReg, + unsigned char bData) +/* +***************************************************************************** +* Writes one register in specified CHIP. +* devc = PCI slot code of specific board. +* iDevice: ap192_AK4358=DAC, ap192_AK4114=DIG, etc. +****************************************************************************/ +{ + unsigned char bNum; + GPIOWrite (devc, SPI_DOUT, 0); /* Init SPI signals. */ + GPIOWrite (devc, SPI_CLK, 1); /* */ +/* Drop the chip select low. */ +/* Wait at least 150 nS. */ +/* */ + ap192_Assert_CS (devc, iDevice); + _delay (); +/* Write the address byte. */ +/* */ + ap192_WriteSpiAddr (devc, iDevice, bReg); +/* Write the data byte. */ +/* */ + for (bNum = 0; bNum < 8; bNum++) + { + GPIOWrite (devc, SPI_CLK, 0); /* Drop clock low. */ + _delay (); + GPIOWrite (devc, SPI_DOUT, 0x080 & bData); /* Write data bit. */ + _delay (); + GPIOWrite (devc, SPI_CLK, 1); /* Raise clock. */ + _delay (); + bData <<= 1; /* Next bit. */ + } +/* De-assert chip selects. */ +/* */ + ap192_DeAssert_CS (devc); + _delay (); +} + + +#define GPIO_MUTEn 22 /* Converter mute signal. */ +void +ap192_Mute (envy24ht_devc * devc, int bMute) +/* +***************************************************************************** +* Mutes all outputs if bMute=TRUE. +****************************************************************************/ +{ + if (bMute) + { +/* Soft-mute. Delay currently unspecified, try ½ second. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 1, 0x03); + _delay (); +/* Switch mute transistors on. */ + GPIOWrite (devc, GPIO_MUTEn, 0); + } + else + { +/* Switch mute transistors off. Delay currently unspecified, try ½ second. */ + GPIOWrite (devc, GPIO_MUTEn, 1); + _delay (); +/* Release Soft-mute. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 1, 0x01); + } + + devc->mute = bMute; +} + + +void +ap192_Set_OutAttn (envy24ht_devc * devc, unsigned char bChan, int iAttn) +/* +***************************************************************************** +* Sets the attenuation on one output channel. +* bChan = Channel number (0..7). +* Channel 0:1 = front, 2:3 = center/sub, 4:5 = rear, 6:7 = headphones. +* Registers are 0x04, 05, 06, 07, 08, 09, 0B, 0C respectively +* iAttn = Number of steps to attenuate CODEC. +* Each step equals .5dB (-127..0) +****************************************************************************/ +{ + unsigned char bIndex; + unsigned char bAttn; + if (bChan > 7 || iAttn > 0 || iAttn < -127) /* parameter test */ + { + cmn_err (CE_CONT, "Dnvalid data! %d=bChan, %d=iAttn", bChan, iAttn); + return; + } + if (bChan < 6) + bIndex = 0x04 + bChan; /* for registers 0x04..0x09 */ + else + bIndex = 0x05 + bChan; /* for registers 0x0B..0x0C */ + bAttn = (0x80 + (unsigned char) (iAttn + 127)); /* 7F is max volume. */ +/* MSB enables attenuation. */ + ap192_WriteSpiReg (devc, ap192_AK4358, bIndex, bAttn); +} + +static void +ap192_Set_48K_Mode (envy24ht_devc * devc) +/* +***************************************************************************** +* Sets Chip and Envy24 for 8kHz-48kHz sample rates. +****************************************************************************/ +{ +/* ICE MCLK = 256x. */ + OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 2) & ~BIT3, + devc->mt_base + 2); +/* DFS=normal, RESET. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x4E); +/* DFS=normal, NORMAL OPERATION. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x4F); + + /* Set ADC modes */ + GPIOWrite (devc, 8, 0); /* CKS0 = 0. MCLK = 256x */ + GPIOWrite (devc, 9, 0); /* DFS0 = 0. */ + GPIOWrite (devc, 10, 0); /* DFS1 = 0. Single speed mode. */ + + /* Reset ADC timing */ + GPIOWrite (devc, 11, 0); + _delay (); + GPIOWrite (devc, 11, 1); +} + +static void +ap192_Set_96K_Mode (envy24ht_devc * devc) +/* +***************************************************************************** +* Sets CODEC and Envy24 for 60kHz-96kHz sample rates. +****************************************************************************/ +{ +/* ICE MCLK = 256x. */ + OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 2) & ~BIT3, + devc->mt_base + 2); +/* DFS=double-speed, RESET. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x5E); +/* DFS=double-speed, NORMAL OPERATION. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x5F); + + /* Set ADC modes */ + GPIOWrite (devc, 8, 0); /* CKS0 = 0. MCLK = 256x */ + GPIOWrite (devc, 9, 1); /* DFS0 = 0. */ + GPIOWrite (devc, 10, 0); /* DFS1 = 0. Single speed mode. */ + + /* Reset ADC timing */ + GPIOWrite (devc, 11, 0); + _delay (); + GPIOWrite (devc, 11, 1); +} + +static void +ap192_Set_192K_Mode (envy24ht_devc * devc) +/* +***************************************************************************** +* Sets CODEC and Envy24 for 120kHz-192kHz sample rate. +****************************************************************************/ +{ +/* ICE MCLK = 128x. */ + OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 2) | BIT3, + devc->mt_base + 2); + _delay (); +/*----- SET THE D/A. */ +/* DFS=quad-speed, RESET. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x6E); + _delay (); +/* DFS=quad-speed, NORMAL OPERATION. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x6F); + + /* SPDIF */ + ap192_WriteSpiReg (devc, ap192_AK4114, 0x00, 0x0d); + ap192_WriteSpiReg (devc, ap192_AK4114, 0x00, 0x0f); + + /* Set ADC modes */ + GPIOWrite (devc, 8, 1); /* CKS0 = 0. MCLK = 256x */ + GPIOWrite (devc, 9, 0); /* DFS0 = 0. */ + GPIOWrite (devc, 10, 1); /* DFS1 = 0. Single speed mode. */ + + /* Reset ADC timing */ + GPIOWrite (devc, 11, 0); + _delay (); + GPIOWrite (devc, 11, 1); +} + +static int +set_dac (envy24ht_devc * devc, int reg, int level) +{ + if (level < 0) + level = 0; + if (level > 0x7f) + level = 0x7f; + + ap192_WriteSpiReg (devc, ap192_AK4358, reg, level | 0x80); + + return level; +} + +static void +AK4358_Init (envy24ht_devc * devc) +{ +/*===== AK4358 D/A initialization. Leave soft-muted. */ +/* */ +/* Power down, reset, normal mode. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x00); +/* Power up, reset, normal mode */ + ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x4E); +/* Reset timing, Mode 3(I2S), disable auto clock detect, sharp roll off. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 0, 0x06); +/* Soft mute, reset timing. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 1, 0x02); +/* De-emphasis off. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 3, 0x01); +/* Max volume on all 8 channels. */ + set_dac (devc, 0x04, 0x7f); + set_dac (devc, 0x05, 0x7f); + set_dac (devc, 0x06, 0x7f); + set_dac (devc, 0x07, 0x7f); + set_dac (devc, 0x08, 0x7f); + set_dac (devc, 0x09, 0x7f); + set_dac (devc, 0x0b, 0x7f); + set_dac (devc, 0x0c, 0x7f); + +/* Datt mode 0, DZF non-invert, DCLK polarity 0, PCM mode, DCKS 512fs, TDM normal. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 0xA, 0x00); +/* DZF control disabled. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 0xD, 0x00); + ap192_WriteSpiReg (devc, ap192_AK4358, 0xE, 0x00); + ap192_WriteSpiReg (devc, ap192_AK4358, 0xF, 0x00); +/* Power up, normal operation. */ + ap192_WriteSpiReg (devc, ap192_AK4358, 2, 0x4F); +} + +static void +ap192_set_rate (envy24ht_devc * devc) +{ + int tmp; + + tmp = INB (devc->osdev, devc->mt_base + 0x02); + if (devc->speed <= 48000) + { + ap192_Set_48K_Mode (devc); + OUTB (devc->osdev, tmp & ~BIT (3), devc->mt_base + 0x02); + return; + } + + if (devc->speed <= 96000) + { + ap192_Set_96K_Mode (devc); + + return; + } + + ap192_Set_192K_Mode (devc); + OUTB (devc->osdev, tmp | BIT (3), devc->mt_base + 0x02); +} + +static int +ap192_audio_ioctl (envy24ht_devc * devc, envy24ht_portc * portc, unsigned int cmd, + ioctl_arg arg) +{ + int left, right, value; + + switch (cmd) + { + case SNDCTL_DSP_GETPLAYVOL: + if (portc != &devc->play_portc[0]) + return OSS_EINVAL; + left = (devc->gains[0] & 0xff) * 100 / 0x7f; + right = ((devc->gains[0] >> 8) & 0xff) * 100 / 0x7f; + return *arg = (left | (right << 8)); + break; + + case SNDCTL_DSP_SETPLAYVOL: + if (portc != &devc->play_portc[0]) + return OSS_EINVAL; + value = *arg; + left = value & 0xff; + right = (value >> 8) & 0xff; + + left = (left * 0x7f) / 100; + right = (right * 0x7f) / 100; + left = set_dac (devc, 0x04, left); + right = set_dac (devc, 0x05, right); + devc->gains[0] = left | (right << 8); + mixer_devs[devc->mixer_dev]->modify_counter++; + return 0; + break; + } + return OSS_EINVAL; +} + +static int +ap192_set_control (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24ht_devc *devc = mixer_devs[dev]->hw_devc; + + if (cmd == SNDCTL_MIX_READ) + switch (ctrl) + { + case 0: + return devc->mute; + + default: + return OSS_EINVAL; + } + + if (cmd == SNDCTL_MIX_WRITE) + switch (ctrl) + { + case 0: + value = !!value; + ap192_Mute (devc, value); + return devc->mute; + + default: + return OSS_EINVAL; + } + + return OSS_EINVAL; +} + +static int +ap192_set_ak4358 (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24ht_devc *devc = mixer_devs[dev]->hw_devc; + + if (cmd == SNDCTL_MIX_READ) + { + if (ctrl < 0 || ctrl > 4) + return OSS_EIO; + + return devc->gains[ctrl]; + } + + if (cmd == SNDCTL_MIX_WRITE) + { + int left, right; + + left = value & 0xff; + right = (value >> 8) & 0xff; + + switch (ctrl) + { + case 0: /* PCM */ + left = set_dac (devc, 0x04, left); + right = set_dac (devc, 0x05, right); + break; + + case 1: /* Line IN */ + /* Line IN monitor permits panning but we don't support it */ + left = set_dac (devc, 0x06, left); + set_dac (devc, 0x07, 0); + set_dac (devc, 0x08, 0); + right = set_dac (devc, 0x09, right); + break; + + case 2: /* S/PDIF */ + left = set_dac (devc, 0x0b, left); + left = set_dac (devc, 0x0c, right); + break; + + default: + return OSS_EINVAL; + } + + value = left | (right << 8); + return devc->gains[ctrl] = value; + } + + return OSS_EINVAL; +} + +#if 0 +/*ARGSUSED*/ +static int +set_mongain (envy24ht_devc * devc, int reg, int value) +{ + if (value < 0) + value = 0; + if (value > 79) + value = 79; + + return value; +} +#endif + +/*ARGSUSED*/ +static int +ap192_mixer_init (envy24ht_devc * devc, int dev, int g) +{ + int group = g; + int err; + + if ((group = mixer_ext_create_group (dev, g, "VOL")) < 0) + return group; + + if ((err = mixer_ext_create_control (dev, group, + 0, ap192_set_control, + MIXT_ONOFF, + "ENVY24_MUTE", 2, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 0, ap192_set_ak4358, + MIXT_STEREOSLIDER, + "ENVY24_PCM", 0x7f, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 1, ap192_set_ak4358, + MIXT_STEREOSLIDER, + "ENVY24_IN", 0x7f, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 2, ap192_set_ak4358, + MIXT_STEREOSLIDER, + "ENVY24_SPDIF", 0x7f, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + return 0; +} + +static void +ap192_card_init (envy24ht_devc * devc) +{ + + int i; + +#if 1 + OUTW (devc->osdev, 0xffff, devc->ccs_base + 0x18); /* GPIO direction */ + OUTW (devc->osdev, 0x0000, devc->ccs_base + 0x16); /* GPIO write mask */ + OUTW (devc->osdev, 0xffff, devc->ccs_base + 0x14); /* Initial bit state */ + + OUTB (devc->osdev, 0xff, devc->ccs_base + 0x1a); /* GPIO direction for bits 16:22 */ + OUTB (devc->osdev, 0x00, devc->ccs_base + 0x1f); /* GPIO mask for bits 16:22 */ + OUTB (devc->osdev, 0xff, devc->ccs_base + 0x1e); /* GPIO data for bits 16:22 */ +#endif + + memcpy (devc->channel_names, channel_names, sizeof (channel_names)); + AK4358_Init (devc); + ap192_Set_48K_Mode (devc); + + for (i = 0; i < 5; i++) + devc->gains[i] = 0x7f7f; + + ap192_Mute (devc, 0); + + GPIOWrite (devc, 5, 0); /* Select S/PDIF output mux */ + GPIOWrite (devc, 5, 0); /* Select S/PDIF output mux */ + +} + +envy24ht_auxdrv_t envy24ht_ap192_auxdrv = { + ap192_card_init, + ap192_mixer_init, + ap192_set_rate, + NULL, + NULL, + NULL, /* ap192_private1 */ + ap192_audio_ioctl +}; diff --git a/kernel/drv/oss_envy24ht/envy24ht_aureon.c b/kernel/drv/oss_envy24ht/envy24ht_aureon.c new file mode 100644 index 0000000..98c8d72 --- /dev/null +++ b/kernel/drv/oss_envy24ht/envy24ht_aureon.c @@ -0,0 +1,2536 @@ +/* + * Purpose: Low level routines for Terrate Aureon 7.1 family + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include "oss_envy24ht_cfg.h" + +#define IN +#define OUT +#define UCHAR unsigned char +#define PUCHAR UCHAR* +#define BYTE unsigned char +#define PBYTE BYTE* +#define BOOLEAN unsigned char +#define ULONG unsigned int +#define PULONG ULONG* +#define USHORT unsigned short +#define PUSHORT USHORT* + +#define WORD USHORT +#define PWORD PUSHORT + +/* + * Default levels + */ +#define WM_OUT_DEFAULT 0x7F +#define WM_OUT_MAX 0x7F +#define WM_INP_DEFAULT 0x0C /* for 0dB */ +#define AC97_INP_DEFAULT 0x1f1f +#define AC97_INP_MAX 0x1f + +#define REG_CCS 1 +#define REG_MT 2 + +#define WM_MASTER_MODE_CNTRL 0 + +/* List of the WKN CCS Control Registers. */ +#define WKN_CONTROL_REG 0x00 +#define WKN_INT_MASK_REG 0x01 +#define WKN_INT_STAT_REG 0x02 +#define WKN_DMA_INT 0x10 +#define WKN_UART_RECV 0x80 +#define WKN_UART_TRAN 0x20 + +/* #define WKN_INDEX_REG 0x03 */ + +#define WKN_SYS_CONFIG 0x04 +#define WKN_49MHZ 0x40 +#define WKN_ACLINK_CONFIG 0x05 +#define CODEC_AKM 0x80 + +#define WKN_I2S_CONFIG 0x06 +#define WKN_SPDIF_CONFIG 0x07 + +#define SPDIF_ENABLE_MASK 0x00010000 + +/* #define WKN_AC97_INDEX 0x08 */ +/* #define WKN_AC97_CMDSTAT 0x09 */ +#define WKN_TX_QUE 0x0A +#define WKN_RX_QUE 0x0B + +#define WKN_MIDI_PORT1_DATA_REG 0x0C +#define WKN_MIDI_PORT1_CMD_REG 0x0D +#define WKN_NMI_EXTSTAT_REG 0x0E + +#define WKN_I2C_DEVADDR 0x10 +#define WKN_I2C_AKM 0x20 +#define WKN_I2C_WRITE 0x01 +#define WKN_I2C_READ 0x00 +#define WKN_I2C_BYTEADDR 0x11 +#define WKN_I2C_DATA 0x12 +#define WKN_I2C_STATUS 0x13 +#define WKN_I2C_BUSY 0x01 +#define WKN_GPIO_DATA0 0x14 +#define WKN_GPIO_DATA1 0x15 +#define WKN_GPIO_DATA2 0x1E +#define WKN_GPIO_WRT_MASK0 0x16 +#define WKN_GPIO_WRT_MASK1 0x17 +#define WKN_GPIO_WRT_MASK2 0x1F +#define WKN_GPIO_DIR0 0x18 +#define WKN_GPIO_DIR1 0x19 +#define WKN_GPIO_DIR2 0x1A +#define WKN_GPIO_DATA16 0x1E +#define WKN_GPIO_WRT_MASK16 0x1F + +#define GPIO_DATA_ADR_MASK 0x000000FF +#define GPIO_LD_DATA_H_MASK 0x00000100 +#define GPIO_LD_DATA_L_MASK 0x00000200 +#define GPIO_LD_ADR_MASK 0x00000400 +#define GPIO_GO_MASK 0x00000800 +#define GPIO_WM8770_CS_MASK 0x00001000 +#define GPIO_INT_REST_MASK 0x00002000 +#define GPIO_REMOTE_MASK 0x00004000 +#define GPIO_HEADPHONE_MASK 0x00004000 +#define GPIO_DIGITAL_SEL 0x00008000 +#define GPIO_AC97_RESET_MASK 0x00010000 +#define GPIO_SDA_TX_MASK 0x00020000 +#define GPIO_SDA_MASK 0x00040000 +#define GPIO_SCL_MASK 0x00080000 +#define GPIO_WM8770_RS_MASK 0x00100000 +#define GPIO_CS8415_CDTO_MASK 0x00200000 +#define GPIO_CS8415_CS_MASK 0x00400000 + +#define WM_DAC1_VOL_CNTRL 0x00 +#define WM_DAC2_VOL_CNTRL 0x02 +#define WM_DAC3_VOL_CNTRL 0x04 +#define WM_DAC4_VOL_CNTRL 0x06 +#define WM_MASTER_VOL_CNTRL 0x08 +#define WM_POWER_CNTRL 0x18 +#define WM_ADC_GAIN_CNTRL 0x19 +#define WM_ADC_INPUT_MX 0x1B +#define WM_OUT12_SELECT 0x1C +#define WM_OUT34_SELECT 0x1D + +#define CS_CONTROL_1 0x01 +#define CS_CONTROL_2 0x02 +#define CS_CLK_SRC_CNTRL 0x04 +#define CS_SER_OUT_FORMAT 0x06 + +#define MT_SAMPLE_RATE_REG 0x01 +#define MT_48KHZ 0x0 +#define MT_24KHZ 0x01 +#define MT_12KHZ 0x02 +#define MT_9p6KHZ 0x03 +#define MT_32KHZ 0x04 +#define MT_16KHZ 0x05 +#define MT_8KHZ 0x06 +#define MT_96KHZ 0x07 +#define MT_192KHZ 0x0E +#define MT_64KHZ 0x0F +#define MT_44p1KHZ 0x08 +#define MT_22p05KHZ 0x09 +#define MT_11p025KHZ 0x0A +#define MT_88p2KHZ 0x0B +#define MT_176p4KHZ 0x0C + +#define MT_DATA_FORMAT_REG 0x02 +#define MT_128X 0x08 +#define MT_INTR_MASK_REG 0x03 + +#define SRC_PDMA 0x00 +#define SRC_PSDIN0_L 0x02 +#define SRC_PSDIN0_R 0x03 +#define SRC_SPDIN_L 0x06 +#define SRC_SPDIN_R 0x07 +#define SRC_MASK 0x07 + +static const UCHAR gWMRegister[] = { WM_DAC1_VOL_CNTRL, + WM_DAC1_VOL_CNTRL + 1, + WM_DAC2_VOL_CNTRL, + WM_DAC2_VOL_CNTRL + 1, + WM_DAC3_VOL_CNTRL, + WM_DAC3_VOL_CNTRL + 1, + WM_DAC4_VOL_CNTRL, + WM_DAC4_VOL_CNTRL + 1, + WM_MASTER_VOL_CNTRL, + WM_MASTER_VOL_CNTRL + 1, + WM_ADC_GAIN_CNTRL, + WM_ADC_GAIN_CNTRL + 1 +}; + +#define AC97_IDXREG_RESET 0x00 +#define AC97_IDXREG_STEREO_OUT 0x02 +#define AC97_IDXREG_MONO_OUT 0x06 +#define AC97_IDXREG_PCBEEP 0x0A +#define AC97_IDXREG_PHONE 0x0C +#define AC97_IDXREG_MIC_IN 0x0E +#define AC97_IDXREG_LINE_IN 0x10 +#define AC97_IDXREG_CD_IN 0x12 +#define AC97_IDXREG_VIDEO_IN 0x14 +#define AC97_IDXREG_AUX_IN 0x16 +#define AC97_IDXREG_PCM_OUT 0x18 +#define AC97_IDXREG_RECORD_SELECT 0x1A +#define AC97_IDXREG_RECORD_GAIN 0x1C +#define AC97_IDXREG_GEN_PURPOSE 0x20 +#define AC97_IDXREG_POWER_DOWN 0x26 +#define AC97_IDXREG_TEST_CONTROL 0x5A +#define AC97_IDXREG_VENDOR_RESV 0x7A +#define AC97_IDXREG_VENDOR_ID1 0x7C +#define AC97_IDXREG_VENDOR_ID2 0x7E +/* AC97 register - AC97_IDXREG_RECORD_SELECT */ +#define AC97_REC_SELECT_MIC 0x0000 +#define AC97_REC_SELECT_LINEIN 0x0404 +#define AC97_REC_SELECT_ST_MIX 0x0505 + +/* + * List of the Channel IDs + */ +#define CH_MASTER_LEFT ((OUT_MASTER<<16)|CH_LEFT) +#define CH_MASTER_RIGHT ((OUT_MASTER<<16)|CH_RIGHT) +#define CH_MASTER_BOTH ((OUT_MASTER<<16)|CH_BOTH) + +#define CH_OUT12_LEFT ((OUT_12<<16)|CH_LEFT) +#define CH_OUT12_RIGHT ((OUT_12<<16)|CH_RIGHT) +#define CH_OUT12_BOTH ((OUT_12<<16)|CH_BOTH) +#define CH_OUT34_LEFT ((OUT_34<<16)|CH_LEFT) +#define CH_OUT34_RIGHT ((OUT_34<<16)|CH_RIGHT) +#define CH_OUT34_BOTH ((OUT_34<<16)|CH_BOTH) +#define CH_OUT56_LEFT ((OUT_56<<16)|CH_LEFT) +#define CH_OUT56_RIGHT ((OUT_56<<16)|CH_RIGHT) +#define CH_OUT56_BOTH ((OUT_56<<16)|CH_BOTH) +#define CH_OUT78_LEFT ((OUT_78<<16)|CH_LEFT) +#define CH_OUT78_RIGHT ((OUT_78<<16)|CH_RIGHT) +#define CH_OUT78_BOTH ((OUT_78<<16)|CH_BOTH) + +#define CH_IN12_LEFT ((IN_12<<16)|CH_LEFT) +#define CH_IN12_RIGHT ((IN_12<<16)|CH_RIGHT) +#define CH_IN12_BOTH ((IN_12<<16)|CH_BOTH) + +#define CH_FRONT_BOTH CH_OUT12_BOTH +#define CH_REAR_BOTH CH_OUT34_BOTH +#define CH_CENTER CH_OUT56_LEFT +#define CH_LFE CH_OUT56_RIGHT +#define CH_BS_BOTH CH_OUT78_BOTH + +#define MAX_VOLUME 0x7F +#define MIN_VOLUME 0x00 + +#define MAX_GAIN 0x1F + +#define MT_PLAY_REC_UNDOVR 0x01A +#define MT_INTR_STATUS_REG 0x00 +#define MT_INTR_MASK_REG 0x03 +#define MT_SPDIF_REG 0x3C + +/* List of AC97 inputs */ +#define AC97_CD 0 +#define AC97_AUX 1 +#define AC97_LINE 2 +#define AC97_MIC 3 +#define AC97_PHONO 4 +#define AC97_LINE2 5 +#define AC97_COUNT 6 /* Must match devc->m_AC97Volume definition in envy24ht.h */ + +/* Channel def */ +#define CH_LEFT 0x00000001 +#define CH_RIGHT 0x00000002 +#define CH_BOTH (CH_LEFT|CH_RIGHT) +#define CH_NOP 0xFFFFFFFF + +/* + * List of inputs + */ +#define ADC_CD 0 +#define ADC_AUX 1 +#define ADC_LINE 2 +#define ADC_MIC 3 +#define ADC_PHONO 4 +#define ADC_WTL 5 +#define ADC_LINE_REAR 6 +#define ADC_STEREO_MIX 7 +#define ADC_COUNT 8 /* Must match the size of m_ADCVolume */ + +/* + * List of Lines + */ +#define LINE_OUT_1L 0x00000000 +#define LINE_OUT_1R 0x00000001 +#define LINE_OUT_2L 0x00000002 +#define LINE_OUT_2R 0x00000003 +#define LINE_OUT_3L 0x00000004 +#define LINE_OUT_3R 0x00000005 +#define LINE_OUT_4L 0x00000006 +#define LINE_OUT_4R 0x00000007 +#define LINE_MASTER 0x00000008 +#define LINE_MASTER_ 0x00000009 +#define LINE_GAIN_L 0x0000000a +#define LINE_GAIN_R 0x0000000b +#define LINE_S_NUM 0x0000000c /* 12 - Should match devc->m_fDACMute size */ + +/* + * List of Stereo Lines + */ +#define OUT_12 0x00000000 +#define OUT_34 0x00000001 +#define OUT_56 0x00000002 +#define OUT_78 0x00000003 +#define OUT_MASTER 0x00000004 +#define IN_12 0x00000005 +#define NUM_LINES 0x00000006 + +/* + * SPDIF Out Source + */ +enum +{ + DIGOUT_DIG_IN = 0, + DIGOUT_ANA_IN, + DIGOUT_WAVE +}; +/* + * Digital IN Source + */ +enum +{ + DIGIN_OPTICAL = 0, + DIGIN_COAX, + DIGIN_CD_IN +}; +/* + * Out-0 Source + */ +enum +{ + SRC_DMA1 = 0, + SRC_PSDIN0, + SRC_SPDIN +}; + +/* + * Line IN Source (Aureon 7.1 Universe only) + */ +#define SRC_AUX 0 +#define SRC_WTL 1 +#define SRC_LINE_REAR 2 +#define SRC_GROUND 3 + +/* + * Clock Source + */ +enum +{ + ICE_INTERNAL_CLOCK = 0, + ICE_SPDIF_CLOCK +}; +/* + * Function of Frontjack + */ +enum +{ + FRONT_JACK_LINEIN = 0, + FRONT_JACK_HEADPHONE +}; + +enum MUX_TP_PIN +{ + CD_IN_MUX_TP_PIN = 1, + LINE_IN_MUX_TP_PIN, + AUX_IN_MUX_TP_PIN, + PHONO_IN_MUX_PIN, + LINE2_IN_MUX_PIN, + MIC_IN_MUX_TP_PIN, + DIG_IN_MUX_TP_PIN, + STEREO_MIX_MUX_TP_PIN +}; + +#define AUREON_REMOTE_CNTRL 0x32 +/* #define AUREON_REMOTE_ID 0x0016 */ +#define AUREON_REMOTE_ID 0x6B86 +#define AUREON_PCA_BASEBOARD 0x40 + +#define MT_LOOPBK_CONTROL 0x02C +#define MT_LOOPBK_CONTROL_DAC_REG 0x030 +#define MT_LOOPBK_CONTROL_SPDIF_OUT_REG 0x032 + +#define WIDTH_BYTE 1 +#define WIDTH_WORD 2 +#define WIDTH_DWORD 4 + +#define BITS_ON 0x10 +#define BITS_OFF 0x00 +#define TOGGLE_ON 0x20 + +#define CRITSEC_ON 0x00000001 + +#define WIDTH_MASK 0x0f + +#include "spdif.h" +#include "envy24ht.h" + +/*! \fn ======================================================================= + Function : WritePort +------------------------------------------------------------------------------- + Description : + Parameters : dwRegType -> + : dwIndex -> + : dwValue -> + : dwWidth -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void WritePort + (envy24ht_devc * devc, + IN ULONG dwRegType, IN ULONG dwIndex, IN ULONG dwValue, IN ULONG dwWidth) +{ + oss_native_word pPort = devc->ccs_base; + + switch (dwRegType) + { + case REG_CCS: + pPort = devc->ccs_base; + break; + + case REG_MT: + pPort = devc->mt_base; + break; + } + + /* registers are addressible in byte, word or dword */ + switch (dwWidth) + { + case WIDTH_BYTE: + OUTB (devc->osdev, dwValue, pPort + dwIndex); + break; + case WIDTH_WORD: + OUTW (devc->osdev, dwValue, pPort + dwIndex); + + break; + case WIDTH_DWORD: + OUTL (devc->osdev, dwValue, pPort + dwIndex); + break; + } +} + + +/*! \fn ======================================================================= + Function : ReadPort +------------------------------------------------------------------------------- + Description : + Returns : -> + Parameters : IN ULONG dwWidth -> + IN ULONG dwWidth +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static ULONG ReadPort + (envy24ht_devc * devc, + IN ULONG dwRegType, IN ULONG dwIndex, IN ULONG dwWidth) +{ + oss_native_word pPort = devc->ccs_base; + ULONG dwData = 0; + + switch (dwRegType) + { + case REG_CCS: + pPort = devc->ccs_base; + break; + + case REG_MT: + pPort = devc->mt_base; + break; + } + + /* all other registers are addressible in byte, word or dword */ + switch (dwWidth) + { + case WIDTH_BYTE: + dwData = INB (devc->osdev, pPort + dwIndex); + break; + case WIDTH_WORD: + dwData = INW (devc->osdev, pPort + dwIndex); + break; + case WIDTH_DWORD: + dwData = INL (devc->osdev, pPort + dwIndex); + break; + } + + + return dwData; +} + +/*! \fn ======================================================================= + Function : ReadModifyWritePort +------------------------------------------------------------------------------- + Description : + Returns : VOID -> + Parameters : wRegType -> + : dwMask -> + : dwControl -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void ReadModifyWritePort + (envy24ht_devc * devc, + IN ULONG dwRegType, IN ULONG dwIndex, IN ULONG dwMask, IN ULONG dwControl + /* dwControl: */ + /* bit[3:0] : data width, 1 => byte, 2 => word, 4 => dword */ + /* bit[4] : 1 => turn on bit(s); 0 => turn off bit(s) */ + /* bit[5] : 1 => toggle bit(s) */ + /* bit[6] : 1 => turn on Hw access critical section */ + ) +{ + ULONG dwValue; + ULONG dwWidth; + oss_native_word flags; + + dwWidth = dwControl & WIDTH_MASK; + + if (dwControl & CRITSEC_ON) + { + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + } + + dwValue = ReadPort (devc, dwRegType, dwIndex, dwWidth); + + /* see whether we should turn on or off the bit(s) */ + if (dwControl & BITS_ON) + dwValue |= dwMask; + else + dwValue &= ~dwMask; + + WritePort (devc, dwRegType, dwIndex, dwValue, dwWidth); + + /* see if we need to toggle the bit(s) */ + if (dwControl & TOGGLE_ON) + { + dwValue ^= dwMask; + WritePort (devc, dwRegType, dwIndex, dwValue, dwWidth); + } + + if (dwControl & CRITSEC_ON) + { + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + } + +} + +/*! \fn ======================================================================= + Function : WriteGPIO +------------------------------------------------------------------------------- + Description : Writes masked Data to GPIO Port + Parameters : IN ULONG Data -> + : IN ULONG Mask -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +WriteGPIO (envy24ht_devc * devc, IN ULONG Data, IN ULONG Mask) +{ + USHORT MaskL; + UCHAR MaskH; + USHORT DataL; + UCHAR DataH; + + /* Do the masking. */ + MaskL = (USHORT) Mask; + MaskH = (UCHAR) (Mask >> 16); + DataL = (USHORT) Data; + DataH = (UCHAR) (Data >> 16); + devc->gpio_shadow_L &= ~MaskL; + devc->gpio_shadow_H &= ~MaskH; + devc->gpio_shadow_L |= (MaskL & DataL); + devc->gpio_shadow_H |= (MaskH & DataH); + + /* Write Data */ + WritePort (devc, REG_CCS, WKN_GPIO_DATA0, devc->gpio_shadow_L, WIDTH_WORD); + WritePort (devc, REG_CCS, WKN_GPIO_DATA2, devc->gpio_shadow_H, WIDTH_BYTE); +} + +/*! \fn ======================================================================= + Function : SetSDADir +------------------------------------------------------------------------------- + Description : + Returns : void -> + Parameters : IN BOOLEAN fOut -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +SetSDADir (envy24ht_devc * devc, IN BOOLEAN fOut) +{ + if (fOut) + { + /* Set GPIO 18 direction to output */ + WritePort (devc, REG_CCS, WKN_GPIO_DIR2, 0xDF, WIDTH_BYTE); + /* Turn IIC logic to SDA write */ + WriteGPIO (devc, GPIO_SDA_TX_MASK, GPIO_SDA_TX_MASK); + } + else + { + WriteGPIO (devc, 0, GPIO_SDA_TX_MASK); + /* Direction "Read" for GPIO 18 (SDA) */ + WritePort (devc, REG_CCS, WKN_GPIO_DIR2, 0xDB, WIDTH_BYTE); + } +} + +/*! \fn ======================================================================= + Function : SetSDA +------------------------------------------------------------------------------- + Description : + Parameters : BOOLEAN fSet +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +SetSDA (envy24ht_devc * devc, BOOLEAN fSet) +{ + /* Set GPIO 18 direction to output */ + SetSDADir (devc, 1); + /* Write SDA */ + WriteGPIO (devc, fSet ? GPIO_SDA_MASK : 0, GPIO_SDA_MASK); +} + +/*! \fn ======================================================================= + Function : GetSDA +------------------------------------------------------------------------------- + Description : + Returns : +UCHAR -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static UCHAR +GetSDA (envy24ht_devc * devc) +{ + UCHAR ulData; + + /* Turn IIC logic to SDA read */ + SetSDADir (devc, 0); + ulData = + ((ReadPort (devc, REG_CCS, WKN_GPIO_DATA2, WIDTH_BYTE) & 0x04) == 0x04); + return ulData; +} + +/*! \fn ======================================================================= + Function : SetIIC +------------------------------------------------------------------------------- + Description : + Parameters : IN BOOLEAN fSDA -> + : IN BOOLEAN fCLK -> + : IN ULONG ulSleep -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void SetIIC + (envy24ht_devc * devc, IN BOOLEAN fSDA, IN BOOLEAN fCLK, IN ULONG ulSleep) +{ + SetSDA (devc, fSDA); + if (fCLK) + WriteGPIO (devc, GPIO_SCL_MASK, GPIO_SCL_MASK); + else + WriteGPIO (devc, 0, GPIO_SCL_MASK); + if (ulSleep) + oss_udelay (ulSleep); +} + +/*! \fn ======================================================================= + Function : IICStart +------------------------------------------------------------------------------- + Description : + Parameters : ULONG ulSleep -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +IICStart (envy24ht_devc * devc, ULONG ulSleep) +{ + /* falling edge of SDA while SCL is HIGH */ + SetIIC (devc, 1, 1, ulSleep); + SetIIC (devc, 0, 1, ulSleep); +} + +/*! \fn ======================================================================= + Function : IICStop +------------------------------------------------------------------------------- + Description : + Parameters : ULONG ulSleep -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +IICStop (envy24ht_devc * devc, ULONG ulSleep) +{ + /* rising edge of SDA while SCL is HIGH */ + SetIIC (devc, 0, 1, ulSleep); + SetIIC (devc, 1, 1, ulSleep); + /* Reset Lines (No IIC requirement, but for prevent conflicts with SPI) */ + /*SetIIC(0,0,ulSleep); */ +} + +/*! \fn ======================================================================= + Function : IICSendByte +------------------------------------------------------------------------------- + Description : + Returns : UCHAR -> + Parameters : CHAR bByte -> + : ULONG ulSleep -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static UCHAR IICSendByte + (envy24ht_devc * devc, IN UCHAR bByte, IN ULONG ulSleep) +{ + UCHAR bDataBit, bAck; + int i; + for (i = 7; i >= 0; i--) /* send byte (MSB first) */ + { + bDataBit = (bByte >> i) & 0x01; + + SetIIC (devc, bDataBit, 0, ulSleep); + SetIIC (devc, bDataBit, 1, ulSleep); + SetIIC (devc, bDataBit, 0, ulSleep); + } /* end for i */ + + SetIIC (devc, 1, 0, ulSleep); + + /* This is neccesary for PLC */ + SetSDADir (devc, 0); + /* Get acknowledge */ + SetIIC (devc, 1, 1, ulSleep); + bAck = GetSDA (devc); + /*if (fAddress) */ + /* SetIIC(devc, 1,0,ulSleep); */ + /* else */ + /* this is a start condition but never mind */ + SetIIC (devc, 0, 0, ulSleep); + return (!bAck); /* bAck = 0 --> success */ +} + +#if 0 +/*! \fn ======================================================================= + Function : IICReceiveByte +------------------------------------------------------------------------------- + Description : + Returns : UCHAR -> + Parameters : CHAR fLastByte -> + : ULONG ulSleep -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static UCHAR IICReceiveByte + (envy24ht_devc * devc, UCHAR fLastByte, ULONG ulSleep) +{ + UCHAR bRead = 0; + int i; + + for (i = 7; i >= 0; i--) + { + SetIIC (devc, 1, 0, ulSleep); + SetIIC (devc, 1, 1, ulSleep); + bRead <<= 1; + bRead += GetSDA (devc); + } + + /* -> no acknowledge for last received byte */ + SetIIC (devc, fLastByte, 0, ulSleep); /* SDA = HIGH for last byte */ + SetIIC (devc, fLastByte, 1, ulSleep); + SetIIC (devc, fLastByte, 0, ulSleep); /* clock -> LOW */ + + return bRead; +} +#endif + +/*! \fn ======================================================================= + Function : IICWriteBuffer +------------------------------------------------------------------------------- + Description : + Returns : UCHAR -> + Parameters : CHAR bIicAddress -> + : PUCHAR pbByte -> + : USHORT nNoBytes -> + : ULONG ulSleep -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static UCHAR IICWriteBuffer + (envy24ht_devc * devc, + UCHAR bIicAddress, PUCHAR pbByte, USHORT nNoBytes, ULONG ulSleep) +{ + IICStart (devc, ulSleep); + + /* send IIC address and data byte */ + if (!IICSendByte (devc, bIicAddress, ulSleep)) + goto FAILED; + /* send buffer */ + do + { + if (!IICSendByte (devc, *pbByte, ulSleep)) + goto FAILED; /* got no acknowledge */ + pbByte++; + nNoBytes--; + } + while (nNoBytes); + + IICStop (devc, ulSleep); + return 1; + +FAILED: + IICStop (devc, ulSleep); + return 0; +} + +/*! \fn ======================================================================= + Function : WriteCS8415 +------------------------------------------------------------------------------- + Description : + Parameters : IN UCHAR Register -> + : IN UCHAR Value -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void WriteCS8415 + (envy24ht_devc * devc, IN UCHAR Register, IN UCHAR Value) +{ + ULONG i; + BOOLEAN fData; + ULONG ulCmd; + ULONG ulCount; + + /* m_pAdapter->HwEnter(); *//* TODO */ + + /* Clock low to prevent IIC Startcondition */ + WriteGPIO (devc, 0, GPIO_SCL_MASK); + + SetSDA (devc, 0); + /* Chip select (CS low) */ + WriteGPIO (devc, 0, GPIO_CS8415_CS_MASK); + + /* format buffer */ + ulCmd = 0x200000 + /* chip address + R/W */ + (((ULONG) Register) << 8) + /* register address */ + Value; /* Value */ + ulCmd <<= 8; + ulCount = 24; /* (AddressOnly) ? 16:24; */ + for (i = 0; i < ulCount; i++) + { + fData = (ulCmd & 0x80000000) ? 1 : 0; + /* CCLK -> low */ + WriteGPIO (devc, 0, GPIO_SCL_MASK); + oss_udelay (3); + /* CDTI -> Set data */ + SetSDA (devc, fData); + oss_udelay (3); + /* CCLK -> high */ + WriteGPIO (devc, GPIO_SCL_MASK, GPIO_SCL_MASK); + ulCmd <<= 1; + } + WriteGPIO (devc, 0, GPIO_SCL_MASK); /* CCLK -> low */ + + /* Chip deselect (CS high) */ + WriteGPIO (devc, GPIO_CS8415_CS_MASK, GPIO_CS8415_CS_MASK); + /* m_pAdapter->HwLeave(); *//* TODO */ +} + +#if 0 +/*! \fn ======================================================================= + Function : ReadCS8415 +------------------------------------------------------------------------------- + Description : + Returns : UCHAR -> + Parameters : IN UCHAR Register -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ + /*ARGSUSED*/ static UCHAR +ReadCS8415 (envy24ht_devc * devc, IN UCHAR Register) +{ + ULONG i; + BOOLEAN fData; + UCHAR cValue = 0; + USHORT wCmd; + + /* m_pAdapter->HwEnter(); *//* TODO */ + + /* Clock low to prevent IIC Startcondition */ + WriteGPIO (devc, 0, GPIO_SCL_MASK); + + SetSDA (devc, 0); + + /* Chip select (CS low) */ + WriteGPIO (devc, 0, GPIO_CS8415_CS_MASK); + /* Set GPIO21 to read direction */ + WritePort (devc, REG_CCS, WKN_GPIO_DIR2, 0xDF, WIDTH_BYTE); + + wCmd = 0x2100; /* + *//* chip address + R/W */ + /*(((USHORT)Register) << 8); *//* register address */ + for (i = 0; i < 16; i++) + { + fData = (wCmd & 0x8000) ? 1 : 0; + /* CCLK -> low */ + WriteGPIO (devc, 0, GPIO_SCL_MASK); + /* CDTI -> Set data */ + SetSDA (devc, fData); + oss_udelay (3); + /* CCLK -> high */ + WriteGPIO (devc, GPIO_SCL_MASK, GPIO_SCL_MASK); + wCmd <<= 1; + if (i > 7) + { + /* Read CDTO */ + cValue <<= 1; + cValue += + ((ReadPort (devc, REG_CCS, WKN_GPIO_DATA2, WIDTH_BYTE) & 0x20) == + 0x20); + } + } + + WriteGPIO (devc, 0, GPIO_SCL_MASK); /* CCLK -> low */ + + /* Chip deselect (CS high) */ + WriteGPIO (devc, GPIO_CS8415_CS_MASK, GPIO_CS8415_CS_MASK); + + /* m_pAdapter->HwLeave(); *//* TODO */ + return cValue; +} +#endif + +/* + * Definition of PCA I/Os + */ +typedef union tagPCA_CFG +{ + unsigned char cReg; + struct + { + unsigned char P00_SourceSel:2; /* LineIN Selector */ + /* 00 -> Aux */ + /* 01 -> Wavetable */ + /* 11 -> LineIN Rear */ + unsigned char P02_DigSel:1; /* DigitalIN Selector */ + /* 0 -> Coax */ + /* 1 -> Optical */ + + unsigned char P03_LineLED:1; /* LineLED */ + /* 0 -> Off */ + /* 1 -> On */ + unsigned char P04_unused:4; /* unused */ + } Bits; +} PCA_CFG; + +/*! \fn ======================================================================= + Function : WritePCA +------------------------------------------------------------------------------- + Description : + Returns : NTSTATUS -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +WritePCA (envy24ht_devc * devc) +{ + PCA_CFG tCFG; + BYTE bByte[2]; + + tCFG.cReg = 0; + + switch (devc->m_LineInSource) + { + case SRC_AUX: + tCFG.Bits.P00_SourceSel = 0; + break; + case SRC_WTL: + tCFG.Bits.P00_SourceSel = 1; + break; + case SRC_LINE_REAR: + tCFG.Bits.P00_SourceSel = 2; + break; + case SRC_GROUND: + tCFG.Bits.P00_SourceSel = 3; + break; + + } + + /* Switch LineLED when Line is selected for record */ + if (devc->m_ADCIndex == ADC_LINE) + tCFG.Bits.P03_LineLED = 1; + if (devc->m_DigInSource == DIGIN_OPTICAL) + tCFG.Bits.P02_DigSel = 1; + /* Set all I/Os to Output */ + bByte[0] = 3; + bByte[1] = 0; + IICWriteBuffer (devc, AUREON_PCA_BASEBOARD, bByte, 2, 30); + /* Write config */ + bByte[0] = 0x01; + bByte[1] = tCFG.cReg; + IICWriteBuffer (devc, AUREON_PCA_BASEBOARD, bByte, 2, 30); +} + +/*! \fn ======================================================================= + Function : SetDigInSource +------------------------------------------------------------------------------- + Description : + Returns : void -> + Parameters : IN ULONG Source -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +SetDigInSource (envy24ht_devc * devc, IN ULONG Source) +{ + switch (Source) + { + case DIGIN_OPTICAL: + case DIGIN_COAX: + WriteCS8415 (devc, CS_CONTROL_2, 0x01); + break; + case DIGIN_CD_IN: + WriteCS8415 (devc, CS_CONTROL_2, 0x00); + break; + } + devc->m_DigInSource = Source; + WritePCA (devc); +} + +/*! \fn ======================================================================= + Function : SetOUT0Source +------------------------------------------------------------------------------- + Description : + Returns : void -> + Parameters : IN ULONG Source -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +SetOUT0Source (envy24ht_devc * devc, IN ULONG Source) +{ + ULONG ulShiftL, ulShiftR; + ULONG aulShiftL[] = { 0, 8, 11, 14, 17 }; + ULONG aulShiftR[] = { 3, 20, 23, 26, 29 }; + ULONG ulSourceL, ulSourceR; + int i; + + /* No DigMonitor when Clock is internal */ + if ((devc->m_ClockSource == ICE_INTERNAL_CLOCK) && (Source == SRC_SPDIN)) + return; + for (i = 0; i < 5; i++) + { + ulShiftL = aulShiftL[i]; + ulShiftR = aulShiftR[i]; + switch (Source) + { + case SRC_DMA1: + ulSourceL = SRC_PDMA; + ulSourceR = SRC_PDMA; + break; + case SRC_PSDIN0: + ulSourceL = SRC_PSDIN0_L; + ulSourceR = SRC_PSDIN0_R; + break; + case SRC_SPDIN: + ulSourceL = SRC_SPDIN_L; + ulSourceR = SRC_SPDIN_R; + break; + default: /* Do nothing */ + return; + } + /* First reset all relevant bits */ + ReadModifyWritePort (devc, REG_MT, MT_LOOPBK_CONTROL, + SRC_MASK << ulShiftL, WIDTH_DWORD | BITS_OFF); + ReadModifyWritePort (devc, REG_MT, MT_LOOPBK_CONTROL, + SRC_MASK << ulShiftR, WIDTH_DWORD | BITS_OFF); + /* ..and set routing mask */ + ReadModifyWritePort (devc, REG_MT, MT_LOOPBK_CONTROL, + ulSourceL << ulShiftL, WIDTH_DWORD | BITS_ON); + ReadModifyWritePort (devc, REG_MT, MT_LOOPBK_CONTROL, + ulSourceR << ulShiftR, WIDTH_DWORD | BITS_ON); + } + devc->m_Out0Source = Source; +} +static void SetADCMux (envy24ht_devc * devc, IN ULONG Value); + +/*! \fn ======================================================================= + Function : SetLineSource +------------------------------------------------------------------------------- + Description : + Parameters : IN ULONG Source -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +SetLineSource (envy24ht_devc * devc, IN ULONG Source) +{ + switch (Source) + { + case SRC_AUX: + devc->m_AuxMux = 0x00; + break; + case SRC_WTL: + devc->m_AuxMux = 0x44; + break; + case SRC_LINE_REAR: + devc->m_AuxMux = 0x66; + break; + + } + /* Update ADCMux */ + SetADCMux (devc, devc->m_ADCMux); + devc->m_LineInSource = Source; + WritePCA (devc); +} + +/*! \fn ======================================================================= + Function : SetClockSource +------------------------------------------------------------------------------- + Description : + Parameters : IN ULONG ClockSource -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +SetClockSource (envy24ht_devc * devc, IN ULONG ClockSource) +{ + + if (ClockSource == ICE_INTERNAL_CLOCK) + { + ReadModifyWritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, 0x10, + WIDTH_BYTE | BITS_OFF); + /* Disable DigMonitor to avoid noisy output */ + if (devc->m_Out0Source == SRC_SPDIN) + SetOUT0Source (devc, SRC_DMA1); + } + else + { + ReadModifyWritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, 0x10, + WIDTH_BYTE | BITS_ON); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + } + devc->m_ClockSource = ClockSource; +} + +/*! \fn ======================================================================= + Function : ResetGPIO +------------------------------------------------------------------------------- + Description : + Returns : void -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +ResetGPIO (envy24ht_devc * devc) +{ + + /* Enable all lower GPIOs */ + WritePort (devc, REG_CCS, WKN_GPIO_WRT_MASK0, 0x0, WIDTH_WORD); + /* Enable all upper GPIOs */ + WritePort (devc, REG_CCS, WKN_GPIO_WRT_MASK2, 0x0, WIDTH_BYTE); + /* Set GPIO direction */ + if (devc->subvendor == SSID_AUREON_UNIVERSE) + { + /* -> all output except GPIO_CS8415_CDTO_MASK */ + /* and GPIO_REMOTE_MASK */ + WritePort (devc, REG_CCS, WKN_GPIO_DIR0, + ~(GPIO_CS8415_CDTO_MASK | GPIO_REMOTE_MASK), WIDTH_DWORD); + } + else + { + /* All output except GPIO_CS8415_CDTO_MASK */ + WritePort (devc, REG_CCS, WKN_GPIO_DIR0, ~GPIO_CS8415_CDTO_MASK, + WIDTH_DWORD); + } + + oss_udelay (100); + + /*---------------------------------------------------------------------------- + Reset AC97 Interface + -----------------------------------------------------------------------------*/ + + WriteGPIO (devc, GPIO_AC97_RESET_MASK, GPIO_AC97_RESET_MASK); + oss_udelay (3); + WriteGPIO (devc, 0, GPIO_AC97_RESET_MASK); + oss_udelay (3); + WriteGPIO (devc, GPIO_AC97_RESET_MASK, GPIO_AC97_RESET_MASK); + + /*---------------------------------------------------------------------------- + Enable Remote-Control Interrupts + -----------------------------------------------------------------------------*/ + WriteGPIO (devc, GPIO_INT_REST_MASK, GPIO_INT_REST_MASK); + + /* Select optical input */ + /* WriteGPIO(devc, GPIO_DIGITAL_SEL, 0); */ +} + +/*! \fn ======================================================================= + Function : SetSPDIFConfig +------------------------------------------------------------------------------- + Description : + Returns : void -> + Parameters : IN ULONG Config -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +SetSPDIFConfig (envy24ht_devc * devc, IN ULONG Config) +{ + ReadModifyWritePort (devc, REG_CCS, WKN_SPDIF_CONFIG, 0x80, + WIDTH_BYTE | BITS_OFF); + if (Config & SPDIF_ENABLE_MASK) + { + devc->m_f1724SPDIF = 1; + } + else + { + devc->m_f1724SPDIF = 0; + } + /* Reset SPDIF Config */ + ReadModifyWritePort (devc, REG_MT, MT_SPDIF_REG, 0x000F, + WIDTH_WORD | BITS_OFF); + /* Set new Config */ + ReadModifyWritePort (devc, REG_MT, MT_SPDIF_REG, Config & 0xFFFF, + WIDTH_WORD | BITS_ON); + devc->m_SPDIFConfig = Config; + if (devc->m_f1724SPDIF) + ReadModifyWritePort (devc, REG_CCS, WKN_SPDIF_CONFIG, 0x80, + WIDTH_BYTE | BITS_ON); + /* Force saving of registers */ + /* devc->m_pAdapter->SetDirty(); *//* TODO */ +} + +/*! \fn ======================================================================= + Function : SetFrontjack +------------------------------------------------------------------------------- + Description : + Parameters : IN ULONG Frontjack -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +SetFrontjack (envy24ht_devc * devc, IN ULONG Frontjack) +{ + + switch (Frontjack) + { + case FRONT_JACK_LINEIN: + WriteGPIO (devc, 0, GPIO_HEADPHONE_MASK); + break; + case FRONT_JACK_HEADPHONE: + WriteGPIO (devc, GPIO_HEADPHONE_MASK, GPIO_HEADPHONE_MASK); + break; + } + devc->m_Frontjack = Frontjack; + /* Force saving of registers */ + /* devc->m_pAdapter->SetDirty(); *//* TODO */ + +} + +/*! \fn ======================================================================= + Function : Init1724 +------------------------------------------------------------------------------- + Description : + Returns : void -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +Init1724 (envy24ht_devc * devc) +{ + /* System Config: 4 DACs, one ADC & SPDIF, MPU enabled, 24,576Mhz crystal */ + /* WritePort(REG_CCS, WKN_SYS_CONFIG, 0x2B, WIDTH_BYTE); */ + /* CL_NOTE: New Setings */ + /* System Config: 4 DACs, one ADC & SPDIF, MPU disabled, 24,576Mhz crystal */ + WritePort (devc, REG_CCS, WKN_SYS_CONFIG, 0x0B, WIDTH_BYTE); + + /* Config I2S Interface */ + WritePort (devc, REG_CCS, WKN_ACLINK_CONFIG, 0x80, WIDTH_BYTE); + WritePort (devc, REG_CCS, WKN_I2S_CONFIG, 0xF9, WIDTH_BYTE); + WritePort (devc, REG_CCS, WKN_SPDIF_CONFIG, 0x83, WIDTH_BYTE); + + /* Config Interrupt behaviour */ + WritePort (devc, REG_MT, MT_PLAY_REC_UNDOVR, 0, WIDTH_BYTE); + WritePort (devc, REG_MT, MT_INTR_STATUS_REG, 0, WIDTH_BYTE); + WritePort (devc, REG_MT, MT_INTR_MASK_REG, 0xFF, WIDTH_BYTE); + WritePort (devc, REG_CCS, WKN_INT_STAT_REG, 0, WIDTH_BYTE); + WritePort (devc, REG_CCS, WKN_INT_MASK_REG, 0xFE, WIDTH_BYTE); + + SetSPDIFConfig (devc, 0); + /* SetSDPIFSource(devc, DIGOUT_WAVE); */ + SetDigInSource (devc, DIGIN_OPTICAL); + SetFrontjack (devc, FRONT_JACK_LINEIN); +} + +/*! \fn ======================================================================= + Function : WriteWM8770 +------------------------------------------------------------------------------- + Description : + Parameters : IN UCHAR Register -> + : IN UCHAR Value -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +void +WriteWM8770 (envy24ht_devc * devc, IN UCHAR Register, IN USHORT Value) +{ + int i; + BOOLEAN fData; + USHORT wCmd; +/* KIRQL OldIrql; */ + + /* m_pAdapter->HwEnter(); */ + + /* KeAcquireSpinLock (&m_SPILock,&OldIrql); */ + + + /* Clock low to prevent IIC Startcondition */ + WriteGPIO (devc, 0, GPIO_SCL_MASK); + + SetSDA (devc, 0); + + /* Chip select (CS low) */ + WriteGPIO (devc, 0, GPIO_WM8770_CS_MASK); + + /* format buffer */ + wCmd = (((USHORT) Register) << 9) + Value; + + + for (i = 0; i < 16; i++) + { + fData = (wCmd & 0x8000) ? 1 : 0; + /* CCLK -> low */ + WriteGPIO (devc, 0, GPIO_SCL_MASK); + oss_udelay (3); + /* CDTI -> Set data */ + SetSDA (devc, fData); + oss_udelay (3); + /* CCLK -> high */ + WriteGPIO (devc, GPIO_SCL_MASK, GPIO_SCL_MASK); + wCmd <<= 1; + } + WriteGPIO (devc, 0, GPIO_SCL_MASK); /* CCLK -> low */ + + /* Chip deselect */ + WriteGPIO (devc, GPIO_WM8770_CS_MASK, GPIO_WM8770_CS_MASK); +} + +/*! \fn ======================================================================= + Function : SetVolReg +------------------------------------------------------------------------------- + Description : + Parameters : IN ULONG LineIdx -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +SetVolReg (envy24ht_devc * devc, IN ULONG LineIdx) +{ + int iVol, ChnlVol; + + ChnlVol = devc->m_DACVolume[LineIdx]; + + /* See if we want to mute anything */ + if (devc->m_fDACMute[LineIdx] || devc->m_fDACMute[LINE_MASTER]) + { + WriteWM8770 (devc, gWMRegister[LineIdx], 0x100); + return; + } + + /* Since master volume is virtualized, we add the attenuations for both */ + /* master volume and channel volume to obtain the overall attenuation */ + + /* Get total attenuation */ + iVol = ChnlVol + devc->m_DACVolume[LINE_MASTER] - MAX_VOLUME; + /* Check against bounds */ + iVol = (iVol < MAX_VOLUME) ? iVol : MAX_VOLUME; + if (iVol < MIN_VOLUME) + iVol = MIN_VOLUME; + WriteWM8770 (devc, gWMRegister[LineIdx], iVol | 0x180); +} + + +/*! \fn ======================================================================= + Function : SetMute +------------------------------------------------------------------------------- + Description : + Returns : VOID -> + Parameters : IN ULONG ChannelID -> + : IN BOOLEAN Mute -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void SetDACMute + (envy24ht_devc * devc, IN ULONG ChannelID, IN BOOLEAN Mute) +{ + ULONG LineIndex; + + /* Convert ChannelID to line index */ + /* If it is for left channel, we will need to add one */ + LineIndex = ChannelID >> 15; + + /* See if this is for master volume */ + if (LineIndex == LINE_MASTER) + { + /* if current setting is not the same as previous setting */ + if (devc->m_fDACMute[LINE_MASTER] != Mute) + { + int i; + + devc->m_fDACMute[LINE_MASTER] = Mute; + + /* Need to do it for every single line (excluding Master and Gain) */ + for (i = 0; i <= LINE_OUT_4R; i++) + { + SetVolReg (devc, i); + } + return; + } + } + + /* See if this is for left channel */ + if (ChannelID & CH_LEFT) + { + /* if current setting is not the same as previous setting */ + if (devc->m_fDACMute[LineIndex] != Mute) + { + devc->m_fDACMute[LineIndex] = Mute; + if (LineIndex == LINE_GAIN_L) + { + WriteWM8770 (devc, gWMRegister[LINE_GAIN_L], + devc-> + m_DACVolume[LINE_GAIN_L] | ((Mute) ? 0x20 : 0x00)); + } + else + { + SetVolReg (devc, LineIndex); + } + } + } + + /* See if this is for right channel */ + if (ChannelID & CH_RIGHT) + { + LineIndex++; + /* if current setting is not the same as previous setting */ + if (devc->m_fDACMute[LineIndex] != Mute) + { + devc->m_fDACMute[LineIndex] = Mute; + if (LineIndex == LINE_GAIN_R) + { + WriteWM8770 (devc, gWMRegister[LINE_GAIN_R], + devc-> + m_DACVolume[LINE_GAIN_R] | ((Mute) ? 0x20 : 0x00)); + } + else + { + SetVolReg (devc, LineIndex); + } + } + } + /* m_pAdapter->SetDirty(); *//* TODO */ +} + + + +/*============================================================================= + Function : SetVolume +------------------------------------------------------------------------------- + Description : + Returns : VOID -> + Parameters : IN ICE_HW_PARAM* HwParm -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void SetDACVolume + (envy24ht_devc * devc, IN ULONG ChannelID, IN UCHAR Volume) +{ + ULONG LineIndex; + WORD LeftRight; + BOOLEAN VolChnged = 0; + + /* Convert ChannelID to line index */ + LineIndex = ChannelID >> 15; + /* Get the left/right side */ + LeftRight = (WORD) (ChannelID & 0xffff); + + /* Check if left volume is changed */ + if (LeftRight & CH_LEFT) + { + if (devc->m_DACVolume[LineIndex] != Volume) + { + devc->m_DACVolume[LineIndex] = Volume; + VolChnged = 1; + } + } + + /* Check if right volume is changed */ + if (LeftRight & CH_RIGHT) + { + if (devc->m_DACVolume[LineIndex + 1] != Volume) + { + devc->m_DACVolume[LineIndex + 1] = Volume; + VolChnged = 1; + } + } + + /* If any volume is changed, need to touch hardware */ + if (VolChnged) + { + /* check if this is for input gain */ + if ((ChannelID >> 16) == IN_12) + { + USHORT WMValue = (USHORT) Volume; +#ifdef NULL_DB + WMValue = 0x0C; +#endif + if (LeftRight & CH_LEFT) + WriteWM8770 (devc, gWMRegister[LineIndex], WMValue); + if (LeftRight & CH_RIGHT) + WriteWM8770 (devc, gWMRegister[LineIndex + 1], WMValue); + } + else + /* Yap, now check if this is for master volume */ + if ((ChannelID >> 16) == OUT_MASTER) + { + int i; + /* Need to do it for every single line (excluding Master and Gain) */ + for (i = 0; i <= LINE_OUT_4R; i++) + { + SetVolReg (devc, i); + } + } + else + { + if (LeftRight & CH_LEFT) + SetVolReg (devc, LineIndex); + if (LeftRight & CH_RIGHT) + SetVolReg (devc, LineIndex + 1); + } + } + /* m_pAdapter->SetDirty(); TODO */ +} + + +/*! \fn ======================================================================= + Function : GetVolume +------------------------------------------------------------------------------- + Description : + Returns : UCHAR -> + Parameters : IN ULONG ChannelID -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static UCHAR +GetDACVolume (envy24ht_devc * devc, IN ULONG ChannelID) +{ + UCHAR Value; + USHORT LeftRight; + ULONG LineIndex; + + /* Convert ChannelID to line index */ + LineIndex = ChannelID >> 15; + /* Get the left/right side */ + LeftRight = (USHORT) (ChannelID & 0xffff); + + if (LeftRight == CH_LEFT) + { + Value = devc->m_DACVolume[LineIndex]; + } + else + { + Value = devc->m_DACVolume[LineIndex + 1]; + } + return Value; +} + + +#if 0 +/*! \fn ======================================================================= + Function : GetMute +------------------------------------------------------------------------------- + Description : + Returns : BOOLEAN -> + Parameters : IN ULONG ChannelID -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static BOOLEAN +GetDACMute (envy24ht_devc * devc, IN ULONG ChannelID) +{ + BOOLEAN Value; + USHORT LeftRight; + ULONG LineIndex; + + /* Convert ChannelID to line index */ + LineIndex = ChannelID >> 15; + /* Get the left/right side */ + LeftRight = (USHORT) (ChannelID & 0xffff); + + if (LeftRight == CH_LEFT) + { + Value = devc->m_fDACMute[LineIndex]; + } + else + { + Value = devc->m_fDACMute[LineIndex + 1]; + } + return Value; +} +#endif + +/*! \fn ======================================================================= + Function : SetADCGain +------------------------------------------------------------------------------- + Description : + Parameters : IN ULONG Index -> + : IN ULONG Value -> + : IN ULONG Channel -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void SetADCGain + (envy24ht_devc * devc, IN ULONG Index, IN USHORT Value, IN ULONG Channel) +{ + UCHAR WMReg = 0x19; + USHORT WMValue; + + /* Set only selected Line */ + if (Index != devc->m_ADCIndex) + return; + + switch (Channel) + { + case CH_LEFT: + devc->m_ADCVolume[Index] = + (Value << 8) | (devc->m_ADCVolume[Index] & 0x00FF); + break; + case CH_BOTH: + devc->m_ADCVolume[Index] = (Value << 8) | Value; + break; + case CH_NOP: + /* Hold Value */ + break; + case CH_RIGHT: + default: + devc->m_ADCVolume[Index] = Value | (devc->m_ADCVolume[Index] & 0xFF00); + + } + WMValue = + ((devc->m_ADCVolume[Index] & 0x00FF) < + 0x1F) ? (devc->m_ADCVolume[Index] & 0x00FF) : 0x1F; +#ifdef NULL_DB + WMValue = 0x0C; +#endif + WriteWM8770 (devc, WMReg, WMValue); + WMValue = (((devc->m_ADCVolume[Index] >> 8) & 0x00FF) < 0x1F) ? + ((devc->m_ADCVolume[Index] >> 8) & 0x00FF) : 0x1F; +#ifdef NULL_DB + WMValue = 0x0C; +#endif + WriteWM8770 (devc, WMReg + 1, WMValue); + /* Force saving of registers */ + /* devc->m_pAdapter->SetDirty(); TODO */ +} + +#if 0 +/*! \fn ======================================================================= + Function : GetADCGain +------------------------------------------------------------------------------- + Description : + Returns : UCHAR -> + Parameters : IN ULONG Index -> + : IN ULONG Channel -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static UCHAR GetADCGain + (envy24ht_devc * devc, IN ULONG Index, IN ULONG Channel) +{ + UCHAR Value; + + if (Channel == CH_LEFT) + { + Value = (UCHAR) ((devc->m_ADCVolume[Index] >> 8) & 0xFF); + } + else + { + Value = (UCHAR) (devc->m_ADCVolume[Index] & 0xFF); + } + return Value; +} +#endif + +/*! \fn ======================================================================= + Function : SetADCMux +------------------------------------------------------------------------------- + Description : + Parameters : IN Value -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +SetADCMux (envy24ht_devc * devc, IN ULONG Value) +{ + UCHAR MuxVal = 0; + BOOLEAN fAU; + + /* Store to shadow register */ + devc->m_ADCMux = Value; + devc->m_fSPDIFRecord = 0; + fAU = (devc->subvendor == SSID_AUREON_UNIVERSE); + switch (Value) + { + case CD_IN_MUX_TP_PIN: + devc->m_ADCIndex = ADC_CD; + MuxVal = (fAU) ? 0x11 : 0x00; + break; + case LINE_IN_MUX_TP_PIN: + devc->m_ADCIndex = ADC_LINE; + MuxVal = (fAU) ? 0x33 : 0x22; + break; + case AUX_IN_MUX_TP_PIN: + devc->m_ADCIndex = ADC_AUX; + MuxVal = (fAU) ? devc->m_AuxMux : 0x11; + break; + case MIC_IN_MUX_TP_PIN: + devc->m_ADCIndex = ADC_MIC; + MuxVal = (fAU) ? 0x55 : 0x33; + break; + case DIG_IN_MUX_TP_PIN: + /* Use SPDIF DMA channel */ + devc->m_fSPDIFRecord = 1; + break; + case PHONO_IN_MUX_PIN: + devc->m_ADCIndex = ADC_PHONO; + MuxVal = 0x22; + break; + case STEREO_MIX_MUX_TP_PIN: + devc->m_ADCIndex = ADC_STEREO_MIX; + MuxVal = (fAU) ? 0x77 : 0x44; + break; + + default: + devc->m_ADCIndex = ADC_LINE; + MuxVal = 0x22; + + } + WriteWM8770 (devc, WM_ADC_INPUT_MX, (UCHAR) MuxVal); + /* Reset GAIN */ + SetADCGain (devc, devc->m_ADCIndex, 0, CH_NOP); + /* Update PCA config */ + WritePCA (devc); + /* Force saving of registers */ + /*devc->m_pAdapter->SetDirty(); TODO */ +} + +/*! \fn ======================================================================= + Function : InitWM8770 +------------------------------------------------------------------------------- + Description : +------------------------------------------------------------------------------- + Notes : Call before Registry Read +=============================================================================*/ +static void +InitWM8770 (envy24ht_devc * devc) +{ + /*---------------------------------------------------------------------------- + Reset WM8770 + -----------------------------------------------------------------------------*/ + /* Set SPI Mode */ + WriteGPIO (devc, 0, GPIO_WM8770_RS_MASK); + oss_udelay (3); + WriteGPIO (devc, GPIO_WM8770_CS_MASK, GPIO_WM8770_CS_MASK); + oss_udelay (3); + WriteGPIO (devc, GPIO_WM8770_RS_MASK, GPIO_WM8770_RS_MASK); + oss_udelay (100); + + /*---------------------------------------------------------------------------- + Set defaults + -----------------------------------------------------------------------------*/ + + /* Output defaults */ + SetDACVolume (devc, CH_MASTER_BOTH, WM_OUT_DEFAULT); + SetDACVolume (devc, CH_FRONT_BOTH, WM_OUT_DEFAULT); + SetDACVolume (devc, CH_REAR_BOTH, WM_OUT_DEFAULT); + SetDACVolume (devc, CH_CENTER, WM_OUT_DEFAULT); + SetDACVolume (devc, CH_LFE, WM_OUT_DEFAULT); + SetDACVolume (devc, CH_BS_BOTH, WM_OUT_DEFAULT); + SetDACMute (devc, CH_MASTER_BOTH, 0); + SetDACMute (devc, CH_FRONT_BOTH, 0); + SetDACMute (devc, CH_REAR_BOTH, 0); + SetDACMute (devc, CH_CENTER, 0); + SetDACMute (devc, CH_LFE, 0); + SetDACMute (devc, CH_BS_BOTH, 0); + /* Input */ + SetADCGain (devc, ADC_CD, WM_INP_DEFAULT, CH_BOTH); + SetADCGain (devc, ADC_AUX, WM_INP_DEFAULT, CH_BOTH); + SetADCGain (devc, ADC_LINE, WM_INP_DEFAULT, CH_BOTH); + SetADCGain (devc, ADC_MIC, WM_INP_DEFAULT, CH_BOTH); + /* Mux */ + SetADCMux (devc, STEREO_MIX_MUX_TP_PIN); + + /* At least Power DAC & ADC */ + WriteWM8770 (devc, WM_POWER_CNTRL, 0x0000); + /* Power ADC Input Mux */ + WriteWM8770 (devc, WM_ADC_INPUT_MX, 0x0000); + /* Enable Aux-Output */ + + if (devc->subvendor == SSID_PHASE28) + { + WriteWM8770 (devc, WM_OUT12_SELECT, 0x0009); + WriteWM8770 (devc, WM_OUT34_SELECT, 0x0009); + } + else + { + WriteWM8770 (devc, WM_OUT12_SELECT, 0x000b); + WriteWM8770 (devc, WM_OUT34_SELECT, 0x0009); + } + /* Init Master Mode Register */ + WriteWM8770 (devc, WM_MASTER_MODE_CNTRL, 0x0012); + +} + +/*! \fn ======================================================================= + Function : InitCS8415 +------------------------------------------------------------------------------- + Description : + Returns : void -> +------------------------------------------------------------------------------- + Notes : Has to be called after InitWM8770 since RST must be "high" +=============================================================================*/ +static void +InitCS8415 (envy24ht_devc * devc) +{ + + /* Init Remote-Controller */ + if (devc->subvendor == SSID_AUREON_UNIVERSE) + { + UCHAR bByte[2]; + bByte[0] = (UCHAR) ((AUREON_REMOTE_ID >> 8) & 0xFF); + bByte[1] = (UCHAR) AUREON_REMOTE_ID; + IICWriteBuffer (devc, AUREON_REMOTE_CNTRL, bByte, 2, 200); + } + /*---------------------------------------------------------------------------- + Reset CS8415 + -----------------------------------------------------------------------------*/ + /* Set SPI Mode */ + WriteGPIO (devc, GPIO_CS8415_CS_MASK, GPIO_CS8415_CS_MASK); + oss_udelay (3); + WriteGPIO (devc, 0, GPIO_CS8415_CS_MASK); + oss_udelay (100); + + /* Set defaults */ + WriteCS8415 (devc, CS_CONTROL_1, 0x80); + /* SPDIF mux to RXP1 */ + WriteCS8415 (devc, CS_CONTROL_2, 0x01); + WriteCS8415 (devc, CS_CLK_SRC_CNTRL, 0x41); + WriteCS8415 (devc, CS_SER_OUT_FORMAT, 0x05); + + /* all other register remain to their defaults */ + +} + +#define GPIO_WAIT 10 + +/*! \fn ======================================================================= + Function : WriteAC97Codec +------------------------------------------------------------------------------- + Description : Writes to TT-AC97 Interface + Parameters : IN ULONG Register -> + : IN ULONG Data -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void WriteAC97Codec + (envy24ht_devc * devc, IN ULONG Register, IN ULONG Data) +{ + UCHAR TTData; + +#ifdef TTTT + /* CL_TEST */ + /* Reset AC97 */ + WriteGPIO (devc, GPIO_AC97_RESET_MASK, GPIO_AC97_RESET_MASK); + oss_udelay (3); + WriteGPIO (devc, 0, GPIO_AC97_RESET_MASK); + oss_udelay (3); + WriteGPIO (devc, GPIO_AC97_RESET_MASK, GPIO_AC97_RESET_MASK); + oss_udelay (3); + /* Unmute Master */ + WriteAC97Codec (devc, 0x02, 0x00); +#endif + + /* m_pAdapter->HwEnter(); TODO */ + + oss_udelay (GPIO_WAIT); + + /* Reset all LD Pins and GO Pin */ + WriteGPIO (devc, 0, + GPIO_LD_DATA_H_MASK | GPIO_LD_DATA_L_MASK | GPIO_LD_ADR_MASK | + GPIO_GO_MASK); + + + /* apply address to TT-AC97 Interface */ + WriteGPIO (devc, Register, GPIO_DATA_ADR_MASK); + + /* Set "Load Address" Pin */ + oss_udelay (GPIO_WAIT); + WriteGPIO (devc, GPIO_LD_ADR_MASK, GPIO_LD_ADR_MASK); + + /* Reset "Load Address" Pin */ + oss_udelay (GPIO_WAIT); + WriteGPIO (devc, 0, GPIO_LD_ADR_MASK); + + /* apply data low to TT-AC97 Interface */ + oss_udelay (GPIO_WAIT); + TTData = (UCHAR) (Data & 0x000000FF); + WriteGPIO (devc, TTData, GPIO_DATA_ADR_MASK); + + /* Set "Load Data low" Pin */ + oss_udelay (GPIO_WAIT); + WriteGPIO (devc, GPIO_LD_DATA_L_MASK, GPIO_LD_DATA_L_MASK); + + /* Reset "Load Data low" Pin */ + oss_udelay (GPIO_WAIT); + WriteGPIO (devc, 0, GPIO_LD_DATA_L_MASK); + + /* apply data high to TT-AC97 Interface */ + oss_udelay (GPIO_WAIT); + TTData = (UCHAR) ((Data >> 8) & 0x000000FF); + WriteGPIO (devc, TTData, GPIO_DATA_ADR_MASK); + + /* Set "Load Data high" Pin */ + oss_udelay (GPIO_WAIT); + WriteGPIO (devc, GPIO_LD_DATA_H_MASK, GPIO_LD_DATA_H_MASK); + + /* Reset "Load Data high" Pin */ + oss_udelay (GPIO_WAIT); + WriteGPIO (devc, 0, GPIO_LD_DATA_H_MASK); + + /* Set and immediately reset "GO" Pin */ + oss_udelay (GPIO_WAIT); + WriteGPIO (devc, GPIO_GO_MASK, GPIO_GO_MASK); + WriteGPIO (devc, 0, GPIO_GO_MASK); + /* m_pAdapter->HwLeave(); *//* TODO */ + +} + +/*! \fn ======================================================================= + Function : SetAC97Volume +------------------------------------------------------------------------------- + Description : + Parameters : IN ULONG Index -> + : IN UHSORT Value -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void SetAC97Volume + (envy24ht_devc * devc, IN ULONG Index, IN USHORT left, IN USHORT right) +{ + int AC97Reg = 0, value; + + devc->m_AC97Volume[Index] = left | (right << 8); + + left = AC97_INP_MAX - left; + right = AC97_INP_MAX - right; + + value = (left << 8) | right; + + switch (devc->subvendor) + { + case SSID_AUREON_SKY: + case SSID_PRODIGY71: + case SSID_AUREON_SPACE: + switch (Index) + { + case AC97_MIC: + AC97Reg = AC97_IDXREG_MIC_IN; + break; + case AC97_LINE: + AC97Reg = AC97_IDXREG_LINE_IN; + break; + case AC97_CD: + AC97Reg = AC97_IDXREG_CD_IN; + break; + case AC97_AUX: + AC97Reg = AC97_IDXREG_AUX_IN; + break; + default: + cmn_err (CE_CONT, "Aureon: Bad index %d\n", Index); + return; + } + break; + case SSID_AUREON_UNIVERSE: + switch (Index) + { + case AC97_MIC: + AC97Reg = AC97_IDXREG_MIC_IN; + break; + case AC97_LINE: + AC97Reg = AC97_IDXREG_LINE_IN; + break; + case AC97_CD: + AC97Reg = AC97_IDXREG_AUX_IN; + break; + case AC97_PHONO: + AC97Reg = AC97_IDXREG_CD_IN; + break; + case AC97_AUX: + case AC97_LINE2: + AC97Reg = AC97_IDXREG_VIDEO_IN; + break; + default: + cmn_err (CE_CONT, "Aureon: Bad index %d\n", Index); + return; + + } + break; + } + if (!devc->m_fAC97Mute[Index]) + { + WriteAC97Codec (devc, AC97Reg, value); + } + /* Force saving of registers */ + /* m_pAdapter->SetDirty(); TODO */ +} + + +/*! \fn ======================================================================= + Function : GetAC97Volume +------------------------------------------------------------------------------- + Description : + Returns : UCHAR -> + Parameters : IN ULONG Index -> + : IN ULONG Channel -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static USHORT +GetAC97Volume (envy24ht_devc * devc, IN ULONG Index) +{ + return devc->m_AC97Volume[Index]; +} + +/*! \fn ======================================================================= + Function : SetAC97Mute +------------------------------------------------------------------------------- + Description : + Returns : void -> + Parameters : IN ULONG Index -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +SetAC97Mute (envy24ht_devc * devc, IN ULONG Index, BOOLEAN OnOff) +{ + UCHAR AC97Reg = 0; + devc->m_fAC97Mute[Index] = OnOff; + switch (devc->subvendor) + { + case SSID_AUREON_SKY: + case SSID_PRODIGY71: + case SSID_AUREON_SPACE: + switch (Index) + { + case AC97_MIC: + AC97Reg = AC97_IDXREG_MIC_IN; + break; + case AC97_LINE: + AC97Reg = AC97_IDXREG_LINE_IN; + break; + case AC97_CD: + AC97Reg = AC97_IDXREG_CD_IN; + break; + case AC97_AUX: + AC97Reg = AC97_IDXREG_AUX_IN; + break; + default: + cmn_err (CE_CONT, "Aureon: Bad index %d\n", Index); + return; + } + break; + case SSID_AUREON_UNIVERSE: + switch (Index) + { + case AC97_MIC: + AC97Reg = AC97_IDXREG_MIC_IN; + break; + case AC97_LINE: + AC97Reg = AC97_IDXREG_LINE_IN; + break; + case AC97_CD: + AC97Reg = AC97_IDXREG_AUX_IN; + break; + case AC97_PHONO: + AC97Reg = AC97_IDXREG_CD_IN; + break; + case AC97_AUX: + case AC97_LINE2: + AC97Reg = AC97_IDXREG_VIDEO_IN; + break; + default: + cmn_err (CE_CONT, "Aureon: Bad index %d\n", Index); + return; + } + break; + } +#ifdef AC97_MUTE + WriteAC97Codec (devc, AC97Reg, 0x8000); +#else + WriteAC97Codec (devc, AC97Reg, + (OnOff) ? 0x8000 : devc->m_AC97Volume[Index]); +#endif + + /* Force saving of registers */ + /* m_pAdapter->SetDirty(); *//* TODO */ +} + +/*! \fn ======================================================================= + Function : InitAC97 +------------------------------------------------------------------------------- + Description : + Returns : void -> +------------------------------------------------------------------------------- + Notes : Call before Registry Read +=============================================================================*/ +static void +InitAC97 (envy24ht_devc * devc) +{ + /* Reset AC97 */ + oss_udelay (30); + WriteGPIO (devc, GPIO_AC97_RESET_MASK, GPIO_AC97_RESET_MASK); + oss_udelay (3); + WriteGPIO (devc, 0, GPIO_AC97_RESET_MASK); + oss_udelay (3); + WriteGPIO (devc, GPIO_AC97_RESET_MASK, GPIO_AC97_RESET_MASK); + oss_udelay (3); + + /* Unmute Master */ + WriteAC97Codec (devc, 0x02, 0x00); + + /*---------------------------------------------------------------------------- + Set defaults + -----------------------------------------------------------------------------*/ + /* Volume */ + SetAC97Volume (devc, AC97_MIC, AC97_INP_DEFAULT, AC97_INP_DEFAULT); + SetAC97Volume (devc, AC97_LINE, AC97_INP_DEFAULT, AC97_INP_DEFAULT); + SetAC97Volume (devc, AC97_AUX, AC97_INP_DEFAULT, AC97_INP_DEFAULT); + SetAC97Volume (devc, AC97_CD, AC97_INP_DEFAULT, AC97_INP_DEFAULT); + /* Mute */ + SetAC97Mute (devc, AC97_MIC, 0); + SetAC97Mute (devc, AC97_LINE, 0); + SetAC97Mute (devc, AC97_AUX, 0); + SetAC97Mute (devc, AC97_CD, 0); +} + +/*! \fn ======================================================================= + Function : SetSampleRate +------------------------------------------------------------------------------- + Description : + Returns : NTSTATUS -> + Parameters : IN ULONG SampleRate -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static void +SetSampleRate (envy24ht_devc * devc, IN ULONG SampleRate) +{ + WriteWM8770 (devc, WM_MASTER_MODE_CNTRL, 0x0012); + + switch (SampleRate) + { + case 48000: + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_48KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + /* WRITE_PORT_UCHAR(WKNMTBase + MT_DATA_FORMAT_REG,(~MT_128X)&READ_PORT_UCHAR(WKNMTBase + MT_DATA_FORMAT_REG)); */ + break; + case 24000: + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_24KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + break; + case 12000: + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_12KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + break; + case 9600: + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_9p6KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + break; + case 32000: + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_32KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + break; + case 16000: + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_16KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + break; + case 8000: + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_8KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + break; + case 96000: + WriteWM8770 (devc, WM_MASTER_MODE_CNTRL, 0x001A); + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_96KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + break; + case 192000: + WriteWM8770 (devc, WM_MASTER_MODE_CNTRL, 0x001A); + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_192KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_ON); + break; + case 64000: + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_64KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + break; + case 44100: + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_44p1KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + break; + case 22050: + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_22p05KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + break; + case 11025: + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_11p025KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + break; + case 88200: + WriteWM8770 (devc, WM_MASTER_MODE_CNTRL, 0x001A); + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_88p2KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + break; + case 176400: + WriteWM8770 (devc, WM_MASTER_MODE_CNTRL, 0x001A); + WritePort (devc, REG_MT, MT_SAMPLE_RATE_REG, MT_176p4KHZ, WIDTH_BYTE); + ReadModifyWritePort (devc, REG_MT, MT_DATA_FORMAT_REG, MT_128X, + WIDTH_BYTE | BITS_OFF); + break; + default: + break; + + } +} + +static void +aureon_card_init (envy24ht_devc * devc) +{ + +/* Do not change the order of the following lines */ + Init1724 (devc); + ResetGPIO (devc); + + if (devc->subvendor != SSID_PHASE28) + InitAC97 (devc); + InitWM8770 (devc); + InitCS8415 (devc); +/* Do not change the order of the above lines */ + + SetSPDIFConfig (devc, 0); + /* SetSDPIFSource(devc, DIGOUT_WAVE); */ + SetDigInSource (devc, DIGIN_COAX); + SetFrontjack (devc, FRONT_JACK_HEADPHONE); + SetLineSource (devc, SRC_LINE_REAR); + SetClockSource (devc, ICE_INTERNAL_CLOCK); + +} + +static void +aureon_set_rate (envy24ht_devc * devc) +{ + SetSampleRate (devc, devc->speed); +} + +static int +aureon_set_ctl (int dev, int ctl, unsigned int cmd, int value) +{ + envy24ht_devc *devc = mixer_devs[dev]->devc; + + if (cmd == SNDCTL_MIX_READ) + { + switch (ctl) + { + case 1: + return devc->m_LineInSource; + case 3: + return devc->m_DigInSource; + case 4: + return devc->m_Frontjack; + } + } + + if (cmd == SNDCTL_MIX_WRITE) + { + switch (ctl) + { + case 1: + SetLineSource (devc, value); + return devc->m_LineInSource; + case 3: + SetDigInSource (devc, value); + return devc->m_DigInSource; + case 4: + SetFrontjack (devc, value); + return devc->m_Frontjack; + } + } + + return OSS_EINVAL; +} + +static int +aureon_set_vol (int dev, int ChannelID, unsigned int cmd, int value) +{ + envy24ht_devc *devc = mixer_devs[dev]->devc; + ULONG LineIndex; + WORD LeftRight; + int left, right; + + /* Convert ChannelID to line index */ + LineIndex = ChannelID >> 15; + /* Get the left/right side */ + LeftRight = (WORD) (ChannelID & 0xffff); + + if (cmd == SNDCTL_MIX_READ) + { + left = right = 0; + if (LeftRight & CH_LEFT) + left = GetDACVolume (devc, (LineIndex << 15) | CH_LEFT); + if (LeftRight & CH_RIGHT) + right = GetDACVolume (devc, (LineIndex << 15) | CH_RIGHT); + + if (left == 0) + left = right; + else if (right == 0) + right = left; + + return left | (right << 8); + } + + if (cmd == SNDCTL_MIX_WRITE) + { + left = (value & 0xff); + right = ((value >> 8) & 0xff); + + if (LeftRight != CH_BOTH) + right = left; + + if (LeftRight & CH_LEFT) + SetDACVolume (devc, (LineIndex << 15) | CH_LEFT, left); + + if (LeftRight & CH_RIGHT) + SetDACVolume (devc, (LineIndex << 15) | CH_RIGHT, right); + + return left | (right << 8); + } + + return OSS_EINVAL; +} + +static int +aureon_set_ac97 (int dev, int Index, unsigned int cmd, int value) +{ + envy24ht_devc *devc = mixer_devs[dev]->devc; + int left, right; + + if (cmd == SNDCTL_MIX_READ) + { + return GetAC97Volume (devc, Index); + } + + if (cmd == SNDCTL_MIX_WRITE) + { + left = (value & 0xff); + right = ((value >> 8) & 0xff); + + SetAC97Volume (devc, Index, left, right); + + return left | (right << 8); + } + + return OSS_EINVAL; +} + + /*ARGSUSED*/ static int +aureon_mixer_init_common (envy24ht_devc * devc, int dev, int root) +{ + int ctl, group; + + if ((group = mixer_ext_create_group (dev, root, "VOL")) < 0) + return group; + + if ((ctl = mixer_ext_create_control (dev, group, + CH_MASTER_BOTH, + aureon_set_vol, + MIXT_STEREOSLIDER, "MASTER", + WM_OUT_MAX, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return ctl; + + if ((ctl = mixer_ext_create_control (dev, group, + CH_FRONT_BOTH, + aureon_set_vol, + MIXT_STEREOSLIDER, "FRONT", WM_OUT_MAX, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return ctl; + + if ((ctl = mixer_ext_create_control (dev, group, + CH_REAR_BOTH, + aureon_set_vol, + MIXT_STEREOSLIDER, "SURROUND", + WM_OUT_MAX, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return ctl; + + if ((ctl = mixer_ext_create_control (dev, group, + CH_CENTER, + aureon_set_vol, + MIXT_MONOSLIDER, "CENTER", WM_OUT_MAX, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return ctl; + + if ((ctl = mixer_ext_create_control (dev, group, + CH_LFE, + aureon_set_vol, + MIXT_MONOSLIDER, "LFE", WM_OUT_MAX, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return ctl; + + if ((ctl = mixer_ext_create_control (dev, group, + CH_BS_BOTH, + aureon_set_vol, + MIXT_STEREOSLIDER, "REAR", WM_OUT_MAX, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return ctl; + + if ((group = + mixer_ext_create_group_flags (dev, root, "TERRATEC", MIXF_FLAT)) < 0) + return group; + + if ((ctl = mixer_ext_create_control (dev, group, + 1, + aureon_set_ctl, + MIXT_ENUM, "LINESRC", 4, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return ctl; + mixer_ext_set_strings (dev, ctl, "AUX WTL REAR GROUND", 0); + + if ((ctl = mixer_ext_create_control (dev, group, + 3, + aureon_set_ctl, + MIXT_ENUM, "DIGIN", 3, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return ctl; + mixer_ext_set_strings (dev, ctl, "OPTICAL COAX CD", 0); + + return 0; +} + + /*ARGSUSED*/ static int +aureon_mixer_init_universe (envy24ht_devc * devc, int dev, int group) +{ + int ctl; + + if ((group = mixer_ext_create_group (dev, group, "ENVY24_UNIVERSE")) < 0) + return group; + + if ((ctl = mixer_ext_create_control (dev, group, + 4, + aureon_set_ctl, + MIXT_ENUM, "FRONTJACK", 3, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return ctl; + mixer_ext_set_strings (dev, ctl, "LINEIN HEADPH", 0); + + return 0; +} + + /*ARGSUSED*/ static int +aureon_mixer_init_ac97 (envy24ht_devc * devc, int dev, int group) +{ + int ctl; + + if ((group = mixer_ext_create_group (dev, group, "ENVY24_AC97")) < 0) + return group; + + if ((ctl = mixer_ext_create_control (dev, group, + AC97_MIC, + aureon_set_ac97, + MIXT_STEREOSLIDER, "MIC", AC97_INP_MAX, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return ctl; + + if ((ctl = mixer_ext_create_control (dev, group, + AC97_LINE, + aureon_set_ac97, + MIXT_STEREOSLIDER, "LINE", + AC97_INP_MAX, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return ctl; + + if ((ctl = mixer_ext_create_control (dev, group, + AC97_CD, + aureon_set_ac97, + MIXT_STEREOSLIDER, "CD", AC97_INP_MAX, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return ctl; + + if ((ctl = mixer_ext_create_control (dev, group, + AC97_PHONO, + aureon_set_ac97, + MIXT_STEREOSLIDER, "PHONO", + AC97_INP_MAX, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return ctl; + + if ((ctl = mixer_ext_create_control (dev, group, + AC97_AUX, + aureon_set_ac97, + MIXT_STEREOSLIDER, "AUX", AC97_INP_MAX, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return ctl; + + return 0; +} + +static int +aureon_mixer_init (envy24ht_devc * devc, int dev, int root) +{ + aureon_mixer_init_common (devc, dev, root); + + if (devc->subvendor == SSID_AUREON_UNIVERSE) + aureon_mixer_init_universe (devc, dev, root); + + if (devc->subvendor != SSID_PHASE28) + aureon_mixer_init_ac97 (devc, dev, root); + return 0; +} + +envy24ht_auxdrv_t envy24ht_aureon_auxdrv = { + aureon_card_init, + aureon_mixer_init, + aureon_set_rate, + NULL, + NULL, + NULL, /* aureon_private1 */ +}; diff --git a/kernel/drv/oss_envy24ht/envy24ht_julia.c b/kernel/drv/oss_envy24ht/envy24ht_julia.c new file mode 100644 index 0000000..544dd08 --- /dev/null +++ b/kernel/drv/oss_envy24ht/envy24ht_julia.c @@ -0,0 +1,547 @@ +/* + * Purpose: Low level routines for the ESI (Egosys) Juli@ card + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include "oss_envy24ht_cfg.h" + +#include "spdif.h" +#include "envy24ht.h" + +#define AK4358_ADDRESS 0x11 +#define AK4114_ADDRESS 0x10 + +static unsigned char +i2c_read (envy24ht_devc * devc, unsigned char addr, unsigned char pos) +{ + int i; + unsigned char data; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + OUTB (devc->osdev, pos, devc->ccs_base + 0x11); /* Offset */ + OUTB (devc->osdev, addr << 1, devc->ccs_base + 0x10); /* Read address */ + + for (i = 0; i < 2000; i++) + { + unsigned char status = INB (devc->osdev, devc->ccs_base + 0x13); + if (!(status & 1)) + break; + + } + + oss_udelay (1); + data = INB (devc->osdev, devc->ccs_base + 0x12); + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + + return data; +} + +static void +i2c_write (envy24ht_devc * devc, unsigned char addr, unsigned char pos, + unsigned char data) +{ + int i; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + + for (i = 0; i < 2000; i++) + { + unsigned char status = INB (devc->osdev, devc->ccs_base + 0x13); + if (!(status & 1)) + break; + + } + + OUTB (devc->osdev, pos, devc->ccs_base + 0x11); /* Offset */ + OUTB (devc->osdev, data, devc->ccs_base + 0x12); /* Data */ + OUTB (devc->osdev, (addr << 1) | 1, devc->ccs_base + 0x10); /* Write address */ + + for (i = 0; i < 2000; i++) + { + unsigned char status = INB (devc->osdev, devc->ccs_base + 0x13); + if (!(status & 1)) + break; + + } + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + + if ((addr == AK4358_ADDRESS) && (pos <= 0x0c)) + devc->m_DACVolume[pos] = data; +} + +static void +GPIOWrite (envy24ht_devc * devc, int pos, int bit) +{ + int data = INW (devc->osdev, devc->ccs_base + 0x14); + + bit = (bit != 0); + + data &= ~(1 << pos); + data |= (bit << pos); + + OUTW (devc->osdev, data, devc->ccs_base + 0x14); +} + +static int +set_dac (envy24ht_devc * devc, int reg, int level) +{ + if (level < 0) + level = 0; + if (level > 0x7f) + level = 0x7f; + + i2c_write (devc, AK4358_ADDRESS, reg, level | 0x80); + + return level; +} + +static struct { + int rate, spdifin, clks; +} rate_sel[] = { + {32000, 0x30, 0x08}, + {44100, 0x00, 0x09}, + {48000, 0x20, 0x0A}, + {88200, 0x80, 0x05}, + {96000, 0xA0, 0x06}, + {176400, 0xC0, 0x01}, + {192000, 0xE0, 0x02}, + {16000, -1, 0x0C}, + {22050, -1, 0x0D}, + {24000, -1, 0x0E}, + {64000, -1, 0x04}, + {128000, -1, 0x00}, + {-1, -1, -1} +}; + +static void +julia_set_deemph (envy24ht_devc * devc, int mode) +{ + int deemph = 0x01; /* OFF */ + + if (mode == 0) + i2c_write (devc, AK4358_ADDRESS, 0x03, deemph); + else { + + if (devc->speed == 44100) + deemph = 0x00; + else if (devc->speed == 48000) + deemph = 0x02; + else if (devc->speed == 32000) + deemph = 0x03; + + i2c_write (devc, AK4358_ADDRESS, 0x03, deemph); + } +} + +static void +julia_Monitor (envy24ht_devc * devc, int bMonitor, int num) +{ + switch (num) + { + case 0: /* MUTE */ + if (bMonitor) + { + i2c_write (devc, AK4358_ADDRESS, 1, 0x03); + GPIOWrite (devc, 15, 1); + } else { + i2c_write (devc, AK4358_ADDRESS, 1, 0x01); + GPIOWrite (devc, 15, 0); + } + break; + + case 1: /* LINEIN */ + if (bMonitor) + GPIOWrite (devc, 13, 1); + else + GPIOWrite (devc, 13, 0); + break; + + case 2: /* SPDIFOUT */ + if (bMonitor) + GPIOWrite (devc, 11, 1); + else + GPIOWrite (devc, 11, 0); + break; + case 3: /* SPDIFIN */ + if (bMonitor) + GPIOWrite (devc, 12, 1); + else + GPIOWrite (devc, 12, 0); + break; + case 4: /* De-emphasis */ + julia_set_deemph (devc, bMonitor); + break; + } + devc->monitor[num] = bMonitor; +} + +static void +ak4114_init (envy24ht_devc * devc) +{ + /* + * AK4114 S/PDIF interface initialization + */ + i2c_write (devc, AK4114_ADDRESS, 0x00, 0x0f); + i2c_write (devc, AK4114_ADDRESS, 0x01, 0x70); + i2c_write (devc, AK4114_ADDRESS, 0x02, 0x80); + i2c_write (devc, AK4114_ADDRESS, 0x03, 0x49); + i2c_write (devc, AK4114_ADDRESS, 0x04, 0x00); + i2c_write (devc, AK4114_ADDRESS, 0x05, 0x00); + + i2c_write (devc, AK4114_ADDRESS, 0x0d, 0x41); + i2c_write (devc, AK4114_ADDRESS, 0x0e, 0x02); + i2c_write (devc, AK4114_ADDRESS, 0x0f, 0x2c); + i2c_write (devc, AK4114_ADDRESS, 0x10, 0x00); + i2c_write (devc, AK4114_ADDRESS, 0x11, 0x00); +} + +static void +julia_set_rate (envy24ht_devc * devc) +{ + int i, data; + int adc = 0x00; + + if (devc->speed <= 48000) + devc->m_DACVolume[2] = 0x4f; /* DFS=normal-speed */ + else if (devc->speed <= 96000) + { + adc = 0x04; + devc->m_DACVolume[2] = 0x5f; /* DFS=double-speed */ + } + else + { + adc = 0x03; + devc->m_DACVolume[2] = 0x6f; /* DFS=quad-speed */ + } + + data = INW (devc->osdev, devc->ccs_base + 0x14); + data &= ~0x70f; + data |= adc << 8; + + for (i = 0; rate_sel[i].rate; i++) + if (rate_sel[i].rate == devc->speed) + data |= rate_sel[i].clks; + + OUTW (devc->osdev, data, devc->ccs_base + 0x14); + + OUTB (devc->osdev, 0x80, devc->mt_base + 0x05); /* RESET */ + OUTB (devc->osdev, 0x00, devc->mt_base + 0x05); + + if (devc->speed == 8000) + OUTB (devc->osdev, 0x06, devc->mt_base + 0x01); + else if (devc->speed == 9600) + OUTB (devc->osdev, 0x03, devc->mt_base + 0x01); + else if (devc->speed == 11025) + OUTB (devc->osdev, 0x0a, devc->mt_base + 0x01); + else if (devc->speed == 12000) + OUTB (devc->osdev, 0x02, devc->mt_base + 0x01); + else + OUTB (devc->osdev, 0x10, devc->mt_base + 0x01); + + /* Restore ak4358 regs and set DFS */ + for (i = 0; i <= 0x0c; i++) + i2c_write (devc, AK4358_ADDRESS, i, devc->m_DACVolume[i]); + /* Restore ak4114 */ + ak4114_init (devc); + if (devc->monitor[4] == 1) + julia_set_deemph (devc, 1); +} + +static void +julia_sync_ak4114 (void *arg) +{ + envy24ht_devc *devc = arg; + int i; + int spdifin = i2c_read (devc, AK4114_ADDRESS, 7); + + for (i = 0; rate_sel[i].rate; i++) + if (rate_sel[i].spdifin == spdifin) + { + if (devc->speed != rate_sel[i].rate) + { + devc->speed = rate_sel[i].rate; + mixer_devs[devc->mixer_dev]->modify_counter++; + julia_set_rate (devc); + } + break; + } + + if (devc->syncsource == 1) + devc->timeout_id = timeout (julia_sync_ak4114, devc, OSS_HZ); +} + +static int +julia_set_syncsource (envy24ht_devc * devc, int value) +{ + if (value == 1) + { + GPIOWrite (devc, 4, 0); + devc->timeout_id = timeout (julia_sync_ak4114, devc, OSS_HZ); + } else { + if (devc->timeout_id != 0) + untimeout (devc->timeout_id); + devc->timeout_id = 0; + + GPIOWrite (devc, 4, 1); + + if (devc->speed != devc->pending_speed) + { + devc->speed = devc->pending_speed; + mixer_devs[devc->mixer_dev]->modify_counter++; + julia_set_rate (devc); + } + } + + return 0; +} + +static int +julia_audio_ioctl (envy24ht_devc * devc, envy24ht_portc * portc, unsigned int cmd, + ioctl_arg arg) +{ + int left, right, value; + + switch (cmd) + { + case SNDCTL_DSP_GETPLAYVOL: + if (portc != &devc->play_portc[0]) + return OSS_EINVAL; + left = (devc->gains[0] & 0xff) * 100 / 0x7f; + right = ((devc->gains[0] >> 8) & 0xff) * 100 / 0x7f; + return *arg = (left | (right << 8)); + break; + + case SNDCTL_DSP_SETPLAYVOL: + if (portc != &devc->play_portc[0]) + return OSS_EINVAL; + value = *arg; + left = value & 0xff; + right = (value >> 8) & 0xff; + + left = (left * 0x7f) / 100; + right = (right * 0x7f) / 100; + left = set_dac (devc, 0x04, left); + right = set_dac (devc, 0x05, right); + devc->gains[0] = left | (right << 8); + mixer_devs[devc->mixer_dev]->modify_counter++; + return 0; + break; + } + return OSS_EINVAL; +} + +static int +julia_set_control (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24ht_devc *devc = mixer_devs[dev]->hw_devc; + + if (cmd == SNDCTL_MIX_READ) + { + if (ctrl < 0 || ctrl > 4) + return OSS_EINVAL; + + return devc->monitor[ctrl]; + } + + if (cmd == SNDCTL_MIX_WRITE) + { + if (ctrl < 0 || ctrl > 4) + return OSS_EINVAL; + + value = !!value; + julia_Monitor (devc, value, ctrl); + return devc->monitor[ctrl]; + } + + return OSS_EINVAL; +} + +static int +julia_set_ak4358 (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24ht_devc *devc = mixer_devs[dev]->hw_devc; + + if (cmd == SNDCTL_MIX_READ) + { + if (ctrl < 0 || ctrl > 4) + return OSS_EIO; + + return devc->gains[ctrl]; + } + + if (cmd == SNDCTL_MIX_WRITE) + { + int left, right; + + left = value & 0xff; + right = (value >> 8) & 0xff; + + switch (ctrl) + { + case 0: /* PCM */ + left = set_dac (devc, 0x04, left); + right = set_dac (devc, 0x05, right); + break; + case 1: /* LINEIN */ + left = set_dac (devc, 0x06, left); + right = set_dac (devc, 0x07, right); + break; + case 2: /* SPDIFOUT */ + left = set_dac (devc, 0x08, left); + right = set_dac (devc, 0x09, right); + break; + case 3: /* SPDIFIN */ + left = set_dac (devc, 0x0B, left); + right = set_dac (devc, 0x0C, right); + break; + + default: + return OSS_EINVAL; + } + + value = left | (right << 8); + return devc->gains[ctrl] = value; + } + + return OSS_EINVAL; +} + + /*ARGSUSED*/ static int +julia_mixer_init (envy24ht_devc * devc, int dev, int g) +{ + int group = g; + int err, monitor; + + if ((group = mixer_ext_create_group (dev, g, "VOL")) < 0) + return group; + + if ((err = mixer_ext_create_control (dev, group, + 0, julia_set_control, + MIXT_ONOFF, + "ENVY24_MUTE", 2, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 0, julia_set_ak4358, + MIXT_STEREOSLIDER, + "ENVY24_PCM", 0x7f, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 1, julia_set_ak4358, + MIXT_STEREOSLIDER, + "ENVY24_LINEIN", 0x7f, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 2, julia_set_ak4358, + MIXT_STEREOSLIDER, + "ENVY24_SPDIFOUT", 0x7f, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 3, julia_set_ak4358, + MIXT_STEREOSLIDER, + "ENVY24_SPDIFIN", 0x7f, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((monitor = mixer_ext_create_group (dev, g, "MONITOR")) < 0) + return monitor; + + if ((err = mixer_ext_create_control (dev, monitor, + 1, julia_set_control, + MIXT_ONOFF, + "ENVY24_LINEIN", 2, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, monitor, + 2, julia_set_control, + MIXT_ONOFF, + "ENVY24_SPDIFOUT", 2, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, monitor, + 3, julia_set_control, + MIXT_ONOFF, + "ENVY24_SPDIFIN", 2, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, monitor, + 4, julia_set_control, + MIXT_ENUM, + "ENVY24_DEEMPH", 2, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + mixer_ext_set_strings (dev, err, "OFF 50/15usec", 0); + + return 0; +} + +static void +julia_card_init (envy24ht_devc * devc) +{ + ak4114_init (devc); +/* + * AK4358 DAC initialization + */ + i2c_write (devc, AK4358_ADDRESS, 2, 0x00); + i2c_write (devc, AK4358_ADDRESS, 2, 0x4E); + i2c_write (devc, AK4358_ADDRESS, 0, 0x06); + i2c_write (devc, AK4358_ADDRESS, 1, 0x02); + i2c_write (devc, AK4358_ADDRESS, 3, 0x01); + + set_dac (devc, 0x04, 0x7f); + set_dac (devc, 0x05, 0x7f); + set_dac (devc, 0x06, 0x7f); + set_dac (devc, 0x07, 0x7f); + set_dac (devc, 0x08, 0x7f); + set_dac (devc, 0x09, 0x7f); + set_dac (devc, 0x0b, 0x7f); + set_dac (devc, 0x0c, 0x7f); + + i2c_write (devc, AK4358_ADDRESS, 0xA, 0x00); + i2c_write (devc, AK4358_ADDRESS, 0xD, 0x00); + i2c_write (devc, AK4358_ADDRESS, 0xE, 0x00); + i2c_write (devc, AK4358_ADDRESS, 0xF, 0x00); + i2c_write (devc, AK4358_ADDRESS, 2, 0x4F); + + julia_Monitor (devc, 0, 0); /* Unmute */ +} + +static void +julia_card_uninit (envy24ht_devc * devc) +{ + if (devc->timeout_id != 0) + untimeout (devc->timeout_id); +} + +envy24ht_auxdrv_t envy24ht_julia_auxdrv = { + julia_card_init, + julia_mixer_init, + julia_set_rate, + NULL, + NULL, + julia_set_syncsource, + julia_audio_ioctl, + julia_card_uninit +}; diff --git a/kernel/drv/oss_envy24ht/envy24ht_revo51.c b/kernel/drv/oss_envy24ht/envy24ht_revo51.c new file mode 100644 index 0000000..e84cb6a --- /dev/null +++ b/kernel/drv/oss_envy24ht/envy24ht_revo51.c @@ -0,0 +1,938 @@ +/* + * Purpose: Low level routines for M Audio Revolution 5.1 + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include "oss_envy24ht_cfg.h" + +#include "spdif.h" +#include "envy24ht.h" + +static char channel_names[4][10] = { + "front", + "c/l", + "surround", + "headph" +}; + +#define BIT(x) (1<<(x)) +#define BIT3 BIT(3) + +/*----- SPI bus for CODEC communication. */ +/* */ +#define SPI_CLK 1 /* Clock output to CODEC's, rising edge clocks data. */ +#define SPI_DOUT 3 /* Data output to the CODEC. */ +#define SPI_CS0n (1<<4) /* Selects first chip. */ +#define SPI_CS1n (1<<5) /* Selects second chip. */ +#define SPI_CC_AK4358 0x02 /* C1:C0 for ak4358. */ +#define SPI_CC_AK5365 0x02 /* C1:C0 for ak5365. */ +#define WRITEMASK 0xffff +/*----- Revolution defines. */ +/* */ +#define REVO51_AK5365 (1) /* iDevice value for AK5365 A/D. */ +#define REVO51_AK4358 (2) /* iDevice value for AK4358 D/A. */ + +static unsigned int +GpioReadAll (envy24ht_devc * devc) +{ + return INW (devc->osdev, devc->ccs_base + 0x14); +} + +static void +GpioWriteAll (envy24ht_devc * devc, unsigned int data) +{ + OUTW (devc->osdev, 0xffff, devc->ccs_base + 0x18); /* GPIO direction */ + OUTW (devc->osdev, 0x0000, devc->ccs_base + 0x16); /* GPIO write mask */ + + OUTW (devc->osdev, data, devc->ccs_base + 0x14); +} + +static void +GpioWrite (envy24ht_devc * devc, int pos, int bit) +{ + int data = GpioReadAll (devc); + + bit = (bit != 0); + + data &= ~(1 << pos); + data |= (bit << pos); + + GpioWriteAll (devc, data); +} + +void +REVO51_Assert_CS (envy24ht_devc * devc, int iDevice) +/* +***************************************************************************** +* Assert chip select to specified GPIO-connected device. +* iDevice: REVO51_AK5365=ADC, REVO51_AK4358=DAC. +****************************************************************************/ +{ + unsigned int dwGPIO; /* Current GPIO's. */ + dwGPIO = GpioReadAll (devc); /* Read current GPIO's. */ + dwGPIO |= (SPI_CS0n | SPI_CS1n); /* Reset CS bits. */ + switch (iDevice) /* Select CS#. */ + { + case REVO51_AK4358: + dwGPIO &= ~SPI_CS0n; + break; /* DAC */ + case REVO51_AK5365: + dwGPIO &= ~SPI_CS1n; + break; /* ADC */ + default: + break; + } + GpioWriteAll (devc, dwGPIO); /* Write hardware. */ +} + +void +REVO51_DeAssert_CS (envy24ht_devc * devc) +/* +***************************************************************************** +* De-Assert all chip selects. +****************************************************************************/ +{ + unsigned int dwGPIO = GpioReadAll (devc); /* Current GPIO's. */ + dwGPIO |= (SPI_CS0n | SPI_CS1n); /* Clear CS bits. */ + GpioWriteAll (devc, dwGPIO); /* Write back to hardware. */ +} + +/*#define _delay() oss_udelay(1) */ +#define _delay() {} + +void +REVO51_WriteSpiAddr (envy24ht_devc * devc, int iDevice, unsigned char bReg) +/* +***************************************************************************** +* Write the address byte part of the SPI serial stream. +* iDevice: REVO51_AK4358=DAC, REVO51_AK5365=ADC, etc. +****************************************************************************/ +{ + unsigned char bHdr; + unsigned char bNum; +/* Built 8-bit packet header: C1,C0,R/W,A4,A3,A2,A1,A0. */ +/* */ + switch (iDevice) + { + case REVO51_AK4358: + bHdr = SPI_CC_AK4358 << 6; + break; + case REVO51_AK5365: + bHdr = SPI_CC_AK5365 << 6; + break; + default: + bHdr = 0; + break; + } + bHdr = bHdr | 0x20 | (bReg & 0x1F); /* "write" + address. */ +/* Write header to SPI. */ +/* */ + for (bNum = 0; bNum < 8; bNum++) + { + GpioWrite (devc, SPI_CLK, 0); /* Drop clock low. */ + _delay (); + GpioWrite (devc, SPI_DOUT, 0x080 & bHdr); /* Write data bit. */ + _delay (); + GpioWrite (devc, SPI_CLK, 1); /* Raise clock. */ + _delay (); + bHdr <<= 1; /* Next bit. */ + } +} + +void +REVO51_WriteSpiReg (envy24ht_devc * devc, int iDevice, unsigned char bReg, + unsigned char bData) +/* +***************************************************************************** +* Writes one register in specified CHIP. +* devc = PCI slot code of specific board. +* iDevice: REVO51_AK4358=DAC, REVO51_AK5365=ADC, etc. +****************************************************************************/ +{ + unsigned char bNum; + GpioWrite (devc, SPI_DOUT, 0); /* Init SPI signals. */ + GpioWrite (devc, SPI_CLK, 1); /* */ +/* Drop the chip select low. */ +/* Wait at least 150 nS. */ +/* */ + REVO51_Assert_CS (devc, iDevice); + _delay (); +/* Write the address byte. */ +/* */ + REVO51_WriteSpiAddr (devc, iDevice, bReg); +/* Write the data byte. */ +/* */ + for (bNum = 0; bNum < 8; bNum++) + { + GpioWrite (devc, SPI_CLK, 0); /* Drop clock low. */ + _delay (); + GpioWrite (devc, SPI_DOUT, 0x080 & bData); /* Write data bit. */ + _delay (); + GpioWrite (devc, SPI_CLK, 1); /* Raise clock. */ + _delay (); + bData <<= 1; /* Next bit. */ + } +/* De-assert chip selects. */ +/* */ + REVO51_DeAssert_CS (devc); + _delay (); +} + + +#define GPIO_MUTEn 22 /* Converter mute signal. */ +void +REVO51_Mute (envy24ht_devc * devc, int bMute) +/* +***************************************************************************** +* Mutes all outputs if bMute=1. +****************************************************************************/ +{ + if (bMute) + { +/* Soft-mute. Delay currently unspecified, try ½ second. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 1, 0x03); + _delay (); +/* Switch mute transistors on. */ + GpioWrite (devc, GPIO_MUTEn, 0); + } + else + { +/* Switch mute transistors off. Delay currently unspecified, try ½ second. */ + GpioWrite (devc, GPIO_MUTEn, 1); + _delay (); +/* Release Soft-mute. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 1, 0x01); + } + + devc->mute = bMute; +} + + +void +REVO51_Set_OutAttn (envy24ht_devc * devc, unsigned char bChan, int iAttn) +/* +***************************************************************************** +* Sets the attenuation on one output channel. +* bChan = Channel number (0..7). +* Channel 0:1 = front, 2:3 = center/sub, 4:5 = rear, 6:7 = headphones. +* Registers are 0x04, 05, 06, 07, 08, 09, 0B, 0C respectively +* iAttn = Number of steps to attenuate CODEC. +* Each step equals .5dB (-127..0) +****************************************************************************/ +{ + unsigned char bIndex; + unsigned char bAttn; + if (bChan > 7 || iAttn > 0 || iAttn < -127) /* parameter test */ + { + cmn_err (CE_CONT, "\ninvalid data! %d=bChan, %d=iAttn", bChan, iAttn); + return; + } + if (bChan < 6) + bIndex = 0x04 + bChan; /* for registers 0x04..0x09 */ + else + bIndex = 0x05 + bChan; /* for registers 0x0B..0x0C */ + bAttn = (0x80 + (unsigned char) (iAttn + 127)); /* 7F is max volume. */ +/* MSB enables attenuation. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, bIndex, bAttn); +} + +static void +ADC_Set_Chan (envy24ht_devc * devc, int iChan) +/*************************************************************************** +* Makes input selection for ADC. +* 0=mic, 1=line, 2=aux +****************************************************************************/ +{ +/*Check param */ + if (2 < iChan) +#define GPIO_SDA 6 /* SDA pin */ + cmn_err (CE_CONT, "\nInvalid Input channel parameter"); + else + REVO51_WriteSpiReg (devc, REVO51_AK5365, 1, (unsigned char) iChan); +} + +#define GPIO_SCL 7 /* SCL pin */ +static void +start_bit (envy24ht_devc * devc) +/* +***************************************************************************** +* Send I2C Start Bit. +****************************************************************************/ +{ + GpioWrite (devc, GPIO_SDA, 1); /* Make sure DATA high. */ + _delay (); + GpioWrite (devc, GPIO_SCL, 1); /* Set clock high. */ + _delay (); + GpioWrite (devc, GPIO_SDA, 0); /* Falling edge indicates start bit. */ + _delay (); + GpioWrite (devc, GPIO_SCL, 0); /* Start clock train. */ +} + +static void +byte_out (envy24ht_devc * devc, unsigned char b8) +/* +***************************************************************************** +* Send current 8 input bits in B8. Msb is first written. +****************************************************************************/ +{ + int bnum; /* bit number */ + for (bnum = 7; bnum >= 0; bnum--) + { + GpioWrite (devc, GPIO_SDA, b8 & 0x80); /* Set bit. */ + _delay (); /* */ + GpioWrite (devc, GPIO_SCL, 1); /* Set clock high. */ + _delay (); /* */ + GpioWrite (devc, GPIO_SCL, 0); /* Return clock low. */ + b8 = b8 << 1; /* Next position. */ + } +/* No ACK, but we need to clock a false "9th" bit. */ + _delay (); /* */ + GpioWrite (devc, GPIO_SCL, 1); /* Set clock high. */ + _delay (); /* */ + GpioWrite (devc, GPIO_SCL, 0); /* Return clock low. */ +} + +static void +stop_bit (envy24ht_devc * devc) +/* +***************************************************************************** +* Send I2C Stop Bit. +****************************************************************************/ +{ + GpioWrite (devc, GPIO_SDA, 0); /* Make sure data is low. */ + _delay (); + GpioWrite (devc, GPIO_SCL, 1); /* Set clock high. */ + _delay (); + GpioWrite (devc, GPIO_SDA, 1); /* Rising edge indicates stop bit. */ +} +static void +PT2258S_Write_Data (envy24ht_devc * devc, int cnt, unsigned char *aData) +/* +************************************************************************ ***** +* Write an array of bytes (aData) to address VADDR in PT2258S. +* Assumes I2C Bus is inactive, i.e., SCL=1,SDA=1 and both are outputs. +* This routine waits for the byte to complete writing. +************************************************************************ ****/ +{ + int i; + start_bit (devc); /* Send start bit. */ + byte_out (devc, (unsigned char) 0x80); /* Set address to write data to. */ + for (i = 0; i < cnt; i++) + byte_out (devc, aData[i]); /* Write data to address. */ + stop_bit (devc); /* Stop bit ends operation. */ +} +static void +PT2258S_Write_Byte (envy24ht_devc * devc, unsigned char vdata) +/* +***************************************************************************** +* Write a byte VDATA to address VADDR in PT2258S. +* Assumes I2C Bus is inactive, i.e., SCL=1,SDA=1 and both are outputs. +* This routine waits for the byte to complete writing. +****************************************************************************/ +{ + start_bit (devc); /* Send start bit. */ + byte_out (devc, (unsigned char) 0x80); /* Set address to write data to. */ + byte_out (devc, vdata); /* Write data to address. */ + stop_bit (devc); /* Stop bit ends operation. */ +} + +static void +PT2258S_Set_Attn (envy24ht_devc * devc, unsigned char bChan, int iAttn) +/* +************************************************************************ ***** +* Set the attenuation of a specific channel. +* bChan = 0..5. +* iAttn = 0..-79. +************************************************************************ ****/ +{ + static unsigned char bXlat[] = { 0x90, 0x50, 0x10, 0x30, 0x70, 0xB0 }; + unsigned char aAttn[2]; + aAttn[1] = (-iAttn) % 10; /* 1's digit */ + aAttn[0] = (-iAttn) / 10; /* 10's digit */ +/* Check parameters. */ + if ((bChan > 5) || (iAttn < (-79) || iAttn > 0)) + { + cmn_err (CE_CONT, "\nPT2258S_Set_Attn() parameter out of range!"); + return; + } +/* Always set 10's digit, then 1's. */ + aAttn[0] += bXlat[bChan] - 0x10; + aAttn[1] += bXlat[bChan]; + PT2258S_Write_Data (devc, 2, aAttn); +} +static void +PT2258S_Mute (envy24ht_devc * devc, int bMute) +/* +***************************************************************************** +* Mute all 6 outputs of the monitoring volume control. +* Unmuting returns volume to previous levels. +****************************************************************************/ +{ + if (bMute) + PT2258S_Write_Byte (devc, (unsigned char) 0xF9); + else + PT2258S_Write_Byte (devc, (unsigned char) 0xF8); +} + + +static void +REVO51_Set_48K_Mode (envy24ht_devc * devc) +/* +***************************************************************************** +* Sets Chip and Envy24 for 8kHz-48kHz sample rates. +****************************************************************************/ +{ +/* ICE MCLK = 256x. */ + OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 2) & ~BIT3, + devc->mt_base + 2); +/* DFS=normal, RESET. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 2, 0x4E); +/* DFS=normal, NORMAL OPERATION. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 2, 0x4F); +} + +static void +REVO51_Set_96K_Mode (envy24ht_devc * devc) +/* +***************************************************************************** +* Sets CODEC and Envy24 for 60kHz-96kHz sample rates. +****************************************************************************/ +{ +/* ICE MCLK = 256x. */ + OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 2) & ~BIT3, + devc->mt_base + 2); +/* DFS=double-speed, RESET. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 2, 0x5E); +/* DFS=double-speed, NORMAL OPERATION. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 2, 0x5F); +} + +static void +REVO51_Set_192K_Mode (envy24ht_devc * devc) +/* +***************************************************************************** +* Sets CODEC and Envy24 for 120kHz-192kHz sample rate. +****************************************************************************/ +{ +/* ICE MCLK = 128x. */ + OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 2) | BIT3, + devc->mt_base + 2); + _delay (); +/*----- SET THE D/A. */ +/* DFS=quad-speed, RESET. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 2, 0x6E); + _delay (); +/* DFS=quad-speed, NORMAL OPERATION. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 2, 0x6F); +/*------ POWER DOWN THE A/D -- doesn't support 192K. */ + REVO51_WriteSpiReg (devc, REVO51_AK5365, 0, 0x00); +} + +static int +set_dac (envy24ht_devc * devc, int reg, int level) +{ + if (level < 0) + level = 0; + if (level > 0x7f) + level = 0x7f; + + REVO51_WriteSpiReg (devc, REVO51_AK4358, reg, level | 0x80); + + return level; +} + +static void +AK4358_Init (envy24ht_devc * devc) +{ +/*===== AK4358 D/A initialization. Leave soft-muted. */ +/* */ +/* Power down, reset, normal mode. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 2, 0x00); +/* Power up, reset, normal mode */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 2, 0x4E); +/* Reset timing, Mode 3(I2S), disable auto clock detect, sharp roll off. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 0, 0x06); +/* Soft mute, reset timing. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 1, 0x02); +/* De-emphasis off. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 3, 0x01); +/* Max volume on all 8 channels. */ + set_dac (devc, 0x04, 0x7f); + set_dac (devc, 0x05, 0x7f); + set_dac (devc, 0x06, 0x7f); + set_dac (devc, 0x07, 0x7f); + set_dac (devc, 0x08, 0x7f); + set_dac (devc, 0x09, 0x7f); + set_dac (devc, 0x0b, 0x7f); + set_dac (devc, 0x0c, 0x7f); + +/* Datt mode 0, DZF non-invert, DCLK polarity 0, PCM mode, DCKS 512fs, TDM normal. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 0xA, 0x00); +/* DZF control disabled. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 0xD, 0x00); + REVO51_WriteSpiReg (devc, REVO51_AK4358, 0xE, 0x00); + REVO51_WriteSpiReg (devc, REVO51_AK4358, 0xF, 0x00); +/* Power up, normal operation. */ + REVO51_WriteSpiReg (devc, REVO51_AK4358, 2, 0x4F); +} + +static void +PT2258_init (envy24ht_devc * devc) +{ +/*===== PT2258 initialization. */ +/* */ +/* Initializes and clears register . */ + PT2258S_Write_Byte (devc, (unsigned char) 0xC0); +/*Example of 2-line command controlling all channels. */ +/*This sets all channels to max volume. */ +/*PT2258S_Write_Byte(devc, (unsigned char)0xE0); */ +/*PT2258S_Write_Byte(devc, (unsigned char)0xD0); */ +/* set volumes */ + PT2258S_Set_Attn (devc, 0, 0); + PT2258S_Set_Attn (devc, 1, 0); + PT2258S_Set_Attn (devc, 2, 0); + PT2258S_Set_Attn (devc, 3, 0); + PT2258S_Set_Attn (devc, 4, 0); + PT2258S_Set_Attn (devc, 5, 0); +/*mute, true or false */ + PT2258S_Mute (devc, 0); +} + +static void +REVO51_set_recsrc (envy24ht_devc * devc, int src) +{ + devc->recsrc = src; + + REVO51_WriteSpiReg (devc, REVO51_AK5365, 1, src); +} + +static void +AK5365_Init (envy24ht_devc * devc) +{ +/*===== AK5365 2-ch A/D initialization. Leave soft-muted. */ +/* */ +/* Power down. */ + REVO51_WriteSpiReg (devc, REVO51_AK5365, 0, 0x00); +/* Set input to (LINE IN). */ + REVO51_set_recsrc (devc, 0); +/* Clock freq 256fs, I2S mode, mute off. */ + REVO51_WriteSpiReg (devc, REVO51_AK5365, 2, 0x08); +/* ALC settings (ALC is disabled, so rewrite default values). */ + REVO51_WriteSpiReg (devc, REVO51_AK5365, 3, 0x2B); +/* Neutral analog and digital gain. */ + REVO51_WriteSpiReg (devc, REVO51_AK5365, 4, 0x7F); + REVO51_WriteSpiReg (devc, REVO51_AK5365, 5, 0x7F); +/* ALC settings (ALC is disabled, so rewrite default values). */ + REVO51_WriteSpiReg (devc, REVO51_AK5365, 6, 0x28); +/* ALC settings (ALC is disabled, so rewrite default values). */ + REVO51_WriteSpiReg (devc, REVO51_AK5365, 7, 0x89); +/* Power up. */ + REVO51_WriteSpiReg (devc, REVO51_AK5365, 0, 0x01); +} + +static void +revo51_set_rate (envy24ht_devc * devc) +{ + int tmp; + + tmp = INB (devc->osdev, devc->mt_base + 0x02); + if (devc->speed <= 48000) + { + REVO51_Set_48K_Mode (devc); + OUTB (devc->osdev, tmp & ~BIT (3), devc->mt_base + 0x02); + return; + } + + if (devc->speed <= 96000) + { + REVO51_Set_96K_Mode (devc); + + return; + } + + REVO51_Set_192K_Mode (devc); + OUTB (devc->osdev, tmp | BIT (3), devc->mt_base + 0x02); +} + +static int +revo51_audio_ioctl (envy24ht_devc * devc, envy24ht_portc * portc, unsigned int cmd, + ioctl_arg arg) +{ + int value; + + switch (cmd) + { + case SNDCTL_DSP_GET_RECSRC_NAMES: + return oss_encode_enum ((oss_mixer_enuminfo *) arg, "mic line aux", 0); + break; + + case SNDCTL_DSP_GET_RECSRC: + return *arg = devc->recsrc; + break; + + case SNDCTL_DSP_SET_RECSRC: + value = *arg; + if (value < 0 || value > 2) + return OSS_EINVAL; + REVO51_set_recsrc (devc, value); + return *arg = devc->recsrc; + break; + + case SNDCTL_DSP_GET_PLAYTGT: + case SNDCTL_DSP_SET_PLAYTGT: + return *arg = 0; + break; + + case SNDCTL_DSP_GET_PLAYTGT_NAMES: + return oss_encode_enum ((oss_mixer_enuminfo *) arg, portc->name, 0); + break; + + + default: + return OSS_EINVAL; + } +} + +static int +set_reclevel (envy24ht_devc * devc, int value) +{ + int left, right; + + left = value & 0xff; + right = (value >> 8) & 0xff; + + if (left > 0x98) + left = 0x98; + if (right > 0x98) + right = 0x98; + + value = left | (right << 8); + + devc->reclevel = value; + + REVO51_WriteSpiReg (devc, REVO51_AK5365, 4, left); + REVO51_WriteSpiReg (devc, REVO51_AK5365, 5, right); + + return value; +} + +static int +revo51_set_control (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24ht_devc *devc = mixer_devs[dev]->hw_devc; + + if (cmd == SNDCTL_MIX_READ) + switch (ctrl) + { + case 0: + return devc->mute; + + case 1: + return devc->recsrc; + + case 2: + return devc->reclevel; + + default: + return OSS_EINVAL; + } + + if (cmd == SNDCTL_MIX_WRITE) + switch (ctrl) + { + case 0: + value = !!value; + REVO51_Mute (devc, value); + return devc->mute; + + case 1: + if (value < 0 || value > 2) + return devc->recsrc; + REVO51_set_recsrc (devc, value); + return devc->recsrc; + + case 2: + return set_reclevel (devc, value); + + default: + return OSS_EINVAL; + } + + return OSS_EINVAL; +} + +static int +revo51_set_ak4358 (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24ht_devc *devc = mixer_devs[dev]->hw_devc; + + if (cmd == SNDCTL_MIX_READ) + { + if (ctrl < 0 || ctrl > 4) + return OSS_EIO; + + return devc->gains[ctrl]; + } + + if (cmd == SNDCTL_MIX_WRITE) + { + int left, right; + + left = value & 0xff; + right = (value >> 8) & 0xff; + + switch (ctrl) + { + case 0: /* Front */ + left = set_dac (devc, 0x04, left); + right = set_dac (devc, 0x05, right); + break; + + case 1: /* Center */ + left = set_dac (devc, 0x06, left); + right = left; + break; + + case 2: /* LFE */ + left = set_dac (devc, 0x07, left); + right = left; + break; + + case 3: /* Surround */ + left = set_dac (devc, 0x08, left); + left = set_dac (devc, 0x09, right); + break; + + case 4: /* Headphones */ + left = set_dac (devc, 0x0b, left); + left = set_dac (devc, 0x0c, right); + break; + + default: + return OSS_EINVAL; + } + + value = left | (right << 8); + return devc->gains[ctrl] = value; + } + + return OSS_EINVAL; +} + +static int +set_mongain (envy24ht_devc * devc, int reg, int value) +{ + if (value < 0) + value = 0; + if (value > 79) + value = 79; + + PT2258S_Set_Attn (devc, reg, value - 79); + return value; +} + +static int +revo51_set_monitor (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24ht_devc *devc = mixer_devs[dev]->hw_devc; + + if (cmd == SNDCTL_MIX_READ) + { + if (ctrl < 0 || ctrl > 3) + return OSS_EIO; + + return devc->monitor[ctrl]; + } + + if (cmd == SNDCTL_MIX_WRITE) + { + int left, right; + + left = value & 0xff; + right = (value >> 8) & 0xff; + + switch (ctrl) + { + case 0: /* Mic */ + left = set_mongain (devc, 0, left); + right = set_mongain (devc, 1, right); + break; + + case 1: /* Line */ + left = set_mongain (devc, 2, left); + right = set_mongain (devc, 3, right); + break; + + case 2: /* Aux */ + left = set_mongain (devc, 4, left); + right = set_mongain (devc, 5, right); + break; + + case 3: /* Mute */ + value = !!value; + PT2258S_Mute (devc, value); + return devc->monitor[3] = value; + break; + + default: + return OSS_EINVAL; + } + + value = left | (right << 8); + return devc->monitor[ctrl] = value; + } + + return OSS_EINVAL; +} + + /*ARGSUSED*/ static int +revo51_mixer_init (envy24ht_devc * devc, int dev, int g) +{ + int group = 0; + int err; + + if ((err = mixer_ext_create_control (dev, group, + 0, revo51_set_control, + MIXT_ONOFF, + "ENVY24_MUTE", 2, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 0, revo51_set_ak4358, + MIXT_STEREOSLIDER, + "ENVY24_FRONT", 0x7f, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 1, revo51_set_ak4358, + MIXT_SLIDER, + "ENVY24_CENTER", 0x7f, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 2, revo51_set_ak4358, + MIXT_SLIDER, + "ENVY24_LFE", 0x7f, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 3, revo51_set_ak4358, + MIXT_STEREOSLIDER, + "ENVY24_SURROUND", 0x7f, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 4, revo51_set_ak4358, + MIXT_STEREOSLIDER, + "ENVY24_HEADPH", 0x7f, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 2, revo51_set_control, + MIXT_STEREOSLIDER, + "ENVY24_REC", 0x98, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 1, revo51_set_control, + MIXT_ENUM, + "ENVY24_RECSRC", 3, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + mixer_ext_set_strings (dev, err, "mic line aux", 0); + + if ((group = mixer_ext_create_group (dev, g, "MONITOR")) < 0) + return group; + + if ((err = mixer_ext_create_control (dev, group, + 3, revo51_set_monitor, + MIXT_ONOFF, + "MON_MUTE", 2, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 0, revo51_set_monitor, + MIXT_STEREOSLIDER, + "ENVY24_MIC", 79, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 1, revo51_set_monitor, + MIXT_STEREOSLIDER, + "ENVY24_LINE", 79, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 2, revo51_set_monitor, + MIXT_STEREOSLIDER, + "ENVY24_AUX", 79, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + return 0; +} + +static void +revo51_card_init (envy24ht_devc * devc) +{ + + int i; + +#if 1 + OUTW (devc->osdev, 0xffff, devc->ccs_base + 0x18); /* GPIO direction */ + OUTW (devc->osdev, 0x0000, devc->ccs_base + 0x16); /* GPIO write mask */ + OUTW (devc->osdev, 0xffff, devc->ccs_base + 0x14); /* Initial bit state */ + + OUTB (devc->osdev, 0xff, devc->ccs_base + 0x1a); /* GPIO direction for bits 16:22 */ + OUTB (devc->osdev, 0x00, devc->ccs_base + 0x1f); /* GPIO mask for bits 16:22 */ + OUTB (devc->osdev, 0xff, devc->ccs_base + 0x1e); /* GPIO data for bits 16:22 */ +#endif + + memcpy (devc->channel_names, channel_names, sizeof (channel_names)); + AK4358_Init (devc); + AK5365_Init (devc); + PT2258_init (devc); + ADC_Set_Chan (devc, 0); /* TODO */ + REVO51_Set_48K_Mode (devc); + + for (i = 0; i < 5; i++) + devc->gains[i] = 0x7f7f; + for (i = 0; i < 3; i++) + devc->monitor[i] = 79 | (79 << 8); + devc->monitor[3] = 0; /* Unmuted */ + + set_reclevel (devc, 0x7f7f); /* +0 dB */ + + REVO51_Mute (devc, 0); +} + +envy24ht_auxdrv_t envy24ht_revo51_auxdrv = { + revo51_card_init, + revo51_mixer_init, + revo51_set_rate, + NULL, + NULL, + NULL, /* revo51_private1 */ + revo51_audio_ioctl +}; diff --git a/kernel/drv/oss_envy24ht/envy24ht_revo71.c b/kernel/drv/oss_envy24ht/envy24ht_revo71.c new file mode 100644 index 0000000..fd3a9bb --- /dev/null +++ b/kernel/drv/oss_envy24ht/envy24ht_revo71.c @@ -0,0 +1,338 @@ +/* + * Purpose: Low level routines for M Audio Revolution 7.1 + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include "oss_envy24ht_cfg.h" + +#include "spdif.h" +#include "envy24ht.h" + +#define BIT(x) (1<<(x)) + +#define CCLK (1<<1) +#define CDIN (1<<2) /* Currently not connected */ +#define CDTI (1<<3) +#define CSDIG (1<<4) +#define CSN1 (1<<5) +#define CSN2 (1<<6) + +#define WRITEMASK (CSN1|CSN2|CDTI|CCLK) + +#define DAC1 0x01 /* 2ch Front DAC (AK4381) */ +#define DAC2 0x03 /* 6ch Surround DAC (AK4355) */ + + +static void +writereg (envy24ht_devc * devc, int csn, int chip, unsigned char reg, + unsigned char val) +{ + int i; + unsigned short v, tmp; + + tmp = INW (devc->osdev, devc->ccs_base + 0x14) & ~(csn | CCLK | CDTI); + OUTW (devc->osdev, tmp, devc->ccs_base + 0x14); + + reg = (reg & 0x1f) | 0x20 | (chip << 6); /* Chip address (variable), write */ + /* Address bits */ + + for (i = 7; i >= 0; i--) + { + v = (reg & (1 << i)) ? CDTI : 0; + OUTW (devc->osdev, v | tmp, devc->ccs_base + 0x14); /* Tack */ + OUTW (devc->osdev, v | CCLK | tmp, devc->ccs_base + 0x14); /* Tick */ + } + + /* Data bits */ + + for (i = 7; i >= 0; i--) + { + v = (val & (1 << i)) ? CDTI : 0; + OUTW (devc->osdev, v | tmp, devc->ccs_base + 0x14); /* Tack */ + OUTW (devc->osdev, v | CCLK | tmp, devc->ccs_base + 0x14); /* Tick */ + } + + OUTW (devc->osdev, v | tmp, devc->ccs_base + 0x14); /* Tack */ + OUTW (devc->osdev, tmp | csn | CDTI | CCLK, devc->ccs_base + 0x14); /* Release */ + +} + +static void +writedac1 (envy24ht_devc * devc, unsigned char reg, unsigned char data) +{ + writereg (devc, CSN1, DAC1, reg, data); + devc->dac1val[reg] = data; +} + +static void +writedac2 (envy24ht_devc * devc, unsigned char reg, unsigned char data) +{ + writereg (devc, CSN2, DAC2, reg, data); + devc->dac2val[reg] = data; +} + +static void +revo71_mute (envy24ht_devc * devc, int mute) +{ + if (mute) + { + writedac1 (devc, 1, devc->dac1val[1] | BIT (0)); + writedac2 (devc, 1, devc->dac2val[1] | BIT (1)); + oss_udelay (1000); +/* OUTB (devc->osdev, INB (devc->osdev, devc->ccs_base + 0x1e) & ~BIT(22-16), */ +/* devc->ccs_base + 0x1e); */ + } + else + { +/* OUTB (devc->osdev, INB (devc->osdev, devc->ccs_base + 0x1e) | ~BIT(22-16), */ +/* devc->ccs_base + 0x1e); */ + oss_udelay (1000); + writedac1 (devc, 1, devc->dac1val[1] & ~BIT (0)); + writedac2 (devc, 1, devc->dac2val[1] & ~BIT (1)); + } +} + +static void +revo71_card_init (envy24ht_devc * devc) +{ + int i; + + OUTL (devc->osdev, WRITEMASK | 0x400000, devc->ccs_base + 0x18); /* GPIO direction */ + OUTW (devc->osdev, ~WRITEMASK, devc->ccs_base + 0x16); /* GPIO write mask */ + OUTB (devc->osdev, 0x40, devc->ccs_base + 0x1f); + OUTW (devc->osdev, WRITEMASK, devc->ccs_base + 0x14); /* Initial bit state */ + + OUTB (devc->osdev, 0xff, devc->ccs_base + 0x1a); /* GPIO direction for bits 16:22 */ + OUTB (devc->osdev, 0x00, devc->ccs_base + 0x1f); /* GPIO mask for bits 16:22 */ + OUTB (devc->osdev, 0xff, devc->ccs_base + 0x1e); /* GPIO data for bits 16:22 */ + +#if 0 + for (i = 0; i < 7; i++) + { + OUTW (devc->osdev, 1 << i, devc->ccs_base + 0x14); /* Test bit */ + OUTW (devc->osdev, 0, devc->ccs_base + 0x14); /* Test bit */ + } +#endif + + OUTW (devc->osdev, WRITEMASK, devc->ccs_base + 0x14); /* Initial bit state */ + oss_udelay (10); + + revo71_mute (devc, 1); +/* + * Init front DAC (AK4381) + */ + writedac1 (devc, 0x00, 0x0c); + writedac1 (devc, 0x01, 0x03); + writedac1 (devc, 0x02, 0x00); + + writedac1 (devc, 0x03, 0xff); /* Initial volume */ + writedac1 (devc, 0x04, 0xff); /* Initial volume */ + + writedac1 (devc, 0x00, 0x0f); +/* + * Init surround DAC (AK4355) + */ + writedac2 (devc, 0x01, 0x02); + writedac2 (devc, 0x00, 0x06); + oss_udelay (10); + writedac2 (devc, 0x02, 0x0e); + writedac2 (devc, 0x03, 0x01); + + for (i = 4; i < 10; i++) + writedac2 (devc, i, 0xff); /* Initial volumes */ + writedac2 (devc, 0x0a, 0x00); + + writedac2 (devc, 0x01, 0x01); + revo71_mute (devc, 0); +} + +static void +revo71_set_rate (envy24ht_devc * devc) +{ + int rate = devc->speed, i; + unsigned char tmp; + + if (devc->speed == devc->prev_speed) + return; + devc->prev_speed = devc->speed; + + revo71_mute (devc, 1); + + /* Pulse the PRST# signal to reset converters */ + OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x05) | 0x80, + devc->mt_base + 0x05); + oss_udelay (5000); + OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x05) & ~0x80, + devc->mt_base + 0x05); + oss_udelay (5000); + + tmp = 0x03; + if (rate <= 48000) + tmp |= 0x00; + else + { + if (rate <= 96000) + tmp |= 0x04; + else + tmp |= 0x08; + } + + /* Front DAC */ + + writedac1 (devc, 0, 0x0c); + writedac1 (devc, 1, tmp); + writedac1 (devc, 2, 0x00); + writedac1 (devc, 3, devc->dac1val[3]); + writedac1 (devc, 4, devc->dac1val[4]); + writedac1 (devc, 0, 0x0f); + + /* Surround DAC */ + writedac2 (devc, 1, 0x02); + writedac2 (devc, 0, 0x06); + + tmp = 0x0e; + + if (devc->speed > 60000) + { + if (devc->speed > 120000) + tmp |= 0x20; + else + tmp |= 0x10; + } + writedac2 (devc, 2, tmp); + writedac2 (devc, 3, 0x01); + + for (i = 4; i < 10; i++) + writedac2 (devc, i, devc->dac2val[i]); + + writedac2 (devc, 0xa, 0x00); + writedac2 (devc, 1, 0x03); + + revo71_mute (devc, 0); +} + +static int +revo71_set_akm (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24ht_devc *devc = mixer_devs[dev]->hw_devc; + + if (cmd == SNDCTL_MIX_READ) + { + if (ctrl < 0 || ctrl > 4) + return OSS_EIO; + + return devc->gains[ctrl]; + } + + if (cmd == SNDCTL_MIX_WRITE) + { + int left, right; + + left = value & 0xff; + right = (value >> 8) & 0xff; + + switch (ctrl) + { + case 0: /* Front */ + writedac1 (devc, 0x03, left); + writedac1 (devc, 0x04, right); + break; + + case 1: /* Rear */ + writedac2 (devc, 0x06, left); + writedac2 (devc, 0x07, right); + break; + + case 2: /* Center */ + writedac2 (devc, 0x04, left); + right = left; + break; + + case 3: /* LFE */ + writedac2 (devc, 0x05, left); + right = left; + break; + + case 4: /* Surround */ + writedac2 (devc, 0x08, left); + writedac2 (devc, 0x09, right); + break; + + default: + return OSS_EINVAL; + } + + value = left | (right << 8); + return devc->gains[ctrl] = value; + } + + return OSS_EINVAL; +} + +static int +revo71_mixer_init (envy24ht_devc * devc, int dev, int group) +{ + int i, err; + + for (i = 0; i < 6; i++) + devc->gains[i] = 0xffff; + + if ((group = mixer_ext_create_group (dev, 0, "ENVY24HT_GAIN")) < 0) + return group; + + if ((err = mixer_ext_create_control (dev, group, + 0, revo71_set_akm, + MIXT_STEREOSLIDER, + "GAIN_FRONT", 255, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 1, revo71_set_akm, + MIXT_STEREOSLIDER, + "GAIN_REAR", 255, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 2, revo71_set_akm, + MIXT_MONOSLIDER, + "GAIN_CENTER", 255, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 3, revo71_set_akm, + MIXT_MONOSLIDER, + "GAIN_LFE", 255, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 4, revo71_set_akm, + MIXT_STEREOSLIDER, + "GAIN_surround", 255, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + return 0; +} + +envy24ht_auxdrv_t envy24ht_revo71_auxdrv = { + revo71_card_init, + revo71_mixer_init, + revo71_set_rate, + NULL, + NULL, + NULL, /* revo71_private1 */ +}; diff --git a/kernel/drv/oss_envy24ht/envy24ht_via.c.save b/kernel/drv/oss_envy24ht/envy24ht_via.c.save new file mode 100644 index 0000000..49d4199 --- /dev/null +++ b/kernel/drv/oss_envy24ht/envy24ht_via.c.save @@ -0,0 +1,324 @@ +/* + * Purpose: Low level routines for VIA's Envy24HT reference board (AKM codec) + * + * Notice: + * + * This driver is currently disabled because no known real world device + * is based on this design. To enable this driver againa just rename it to + * envy24ht_via.c and edit the models[] table in envy24ht.c. + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include "oss_envy24ht_cfg.h" + +#include "spdif.h" +#include "envy24ht.h" + +#define AKM_ADDRESS 0x10 + +static unsigned char +i2c_read (envy24ht_devc * devc, unsigned char addr, unsigned char pos) +{ + int i; + unsigned char data; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + OUTB (devc->osdev, pos, devc->ccs_base + 0x11); /* Offset */ + OUTB (devc->osdev, addr << 1, devc->ccs_base + 0x10); /* Read address */ + + for (i = 0; i < 2000; i++) + { + unsigned char status = INB (devc->osdev, devc->ccs_base + 0x13); + if (!(status & 1)) + break; + + } + + oss_udelay (1); + data = INB (devc->osdev, devc->ccs_base + 0x12); + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + + return data; +} + +static void +i2c_write (envy24ht_devc * devc, unsigned char addr, unsigned char pos, + unsigned char data) +{ + int i; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + OUTB (devc->osdev, pos, devc->ccs_base + 0x11); /* Offset */ + OUTB (devc->osdev, data, devc->ccs_base + 0x12); /* Data */ + OUTB (devc->osdev, (addr << 1) | 1, devc->ccs_base + 0x10); /* Write address */ + + for (i = 0; i < 2000; i++) + { + unsigned char status = INB (devc->osdev, devc->ccs_base + 0x13); + if (!(status & 1)) + break; + + } + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); +} + +static void +viaref_card_init (envy24ht_devc * devc) +{ + int addr = AKM_ADDRESS; + + if (devc->codec_type != CODEC_I2S) + return; + + OUTW (devc->osdev, 0x000f, devc->ccs_base + 0x14); /* GPIO */ + + oss_udelay (1000); + + i2c_write (devc, addr, 0, 0x00); + i2c_write (devc, addr, 0, 0x0f); + i2c_write (devc, addr, 1, 0x00); + i2c_write (devc, addr, 2, 0x01); + i2c_write (devc, addr, 5, 0x07); + i2c_write (devc, addr, 6, 0x00); + i2c_write (devc, addr, 7, 0x00); + + i2c_write (devc, addr, 6, 0x00); + i2c_write (devc, addr, 7, 0x00); + i2c_write (devc, addr, 8, 0x00); + i2c_write (devc, addr, 9, 0x00); + i2c_write (devc, addr, 10, 0x00); + i2c_write (devc, addr, 11, 0x00); + + devc->recsrc = 0; + +#if 0 + cmn_err(CE_CONT, "Regs="); + for (i = 0; i < 0x18; i++) + { + cmn_err(CE_CONT, "%02x ", i2c_read (devc, addr, i)); + } + cmn_err(CE_CONT, "\n"); +#endif +} + +static int +viaref_set_akm (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24ht_devc *devc = mixer_devs[dev]->hw_devc; + int left, right, val; + unsigned char tmp, tmp2, old, old2; + + if (cmd == SNDCTL_MIX_READ) + { + switch (ctrl) + { + /* Output gain controls */ + case 0: + case 1: + case 2: + case 3: + return devc->gains[ctrl]; + + case 10: /* Loopback enable */ + return !!(i2c_read (devc, AKM_ADDRESS, 0x01) & 0x30); + + case 11: /* codec.recsrc (ANALOG OPTICAL COAX CD AUX) */ + return devc->recsrc; + break; + + default: + return OSS_EINVAL; + } + } + + if (cmd == SNDCTL_MIX_WRITE) + { + oss_native_word flags; + left = value & 0xff; + right = (value >> 8) & 0xff; + val = left | (right << 8); + + switch (ctrl) + { + case 0: + left = 0xff - left; + right = 0xff - right; + i2c_write (devc, AKM_ADDRESS, 0x06, left); + i2c_write (devc, AKM_ADDRESS, 0x07, right); + return devc->gains[ctrl] = val; + + case 1: + left = 0xff - left; + right = 0xff - right; + i2c_write (devc, AKM_ADDRESS, 0x08, left); + i2c_write (devc, AKM_ADDRESS, 0x09, right); + return devc->gains[ctrl] = val; + + case 2: + val = left | (left << 8); + left = 0xff - left; + i2c_write (devc, AKM_ADDRESS, 0x0a, left); + return devc->gains[ctrl] = val; + + case 3: + val = left | (left << 8); + left = 0xff - left; + i2c_write (devc, AKM_ADDRESS, 0x0b, left); + return devc->gains[ctrl] = val; + + case 10: /* Loopback enable */ + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + old = tmp = i2c_read (devc, AKM_ADDRESS, 0x01); + value = !!value; + if (value) + tmp |= 0x10; + else + tmp &= ~0x30; + + if (tmp != old) + { + i2c_write (devc, AKM_ADDRESS, 0x01, tmp); + } + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return value; + + case 11: /* codec.recsrc (ANALOG OPTICAL COAX CD AUX) */ + if (value < 0 || value > 4) + return devc->recsrc; + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + old = tmp = i2c_read (devc, AKM_ADDRESS, 0x01); + old2 = tmp2 = i2c_read (devc, AKM_ADDRESS, 0x02); + + tmp &= ~0x0c; + tmp2 &= ~0x03; + + switch (value) + { + case 0: /* Analog */ + tmp2 |= 0x01; + break; + + case 1: /* Optical (RX2) */ + tmp |= 0x04; + tmp2 |= 0x00; + break; + + case 2: /* Coax (RX1) */ + tmp |= 0x00; + tmp2 |= 0x00; + break; + + case 3: /* CD digital input (RX3) */ + tmp |= 0x08; + tmp2 |= 0x00; + break; + + case 4: /* Unused digital input (RX4) */ + tmp |= 0x08; + tmp2 |= 0x00; + break; + } + + if (tmp != old) + i2c_write (devc, AKM_ADDRESS, 0x01, tmp); + if (tmp2 != old2) + i2c_write (devc, AKM_ADDRESS, 0x02, tmp2); + + devc->recsrc = value; + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return devc->recsrc; + break; + + } + return OSS_EIO; + } + + return OSS_EIO; +} + +static int +viaref_mixer_init (envy24ht_devc * devc, int dev, int group) +{ + int i, err; + if (devc->codec_type != CODEC_I2S) + return 0; + + for (i = 0; i < 4; i++) + devc->gains[i] = 0xffff; + + if ((group = mixer_ext_create_group (dev, 0, "ENVY24HT_GAIN")) < 0) + return group; + + if ((err = mixer_ext_create_control (dev, group, + 0, viaref_set_akm, + MIXT_STEREOSLIDER, + "GAIN_FRONT", 255, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 1, viaref_set_akm, + MIXT_STEREOSLIDER, + "GAIN_REAR", 255, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 2, viaref_set_akm, + MIXT_MONOSLIDER, + "GAIN_CENTER", 255, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 3, viaref_set_akm, + MIXT_MONOSLIDER, + "GAIN_LFE", 255, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((group = mixer_ext_create_group (dev, 0, "ENVY24HT_CODEC")) < 0) + return group; + + if ((err = mixer_ext_create_control (dev, group, + 11, viaref_set_akm, + MIXT_ENUM, + "CODEC_RECSRC", 5, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 10, viaref_set_akm, + MIXT_ONOFF, + "CODEC_LOOPBACK", 2, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + return 0; +} + +#if 0 +static int +viaref_private1 (envy24ht_devc * devc, int value) +{ + i2c_write (devc, AKM_ADDRESS, (value >> 8) & 0xff, value & 0xff); + return 0; +} +#endif + +envy24ht_auxdrv_t envy24ht_viaref_auxdrv = { + viaref_card_init, + viaref_mixer_init +}; diff --git a/kernel/drv/oss_envy24ht/oss_envy24ht.c b/kernel/drv/oss_envy24ht/oss_envy24ht.c new file mode 100644 index 0000000..11fb75b --- /dev/null +++ b/kernel/drv/oss_envy24ht/oss_envy24ht.c @@ -0,0 +1,2411 @@ +/* + * Purpose: VIA ENVY24HT chipset driver. + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include "oss_envy24ht_cfg.h" +#include <oss_pci.h> + +#define SUPPORTED_FORMAT AFMT_S32_LE + +#include <spdif.h> +#include "envy24ht.h" + +/* extern envy24ht_auxdrv_t envy24ht_viaref_auxdrv; */ +extern envy24ht_auxdrv_t envy24ht_ac97_auxdrv; +extern envy24ht_auxdrv_t envy24ht_revo51_auxdrv; +extern envy24ht_auxdrv_t envy24ht_revo71_auxdrv; +extern envy24ht_auxdrv_t envy24ht_aureon_auxdrv; +extern envy24ht_auxdrv_t envy24ht_julia_auxdrv; +extern envy24ht_auxdrv_t envy24ht_ap192_auxdrv; + +#define OUTCH_NAMES "front c/l side rear" +static char channel_names[4][10] = { + "front", + "c/l", + "side", + "rear" +}; + +static card_spec models[] = { + {0x17241412, "Generic Envy24PT motherboard audio", 6, 2, + MF_SPDIFOUT | MF_ENVY24PT, &envy24ht_ac97_auxdrv}, + {0xf641270f, "Chaintech ZNF3-150", 6, 2, + MF_SPDIFOUT | MF_ENVY24PT, &envy24ht_ac97_auxdrv}, + {0x2723270f, "Chaintech 9CJS", 6, 2, + MF_SPDIFOUT | MF_ENVY24PT, &envy24ht_ac97_auxdrv}, + {0x50361297, "Shuttle SN25P", 6, 2, + MF_SPDIFOUT | MF_ENVY24PT, &envy24ht_ac97_auxdrv}, + {0x020010b0, "Gainward Hollywood Envy24HT-S", 6, 2, + MF_SPDIFOUT | MF_ENVY24PT, &envy24ht_ac97_auxdrv}, + {0x36311412, "M Audio Revolution 5.1", 6, 2, + MF_SPDIFOUT | MF_192K, &envy24ht_revo51_auxdrv}, + {0x36301412, "M Audio Revolution 7.1", 8, 2, + MF_SPDIFOUT | MF_192K, &envy24ht_revo71_auxdrv}, + {SSID_AUREON_SPACE, "Terratec Aureon 7.1 Space", 8, 2, + MF_SPDIFOUT | MF_192K | MF_NOAC97, &envy24ht_aureon_auxdrv}, + {SSID_AUREON_UNIVERSE, "Terratec Aureon 7.1 Universe", 8, 2, + MF_SPDIFOUT | MF_192K | MF_NOAC97, &envy24ht_aureon_auxdrv}, + {SSID_AUREON_SKY, "Terratec Aureon 7.1 Sky", 8, 2, + MF_SPDIFOUT | MF_192K | MF_NOAC97, &envy24ht_aureon_auxdrv}, + {SSID_PRODIGY71, "Audiotrak Prodigy 7.1", 8, 2, + MF_SPDIFOUT | MF_192K | MF_NOAC97, &envy24ht_aureon_auxdrv}, + {SSID_JULIA, "Ego Systems Juli@", 2, 2, + MF_SPDIFOUT | MF_SPDIFIN | MF_192K | MF_NOAC97 | MF_MIDI, + &envy24ht_julia_auxdrv}, + {SSID_PHASE28, "Terratec PHASE 28", 8, 2, + MF_SPDIFOUT | MF_192K | MF_NOAC97 | MF_MIDI, &envy24ht_aureon_auxdrv}, + {SSID_AP192, "M-Audio Audiophile 192", 2, 2, + MF_SPDIFOUT | MF_SPDIFIN | MF_192K | MF_NOAC97 | MF_MIDI, + &envy24ht_ap192_auxdrv}, + {0x24031412, "VIA Vinyl Tremor Audio", 6, 2, + MF_SPDIFOUT | MF_ENVY24PT, &envy24ht_ac97_auxdrv}, + /* XXX Do a separate auxdrv, to adjust for Envy24HT-S and other differences. */ + {SSID_PRODIGYHD2, "Audiotrak Prodigy HD2", 2, 2, + MF_SPDIFOUT | MF_192K | MF_NOAC97, &envy24ht_ap192_auxdrv}, + {SSID_PRODIGYHD2_ADE, "Audiotrak Prodigy HD2 Advance DE", 2, 2, + MF_SPDIFOUT | MF_192K | MF_NOAC97, &envy24ht_ap192_auxdrv}, + /* {0x17241412, "VIA Envy24HT reference design", 6, 2, */ + /* MF_SPDIFOUT | MF_MIDI, &envy24ht_viaref_auxdrv}, */ + {0} +}; + +static struct speed_sel speed_tab[] = { + { + 8000, 0x06} + , + { + 9600, 0x03} + , + { + 11025, 0x0a} + , + { + 12000, 0x02} + , + { + 16000, 0x05} + , + { + 22050, 0x09} + , + { + 24000, 0x01} + , + { + 32000, 0x04} + , + { + 44100, 0x08} + , + { + 48000, 0x00} + , + { + 64000, 0x0f} + , + { + 88200, 0x0b} + , + { + 96000, 0x07} + , + { + 176400, 0x0c} + , + { + 192000, 0x0e} + , + {-1, 0x10} + , +}; + +static const envy24ht_auxdrv_t dummy_auxdrv = { NULL }; + +static int +ac97_read (void *devc_, int wAddr) +{ + envy24ht_devc *devc = devc_; + oss_native_word flags; + int n = 0, dat; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + while (n++ < 10000 && INB (devc->osdev, devc->mt_base + 0x05) & 0x30); + OUTB (devc->osdev, wAddr, devc->mt_base + 0x04); + OUTB (devc->osdev, 0x10, devc->mt_base + 0x05); /* Codec read */ + + n = 0; + while (n++ < 10000 && INB (devc->osdev, devc->mt_base + 0x05) & 0x10); + dat = INW (devc->osdev, devc->mt_base + 0x06); + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + + return dat; +} + +static int +ac97_write (void *devc_, int wAddr, int wData) +{ + envy24ht_devc *devc = devc_; + oss_native_word flags; + int n = 0; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + while (n++ < 10000 && INB (devc->osdev, devc->mt_base + 0x05) & 0x30); + + OUTB (devc->osdev, wAddr, devc->mt_base + 0x04); + OUTW (devc->osdev, wData, devc->mt_base + 0x06); + OUTB (devc->osdev, 0x20, devc->mt_base + 0x05); /* Codec write */ + + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + + return 0; +} + +static __inline__ int +input_avail (envy24ht_devc * devc) +{ + unsigned char status; + + status = INB (devc->osdev, devc->ccs_base + 0x0b); + return status & 0x1f; /* Number of bytes in RX queue */ +} + +static __inline__ int +output_ready (envy24ht_devc * devc) +{ + unsigned char status; + + status = INB (devc->osdev, devc->ccs_base + 0x0a); + return (31 - (status & 0x1f)) > 0; /* Number of free bytes in TX queue */ +} + +static __inline__ void +midi_cmd (envy24ht_devc * devc, unsigned char cmd) +{ + OUTB (devc->osdev, cmd, devc->ccs_base + 0x0d); +} + +static __inline__ int +midi_read (envy24ht_devc * devc) +{ + return INB (devc->osdev, devc->ccs_base + 0x0c); +} + +static __inline__ void +midi_write (envy24ht_devc * devc, unsigned char byte) +{ + OUTB (devc->osdev, byte, devc->ccs_base + 0x0c); +} + +static void reset_midi (envy24ht_devc * devc); +static void enter_uart_mode (envy24ht_devc * devc); + +static void +midi_input_loop (envy24ht_devc * devc) +{ + int n = 0; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + while (input_avail (devc) && n++ < 33) + { + unsigned char c = midi_read (devc); + + devc->input_byte = c; + if (devc->midi_opened & OPEN_READ && devc->midi_input_intr) + devc->midi_input_intr (devc->midi_dev, c); + } + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); +} + +static void +midiintr (envy24ht_devc * devc) +{ + int status; + + status = INB (devc->osdev, devc->ccs_base + 0x02); + if (status & 0x80) + midi_input_loop (devc); + if ((status & 0x20)) + { + OUTB (devc->osdev, INB (devc->osdev, devc->ccs_base + 0x01) | 0x20, + devc->ccs_base + 0x01); + +#if 0 + if (devc->midi_output_intr) + devc->midi_output_intr (devc->midi_dev); +#endif + } +} + +/*ARGSUSED*/ +static int +midi_open (int dev, int mode, oss_midi_inputbyte_t inputbyte, + oss_midi_inputbuf_t inputbuf, oss_midi_outputintr_t outputintr) +{ + envy24ht_devc *devc = (envy24ht_devc *) midi_devs[dev]->devc; + int n = 0, tmp; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + + if (devc->midi_opened & mode) + { + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return OSS_EBUSY; + } + devc->midi_opened |= mode; + + if (mode & OPEN_READ) + { + while (n++ < 33 && input_avail (devc)) + midi_read (devc); + + devc->midi_input_intr = inputbyte; + devc->midi_output_intr = outputintr; + } + enter_uart_mode (devc); + tmp = INB (devc->osdev, devc->ccs_base + 0x01); + if (mode & OPEN_READ) + tmp &= ~0x80; + OUTB (devc->osdev, tmp, devc->ccs_base + 0x01); + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + + return 0; +} + +static void +midi_close (int dev, int mode) +{ + envy24ht_devc *devc = (envy24ht_devc *) midi_devs[dev]->devc; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + reset_midi (devc); + enter_uart_mode (devc); + reset_midi (devc); + OUTB (devc->osdev, INB (devc->osdev, devc->ccs_base + 0x01) | 0xa0, + devc->ccs_base + 0x01); + devc->midi_opened &= ~mode; + devc->midi_input_intr = NULL; + devc->midi_output_intr = NULL; + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); +} + +static int +midi_out (int dev, unsigned char midi_byte) +{ + envy24ht_devc *devc = (envy24ht_devc *) midi_devs[dev]->devc; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + if (!output_ready (devc)) + { + OUTB (devc->osdev, INB (devc->osdev, devc->ccs_base + 0x01) & ~0x20, + devc->ccs_base + 0x01); + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return 0; + } + + midi_write (devc, midi_byte); + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return 1; +} + +/*ARGSUSED*/ +static int +midi_ioctl (int dev, unsigned cmd, ioctl_arg arg) +{ + return OSS_EINVAL; +} + +static midi_driver_t envy24ht_midi_driver = { + midi_open, + midi_close, + midi_ioctl, + midi_out +}; + +static void +enter_uart_mode (envy24ht_devc * devc) +{ + devc->input_byte = 0; + midi_cmd (devc, 1); +} + +void +attach_midi (envy24ht_devc * devc) +{ + char name[128]; + enter_uart_mode (devc); + + sprintf (name, "%s input", devc->model_data->product); + devc->midi_dev = oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "ENVY24HT", name, &envy24ht_midi_driver, sizeof (midi_driver_t), + MFLAG_INPUT, devc, devc->osdev); + sprintf (name, "%s output", devc->model_data->product); + devc->midi_dev = oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "ENVY24HT", name, &envy24ht_midi_driver, sizeof (midi_driver_t), + MFLAG_OUTPUT, devc, devc->osdev); + devc->midi_opened = 0; + devc->midi_attached = 1; +} + +static void +reset_midi (envy24ht_devc * devc) +{ + /* + * Send the RESET command. Try again if no success at the first time. + */ + + midi_cmd (devc, 0); +} + +void +unload_midi (envy24ht_devc * devc) +{ + if (devc->midi_attached) + reset_midi (devc); + devc->midi_attached = 0; +} + +static int +envy24htintr (oss_device_t * osdev) +{ + envy24ht_devc *devc = osdev->devc; + int port, status, mt_status, serviced = 0; + + status = INB (devc->osdev, devc->ccs_base + 0x02); + if (status != 0) + serviced = 1; + + if (status & 0xa0) + midiintr (devc); + +/* + * Handle audio interrupts + */ + mt_status = INB (devc->osdev, devc->mt_base + 0x00); + + if (mt_status & 0x08) /* FIFO underrun/overrun */ + { + OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x1A), + devc->mt_base + 0x1A); + serviced = 1; + } + + if ((status & 0x10) || mt_status != 0) + { + + for (port = 0; port < devc->nr_outdevs; port++) + { + envy24ht_portc *portc = &devc->play_portc[port]; + + if (mt_status & portc->mask) + { + oss_audio_outputintr (portc->dev, 1); + } + } + + for (port = 0; port < devc->nr_indevs; port++) + { + envy24ht_portc *portc = &devc->rec_portc[port]; + + if (mt_status & portc->mask) + { + oss_audio_inputintr (portc->dev, 0); + } + } + } + OUTB (devc->osdev, mt_status, devc->mt_base + 0x00); + OUTB (devc->osdev, status, devc->ccs_base + 0x02); + + return serviced; +} + +/*ARGSUSED*/ +static int +envy24ht_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg) +{ + return OSS_EINVAL; +} + +static mixer_driver_t envy24ht_mixer_driver = { + envy24ht_mixer_ioctl +}; + +static int +envy24ht_set_route (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24ht_devc *devc = mixer_devs[dev]->hw_devc; + unsigned int tmp; + oss_native_word flags; + + if (ctrl < 0 || ctrl > 8) + return OSS_EINVAL; + + if (cmd == SNDCTL_MIX_READ) + { + tmp = INL (devc->osdev, devc->mt_base + 0x2c); + + switch (ctrl) + { + case 0: + tmp >>= 8; + break; + case 2: + tmp >>= 11; + break; + case 4: + tmp >>= 14; + break; + case 6: + tmp >>= 17; + break; + } + + tmp = (tmp & 0x03) >> 1; + + if (tmp == 3) + return 2; + return tmp; + } + else if (cmd == SNDCTL_MIX_WRITE) + { + int left_shift = 0, right_shift = 0; + + static const unsigned char cnv_tab[3] = { 0x00, 0x02, 0x06 }; + if (value < 0 || value > 2) + return OSS_EINVAL; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + tmp = INL (devc->osdev, devc->mt_base + 0x2c); + + switch (ctrl) + { + case 0: + left_shift = 8; + right_shift = 20; + break; + case 2: + left_shift = 11; + right_shift = 23; + break; + case 4: + left_shift = 14; + right_shift = 26; + break; + case 6: + left_shift = 17; + right_shift = 29; + break; + case 8: + left_shift = 0; + right_shift = 3; + break; + } + + tmp &= ~(0x7 << left_shift); + tmp &= ~(0x7 << right_shift); + tmp |= cnv_tab[value] << left_shift; + if (ctrl != 8) + tmp |= (cnv_tab[value] + 1) << right_shift; + else + tmp |= cnv_tab[value] << right_shift; + + OUTL (devc->osdev, tmp, devc->mt_base + 0x2c); + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + + return value; + } + return OSS_EINVAL; +} + +static int +read_peak (envy24ht_devc * devc, int ch) +{ + int tmp; + + if (ch >= 22) + return 0; + + OUTB (devc->osdev, ch, devc->mt_base + 0x3e); + tmp = INB (devc->osdev, devc->mt_base + 0x3f); + + return tmp; +} + +/*ARGSUSED*/ +static int +envy24ht_get_peak (int dev, int ctrl, unsigned int cmd, int value) +{ + static const unsigned char peak_cnv[256] = { + 0, 18, 29, 36, 42, 47, 51, 54, 57, 60, 62, 65, 67, 69, 71, 72, + 74, 75, 77, 78, 79, 81, 82, 83, 84, 85, 86, 87, 88, 89, 89, 90, + 91, 92, 93, 93, 94, 95, 95, 96, 97, 97, 98, 99, 99, 100, 100, 101, + 101, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, + 108, + 109, 109, 110, 110, 110, 111, 111, 111, 112, 112, 113, 113, 113, 114, 114, + 114, + 115, 115, 115, 115, 116, 116, 116, 117, 117, 117, 118, 118, 118, 118, 119, + 119, + 119, 119, 120, 120, 120, 121, 121, 121, 121, 122, 122, 122, 122, 122, 123, + 123, + 123, 123, 124, 124, 124, 124, 125, 125, 125, 125, 125, 126, 126, 126, 126, + 126, + 127, 127, 127, 127, 127, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, + 130, + 130, 130, 130, 130, 130, 131, 131, 131, 131, 131, 131, 132, 132, 132, 132, + 132, + 132, 133, 133, 133, 133, 133, 133, 134, 134, 134, 134, 134, 134, 134, 135, + 135, + 135, 135, 135, 135, 135, 136, 136, 136, 136, 136, 136, 136, 137, 137, 137, + 137, + 137, 137, 137, 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, + 139, + 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, + 141, + 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 142, 142, 142, 143, 143, + 143, + 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, 144, + 144, + }; + + envy24ht_devc *devc = mixer_devs[dev]->hw_devc; + + int i, orign, n = -1, left = 0, right = 0; + + for (i = 0; i < 12 && n == -1; i++) + if (ctrl & (1 << i)) + n = i; + + if (n == -1) + return OSS_EINVAL; + + orign = n; + if (ctrl & 0x80000000) + n += 10; /* Recording stream */ + + if (cmd == SNDCTL_MIX_READ) + { + left = read_peak (devc, n); + if (ctrl & (1 << (orign + 1))) /* Stereo mode? */ + right = read_peak (devc, n + 1); + else + right = left; + + left = peak_cnv[left]; + right = peak_cnv[right]; + return left | (right << 8); + } + + return OSS_EINVAL; +} + +/*ARGSUSED*/ +static int +create_peak_mixer (int dev, envy24ht_devc * devc, int root) +{ + int i, mask = devc->outportmask, group, err, num, skip; + int nc = devc->nr_play_channels; + char tmp[64]; + + if ((group = mixer_ext_create_group (dev, 0, "ENVY24_PEAK")) < 0) + return group; + + skip = 2; + + for (i = 0; i < nc; i += skip) + { + + num = 1 << i; + if (!(mask & num)) + continue; /* Not present */ + + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDIFOUT"); + else + sprintf (tmp, devc->channel_names[i / 2], i + 1, i + 2); + + num |= 1 << (i + 1); + if ((err = mixer_ext_create_control (dev, group, + num, envy24ht_get_peak, + MIXT_STEREOPEAK, + tmp, 144, + MIXF_READABLE | MIXF_DECIBEL)) < + 0) + return err; + } + } + + mask = devc->inportmask; + for (i = 0; i < devc->nr_rec_channels; i += skip) + { + + num = 1 << i; + if (!(mask & num)) + continue; /* Not present */ + + num |= 0x80000000; /* Input flag */ + + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDIFIN"); + else + strcpy (tmp, "ENVY24_IN"); + + num |= 1 << (i + 1); + if ((err = mixer_ext_create_control (dev, group, + num, envy24ht_get_peak, + MIXT_STEREOPEAK, + tmp, 144, + MIXF_READABLE | MIXF_DECIBEL)) < + 0) + return err; + } + } + + return 0; +} + +/*ARGSUSED*/ +static int +create_rout_mixer (int dev, envy24ht_devc * devc, int root) +{ + int i, mask = devc->outportmask, group, ret, num; + char *name; + + if ((group = + mixer_ext_create_group_flags (dev, 0, "ENVY24_ROUTE", MIXF_FLAT)) < 0) + return group; + + for (i = 0; i < 8; i += 2) + { + + num = 1 << i; + if (!(mask & num)) + continue; /* Not present */ + + name = devc->channel_names[i / 2]; + + if ((ret = mixer_ext_create_control (dev, group, + i, + envy24ht_set_route, + MIXT_ENUM, name, 3, + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return ret; + mixer_ext_set_strings (dev, ret, "DMA ANALOGIN DIGITALIN", 0); + } + + if (devc->model_data == NULL) + { + cmn_err (CE_CONT, "Internal error: No model data\n"); + return 0; + } + + mask = devc->inportmask; + + if (devc->model_data->flags & MF_SPDIFOUT) + { + if ((ret = mixer_ext_create_control (dev, group, + 8, envy24ht_set_route, + MIXT_ENUM, + "ENVY24_SPDIFOUT", 3, + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return ret; + } + + return 0; +} + +/* + * S/PDIF lowlevel driver + */ +/*ARGSUSED*/ +static int +default_reprogram_device (void *_devc, void *_portc, + oss_digital_control * ctl, unsigned int mask) +{ + unsigned char c; + unsigned short cbits = 0; + envy24ht_devc *devc = _devc; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + + cbits |= (ctl->rate_bits & 0x0f) << 12; /* Sample rate */ + + if (ctl->out_vbit == VBIT_ON) + cbits |= 0x8000; /* Turn on the V bit */ + + cbits |= ctl->cbitout[0] & 0x07; /* Consumer/pro, audio/data, copyright */ + cbits |= (!!(ctl->cbitout[1] & 0x80)) << 11; /* Generation level */ + cbits |= (ctl->cbitout[1] & 0x7f) << 4; /* Category code */ + cbits |= (ctl->emphasis_type & 1) << 3; /* Pre-emphasis */ + + if (cbits != INW (devc->osdev, devc->mt_base + 0x3c)) + { + c = INB (devc->osdev, devc->ccs_base + 0x07); + OUTB (devc->osdev, c & ~0x80, devc->ccs_base + 0x07); /* Disable S/PDIF transmitter */ + OUTW (devc->osdev, cbits, devc->mt_base + 0x3c); + OUTB (devc->osdev, c | 0x80, devc->ccs_base + 0x07); /* (Re)enable S/PDIF transmitter */ + } + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + return 0; +} + +spdif_driver_t default_spdif_driver = { +/* reprogram_device: */ default_reprogram_device, +}; + + +static void +setup_sample_rate (envy24ht_devc * devc) +{ + unsigned char bits; + int change = 0; + + devc->speedbits = bits = speed_tab[devc->pending_speed_sel].speedbits; + if (devc->speed != speed_tab[devc->pending_speed_sel].speed) + change = 1; + devc->speed = devc->pending_speed = + speed_tab[devc->pending_speed_sel].speed; + mixer_devs[devc->mixer_dev]->modify_counter++; + + if (change) + { + oss_spdif_setrate (&devc->spdc, devc->speed); + + if (devc->model_data->svid == SSID_JULIA) + goto JULIA; + + if (devc->syncsource != 0) /* External sync */ + bits |= 0x10; + + if (devc->speed > 120000) + { + OUTB (devc->osdev, 0x08, devc->mt_base + 0x02); /* 128x I2S setup */ + } + else + { + OUTB (devc->osdev, 0x00, devc->mt_base + 0x02); /* 256x I2S setup */ + } + + OUTB (devc->osdev, bits & 0x0f, devc->mt_base + 0x01); /* Sampling rate */ +JULIA: + if (devc->auxdrv->set_rate) + devc->auxdrv->set_rate (devc); + } +} + +static int +envy24ht_set_control (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24ht_devc *devc = mixer_devs[dev]->hw_devc; + oss_native_word flags; + + if (cmd == SNDCTL_MIX_READ) + switch (ctrl) + { + case 1: + return devc->pending_speed_sel; + break; + + case 2: + return devc->syncsource; + break; + + case 3: + return devc->use_src; + break; + + case 5: + return devc->ratelock; + break; + + case 6: + return devc->speed; + break; + + case 7: + return 1; + + default: + return OSS_EIO; + } + + if (cmd == SNDCTL_MIX_WRITE) + switch (ctrl) + { + case 1: + if (value < 0 || value > devc->max_ratesel) + return OSS_EIO; + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + if (devc->configured_rate_sel != value) + { + devc->configured_rate_sel = value; + if (devc->open_count < 1) + { + devc->pending_speed_sel = devc->configured_rate_sel; + setup_sample_rate (devc); + } + } + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return devc->configured_rate_sel; + break; + + case 2: + if (value < 0 || value > 2) + return OSS_EIO; + devc->syncsource = value; + if (devc->model_data->svid == SSID_JULIA) + devc->auxdrv->private1 (devc, value); + return devc->syncsource; + break; + + case 3: + devc->use_src = !!value; + return devc->use_src; + break; + + case 5: + return devc->ratelock = !!value; + break; + + case 6: + return devc->speed; + break; + + case 7: + return 1; + break; + + default: + return OSS_EIO; + } + + return OSS_EINVAL; +} + +static int +envy24ht_mix_init (int dev) +{ + envy24ht_devc *devc = mixer_devs[dev]->hw_devc; + int group, err, n; + + if ((group = + mixer_ext_create_group_flags (dev, 0, "ENVY24", MIXF_FLAT)) < 0) + return group; + + if ((err = create_peak_mixer (dev, devc, group)) < 0) + return err; + + if ((err = create_rout_mixer (dev, devc, group)) < 0) + return err; + + n = devc->max_ratesel + 1; + if ((err = mixer_ext_create_control (dev, group, + 1, envy24ht_set_control, + MIXT_ENUM, + "ENVY24_RATE", n, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + mixer_ext_set_strings (dev, err, + "8000 9600 11025 12000 16000 22050 24000 32000 44100 48000 64000 88200 96000 176400 192000", 0); + + if ((err = mixer_ext_create_control (dev, group, + 2, envy24ht_set_control, + MIXT_ENUM, + "ENVY24_SYNC", 2, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 3, envy24ht_set_control, + MIXT_ONOFF, + "ENVY24_SRC", 2, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 5, envy24ht_set_control, + MIXT_ONOFF, + "ENVY24_RATELOCK", 2, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + if ((err = mixer_ext_create_control (dev, group, + 6, envy24ht_set_control, + MIXT_VALUE, + "ENVY24_ACTRATE", 192000, + MIXF_READABLE)) < 0) + return err; + + if (devc->auxdrv->mixer_init) + if ((err = devc->auxdrv->mixer_init (devc, dev, 0)) < 0) + return err; + + if ((err = oss_spdif_mix_init (&devc->spdc)) < 0) + return err; + + return 0; +} + +static int +eeprom_read (envy24ht_devc * devc, int pos) +{ + int i, status; + + for (i = 0; i < 0x10000; i++) + { + status = INB (devc->osdev, devc->ccs_base + 0x13); + if (!(status & 1)) + break; + + } + + OUTB (devc->osdev, pos, devc->ccs_base + 0x11); /* Offset */ + OUTB (devc->osdev, 0xa0, devc->ccs_base + 0x10); /* EEPROM read */ + + for (i = 0; i < 0x10000; i++) + { + status = INB (devc->osdev, devc->ccs_base + 0x13); + if (!(status & 1)) + break; + + } + + oss_udelay (1); + return INB (devc->osdev, devc->ccs_base + 0x12); +} + +static void +envy24pt_init (envy24ht_devc * devc) +{ + int gpio; + + gpio = INW (devc->osdev, devc->ccs_base + 0x14); + gpio |= INB (devc->osdev, devc->ccs_base + 0x1e) << 16; + +#if 0 +#define GPBIT(nn) !!(1<<nn) + cmn_err (CE_CONT, "GPIO=%06x\n", gpio); + cmn_err (CE_CONT, "With SPDIF_IN 'optical' connector: %d\n", GPBIT (1)); + cmn_err (CE_CONT, "With SPDIF_IN 'coaxial' connector: %d\n", GPBIT (2)); + + cmn_err (CE_CONT, "AC97 with stereo DAC for 7.1: %d\n", !GPBIT (8)); + cmn_err (CE_CONT, "Extra ADC/DAC connected to S/PDIF pins: %d\n", + GPBIT (9)); + cmn_err (CE_CONT, "S/PDIF connected to RDMA1: %d\n", !GPBIT (10)); + cmn_err (CE_CONT, "Smart 5.1 function supported: %d\n", GPBIT (11)); + cmn_err (CE_CONT, "De-POP function supported: %d\n", !GPBIT (12)); + cmn_err (CE_CONT, "PDMA4 DAC: 0=cs4321 1=WM8728: %d\n", GPBIT (13)); +#endif + + OUTB (devc->osdev, 0x00, devc->ccs_base + 0x04); /* System configuration */ + OUTB (devc->osdev, 0x02, devc->ccs_base + 0x05); /* AC-link configuration */ + OUTB (devc->osdev, 0x00, devc->ccs_base + 0x06); /* I2S configuration */ + OUTB (devc->osdev, 0x83, devc->ccs_base + 0x07); /* S/PDIF configuration */ + + /* TODO: GPIO initialization */ +} + +static void +julia_eeprom_init (envy24ht_devc * devc) +{ + OUTB (devc->osdev, 0x39, devc->ccs_base + 0x04); /* System configuration */ + OUTB (devc->osdev, 0x80, devc->ccs_base + 0x05); /* AC-link configuration */ + OUTB (devc->osdev, 0x78, devc->ccs_base + 0x06); /* I2S configuration */ + OUTB (devc->osdev, 0xc3, devc->ccs_base + 0x07); /* S/PDIF configuration */ + + OUTW (devc->osdev, 0xffff, devc->ccs_base + 0x18); /* GPIO direction */ + OUTW (devc->osdev, 0x0000, devc->ccs_base + 0x16); /* GPIO write mask */ + OUTW (devc->osdev, 0x801A, devc->ccs_base + 0x14); /* Initital bit state */ +} + +static int +init_eeprom_v2 (envy24ht_devc * devc) +{ + unsigned char *eeprom = (unsigned char *) &devc->eeprom; + + OUTB (devc->osdev, eeprom[6], devc->ccs_base + 0x04); + OUTB (devc->osdev, eeprom[7], devc->ccs_base + 0x05); + OUTB (devc->osdev, eeprom[8], devc->ccs_base + 0x06); + OUTB (devc->osdev, eeprom[9], devc->ccs_base + 0x07); + + OUTB (devc->osdev, eeprom[10], devc->ccs_base + 0x18); + OUTB (devc->osdev, eeprom[11], devc->ccs_base + 0x19); + OUTB (devc->osdev, eeprom[12], devc->ccs_base + 0x1a); + + OUTB (devc->osdev, eeprom[13], devc->ccs_base + 0x16); + OUTB (devc->osdev, eeprom[14], devc->ccs_base + 0x17); + OUTB (devc->osdev, eeprom[15], devc->ccs_base + 0x1f); + + OUTB (devc->osdev, eeprom[16], devc->ccs_base + 0x14); + OUTB (devc->osdev, eeprom[17], devc->ccs_base + 0x15); + OUTB (devc->osdev, eeprom[18], devc->ccs_base + 0x1e); + return 1; +} + +static int +load_eeprom (envy24ht_devc * devc) +{ + int status, i, check; + unsigned char *eeprom = (unsigned char *) &devc->eeprom, c; + static const char resolutions[4] = { 16, 18, 20, 24 }; + + c = 0; + + status = INB (devc->osdev, devc->ccs_base + 0x13); + + if (!(status & 0x80)) + return 0; /* No EEPROM */ + + devc->eeprom.bSize = sizeof (devc->eeprom); /* Guess the size */ + + for (i = 0; i < devc->eeprom.bSize; i++) + { + eeprom[i] = eeprom_read (devc, i); + eeprom[i] = eeprom_read (devc, i); + + if (devc->eeprom.bSize > sizeof (devc->eeprom)) + devc->eeprom.bSize = sizeof (devc->eeprom); + } +#if 1 + DDB (cmn_err (CE_CONT, "EEPROM=")); + for (i = 0; i < devc->eeprom.bSize; i++) + DDB (cmn_err (CE_CONT, "0x%02x, ", eeprom[i])); + DDB (cmn_err (CE_CONT, "\n")); +#endif + + check = 0; + for (i = 0; i < 4; i++) + { + check <<= 8; + check |= eeprom[i]; + } + + if (check != devc->model_data->svid) + cmn_err (CE_CONT, + "Envy24 WARNING: Possible EEPROM read error %08x != %08x\n", + check, devc->model_data->svid); + + DDB (cmn_err (CE_CONT, "EEPROM version %d\n", devc->eeprom.bVersion)); + + if (devc->eeprom.bVersion == 2) + { + return init_eeprom_v2 (devc); + } + + /* Init the controller registers based on the EEPROM data */ + + OUTB (devc->osdev, devc->eeprom.bCodecConfig, devc->ccs_base + 0x04); + OUTB (devc->osdev, devc->eeprom.bACLinkConfig, devc->ccs_base + 0x05); + OUTB (devc->osdev, devc->eeprom.bI2SID, devc->ccs_base + 0x06); + OUTB (devc->osdev, devc->eeprom.bSpdifConfig, devc->ccs_base + 0x07); + + /* GPIO ports */ + + OUTB (devc->osdev, devc->eeprom.bGPIODirection2, devc->ccs_base + 0x18); + OUTB (devc->osdev, devc->eeprom.bGPIODirection1, devc->ccs_base + 0x19); + OUTB (devc->osdev, devc->eeprom.bGPIODirection0, devc->ccs_base + 0x1a); + + OUTB (devc->osdev, devc->eeprom.bGPIOInitMask2, devc->ccs_base + 0x14); + OUTB (devc->osdev, devc->eeprom.bGPIOInitMask1, devc->ccs_base + 0x15); + OUTB (devc->osdev, devc->eeprom.bGPIOInitMask0, devc->ccs_base + 0x1f); + + OUTB (devc->osdev, devc->eeprom.bGPIOInitState2, devc->ccs_base + 0x14); + OUTB (devc->osdev, devc->eeprom.bGPIOInitState1, devc->ccs_base + 0x15); + OUTB (devc->osdev, devc->eeprom.bGPIOInitState0, devc->ccs_base + 0x1e); + +#if 1 + DDB (cmn_err (CE_CONT, "GPIO=%02x%02x%02x (%02x%02x%02x, %02x%02x%02x)\n", + devc->eeprom.bGPIOInitState2, + devc->eeprom.bGPIOInitState1, + devc->eeprom.bGPIOInitState0, + devc->eeprom.bGPIODirection2, + devc->eeprom.bGPIODirection1, + devc->eeprom.bGPIODirection0, + devc->eeprom.bGPIOInitMask2, + devc->eeprom.bGPIOInitMask1, devc->eeprom.bGPIOInitMask0)); + + c = devc->eeprom.bCodecConfig; + switch ((c >> 6) % 0x03) + { + case 0: + DDB (cmn_err (CE_CONT, "24.576MHz crystal\n")); + break; + case 1: + DDB (cmn_err (CE_CONT, "49.152MHz crystal\n")); + break; + default: + DDB (cmn_err (CE_CONT, "Unknown crystal frequency\n")); + } + + if (c & 0x20) + { + DDB (cmn_err (CE_CONT, "Has MPU401 UART\n")); + } + else + { + DDB (cmn_err (CE_CONT, "No MPU401 UART\n")); + } + DDB (cmn_err + (CE_CONT, "%d stereo ADC pairs connected\n", ((c >> 2) & 0x03) + 1)); + DDB (cmn_err (CE_CONT, "%d stereo DAC pairs connected\n", (c & 0x03) + 1)); + + c = devc->eeprom.bACLinkConfig; + DDB (cmn_err + (CE_CONT, "Converter type: %s\n", (c & 0x80) ? "I2S" : "AC97")); + if (!(c & 0x80)) + { + if (!(devc->model_data->flags & MF_NOAC97)) + devc->codec_type = CODEC_AC97; + + DDB (cmn_err (CE_NOTE, + "AC link connection mode type: %s\n", + (c & 0x02) ? "packed" : "split")); + } + else + { + c = devc->eeprom.bI2SID; + + DDB (cmn_err (CE_CONT, "I2C codec has volume control/mute: %s\n", + (c % 0x80) ? "YES" : "NO")); + DDB (cmn_err (CE_CONT, "I2C codec has 96 KHz S/R support: %s\n", + (c % 0x40) ? "YES" : "NO")); + DDB (cmn_err (CE_CONT, "I2C codec has 192 KHz S/R support: %s\n", + (c % 0x08) ? "YES" : "NO")); + + DDB (cmn_err (CE_CONT, + "Converter resolution %d bits\n", + resolutions[(c >> 4) & 0x03])); + } + + c = INB (devc->osdev, devc->ccs_base + 0x07); + DDB (cmn_err (CE_CONT, + "Internal S/PDIF out implemented: %s\n", + (c & 0x40) ? "YES" : "NO")); + DDB (cmn_err + (CE_CONT, "Internal S/PDIF out enabled: %s\n", + (c & 0x80) ? "YES" : "NO")); + DDB (cmn_err + (CE_CONT, "External S/PDIF out implemented: %s\n", + (c & 0x01) ? "YES" : "NO")); + DDB (cmn_err + (CE_CONT, "S/PDIF input present: %s\n", (c & 0x02) ? "YES" : "NO")); + DDB (cmn_err (CE_CONT, "S/PDIF chip IDs %x\n", (c >> 2) & 0x0f)); +#endif + + return 1; +} + +/*ARGSUSED*/ +static void +dump_regs (envy24ht_devc * devc, char *lbl) +{ +#if 0 + int i; + + cmn_err (CE_CONT, "\nDump registers: %s\n", lbl); + + for (i = 0; i < 0x20; i += 4) + { + if (!(i % (8 * 4))) + cmn_err (CE_CONT, "\nCCS%02x: ", i); + cmn_err (CE_CONT, "%08x ", INL (devc->osdev, devc->ccs_base + i)); + } + cmn_err (CE_CONT, "\n"); + + for (i = 0; i < 0x80; i += 4) + { + if (!(i % (8 * 4))) + cmn_err (CE_CONT, "\nMT%02x: ", i); + cmn_err (CE_CONT, "%08x ", INL (devc->osdev, devc->mt_base + i)); + } + cmn_err (CE_CONT, "\n"); +#endif +} + +static int +verify_rate (envy24ht_devc * devc, int arg) +{ + if (devc->codec_type == CODEC_AC97 && arg > 48000) + arg = 48000; + if (arg > 96000 && !(devc->model_data->flags & MF_192K)) + arg = 96000; + + return arg; +} + +static int +envy24ht_set_rate (int dev, int arg) +{ + envy24ht_devc *devc = audio_engines[dev]->devc; + envy24ht_portc *portc = audio_engines[dev]->portc; + int i, ix, diff, bestdiff; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + + if (arg == 0) + { + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return devc->pending_speed; + } + + arg = verify_rate (devc, arg); +/* + * Don't permit changing the sampling rate if we have multiple clients. + */ + if (devc->open_count != 1 || devc->ratelock) + { + DDB (cmn_err (CE_CONT, "Can't set speed: open_count %d, ratelock %d\n", + devc->open_count, devc->ratelock)); + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + if (arg != devc->pending_speed) + { + audio_engines[dev]->fixed_rate = devc->speed; + audio_engines[dev]->min_rate = devc->speed; + audio_engines[dev]->max_rate = devc->speed; + audio_engines[dev]->flags |= ADEV_FIXEDRATE; + } + else + { + audio_engines[dev]->min_rate = 8000; + audio_engines[dev]->max_rate = 192000; + audio_engines[dev]->flags &= ~ADEV_FIXEDRATE; + } + return devc->pending_speed; + } + + if (portc->dev_flags & DF_SPDIF) + { + /* Allow only supported S/PDIF rates */ + if (arg < 32000) + arg = 32000; + if (arg > 96000) + arg = 96000; + } + + ix = 9; + bestdiff = 1000000; + i = 0; + audio_engines[dev]->flags &= ~ADEV_FIXEDRATE; + + while (speed_tab[i].speed != -1) + { + diff = speed_tab[i].speed - arg; + if (diff < 0) + diff = -diff; + if (diff < bestdiff) + { + ix = i; + bestdiff = diff; + } + i++; + } + + devc->pending_speed = speed_tab[ix].speed; + devc->pending_speed_sel = ix; + /*cmn_err(CE_CONT, "Requested sampling rate %d, got %d\n", arg, devc->pending_speed); */ + + //setup_sample_rate (devc); + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return devc->pending_speed; +} + +static short +envy24ht_set_channels (int dev, short arg) +{ + envy24ht_portc *portc = audio_engines[dev]->portc; + envy24ht_devc *devc = audio_engines[dev]->devc; + oss_native_word flags; + + if (arg == 0) + return portc->channels; + + if (portc->dev_flags & DF_MULTICH) + { + int n = 2, ch, i, mask; + + if (arg < 2) + arg = 2; + + arg = ((arg + 1) / 2) * 2; /* Round to even number of channels */ + + if (arg > devc->model_data->nr_outs) + arg = devc->model_data->nr_outs; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + + devc->busy_play_channels &= ~portc->used_chmask; + + for (ch = 2; ch <= arg; ch += 2) + { + mask = 0; + + for (i = 0; i < ch; i++) + mask |= (1 << i); + + if (devc->busy_play_channels & mask) + break; + n = ch; + } + + portc->channels = n; + portc->used_chmask = 0; + for (i = 0; i < n; i++) + portc->used_chmask |= (1 << i); + + devc->busy_play_channels |= portc->used_chmask; + /* MT19: Channel allocation */ + OUTB (devc->osdev, 4 - n / 2, devc->mt_base + 0x19); + /* cmn_err(CE_CONT, "%d channels: MT19=%02x\n", n, INB(devc->osdev, devc->mt_base+0x19)); */ + + if (portc->channels == 6) + { + /* The fragment size must be a multiple of 6 */ + audio_engines[dev]->min_block = 4 * 288; + audio_engines[dev]->max_block = 4 * 288; + + } + else + { + audio_engines[dev]->min_block = 0; + audio_engines[dev]->max_block = 0; + } + + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return portc->channels; + } + + return portc->channels = 2; +} + +/*ARGSUSED*/ +static int +ac3_write (adev_t * adev, + dmap_t * dmap, + void *frombuf, void *tobuf, int maxspace, int *fromlen, int *tolen) +{ +/* + * This routine takes AC3 input 16 bits at time and packs them to + * 32 bit words. + */ + int i, l; + unsigned short *ip; + unsigned int *op; + + l = *fromlen * 2; + if (l > maxspace) + { + l = maxspace; + } + + *tolen = l; + *fromlen = l / 2; + l /= 4; + + ip = frombuf; + op = tobuf; + + for (i = 0; i < l; i++) + { + *op++ = (*ip++) << 16; + } + + return 0; +} + +static unsigned int +envy24ht_set_format (int dev, unsigned int arg) +{ + envy24ht_portc *portc = audio_engines[dev]->portc; + + if (arg == 0) + return portc->fmt; + + if (arg == AFMT_AC3 && (portc->dev_flags & DF_AC3)) + { + audio_engines[dev]->dmap_out->device_write = ac3_write; + return portc->fmt = AFMT_AC3; + } + audio_engines[dev]->dmap_out->device_write = NULL; + return portc->fmt = SUPPORTED_FORMAT; +} + +static int +envy24ht_ioctl (int dev, unsigned int cmd, ioctl_arg arg) +{ + envy24ht_portc *portc = audio_engines[dev]->portc; + envy24ht_devc *devc = audio_engines[dev]->devc; + + switch (cmd) + { + case SNDCTL_DSP_GET_CHNORDER: + *(oss_uint64_t *) arg = CHNORDER_NORMAL; + return 0; + } + + if (portc->dev_flags & DF_SPDIF) + { + int ret; + ret = oss_spdif_ioctl (&devc->spdc, portc->open_mode, cmd, arg); + if (ret != SPDIF_NOIOCTL) + return ret; + } + + if (devc->auxdrv->audio_ioctl) + return devc->auxdrv->audio_ioctl (devc, portc, cmd, arg); + return OSS_EINVAL; +} + +static void envy24ht_trigger (int dev, int state); + +static void +envy24ht_reset (int dev) +{ + envy24ht_trigger (dev, 0); +} + +/*ARGSUSED*/ +static int +envy24ht_open_input (int dev, int mode, int open_flags) +{ + envy24ht_portc *portc = audio_engines[dev]->portc; + envy24ht_devc *devc = audio_engines[dev]->devc; + adev_p adev = audio_engines[dev]; + oss_native_word flags; + + if (mode & OPEN_WRITE) + { + cmn_err (CE_CONT, "Playback is not possible with %s\n", adev->devnode); + return OSS_ENOTSUP; + } + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + + if (portc->open_mode || (devc->busy_rec_channels & portc->chmask)) + { + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return OSS_EBUSY; + } + portc->open_mode = mode; + devc->open_count++; + if (devc->open_count == 1) + { + devc->pending_speed_sel = devc->configured_rate_sel; + } + + if (portc->dev_flags & DF_SPDIF) + oss_spdif_open (&devc->spdc, mode); + + portc->used_chmask = portc->chmask; + devc->busy_rec_channels |= portc->chmask; + + if (!devc->use_src) + adev->flags |= ADEV_NOSRC; + else + adev->flags &= ~ADEV_NOSRC; + + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return 0; +} + +/*ARGSUSED*/ +static int +envy24ht_open_output (int dev, int mode, int open_flags) +{ + envy24ht_portc *portc = audio_engines[dev]->portc; + envy24ht_devc *devc = audio_engines[dev]->devc; + oss_native_word flags; + adev_p adev = audio_engines[dev]; + + if (mode & OPEN_READ) + { + cmn_err (CE_CONT, "Recording is not possible with %s\n", adev->devnode); + return OSS_ENOTSUP; + } + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + + if (portc->open_mode || (devc->busy_play_channels & portc->chmask)) + { + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return OSS_EBUSY; + } + + portc->open_mode = mode; + portc->used_chmask = portc->chmask; + devc->busy_play_channels |= portc->chmask; + audio_engines[dev]->dmap_out->device_write = NULL; + + devc->open_count++; + if (devc->open_count == 1) + { + devc->pending_speed_sel = devc->configured_rate_sel; + } + + if (portc->dev_flags & DF_SPDIF) + oss_spdif_open (&devc->spdc, mode); + + if (!devc->use_src) + adev->flags |= ADEV_NOSRC; + else + adev->flags &= ~ADEV_NOSRC; + + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return 0; +} + +static void +envy24ht_close (int dev, int mode) +{ + envy24ht_portc *portc = audio_engines[dev]->portc; + envy24ht_devc *devc = audio_engines[dev]->devc; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + devc->open_count--; + + if (portc->open_mode & OPEN_READ) + devc->busy_rec_channels &= ~portc->used_chmask; + if (portc->open_mode & OPEN_WRITE) + devc->busy_play_channels &= ~portc->used_chmask; + portc->open_mode = 0; + + if (portc->dev_flags & DF_MULTICH) + { + OUTB (devc->osdev, 0x03, devc->mt_base + 0x19); /* Channel allocation */ + portc->chmask = 0x003; /* Just the front channels */ + } + + if (portc->dev_flags & DF_SPDIF) + oss_spdif_close (&devc->spdc, mode); + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); +} + +/*ARGSUSED*/ +static void +envy24ht_output_block (int dev, oss_native_word buf, int count, int fragsize, + int intrflag) +{ +} + +/*ARGSUSED*/ +static void +envy24ht_start_input (int dev, oss_native_word buf, int count, int fragsize, + int intrflag) +{ +} + +static void +envy24ht_trigger (int dev, int state) +{ + envy24ht_devc *devc = audio_engines[dev]->devc; + envy24ht_portc *portc = audio_engines[dev]->portc; + unsigned char enable, intrmask; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + enable = INB (devc->osdev, devc->mt_base + 0x18); + intrmask = INB (devc->osdev, devc->mt_base + 0x03); + + if (portc->state_bits == state) /* No change */ + { + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return; + } + portc->state_bits = state; + + if (portc->open_mode & OPEN_WRITE) + { + if (state & PCM_ENABLE_OUTPUT) + { + enable |= portc->mask; + intrmask &= ~portc->mask; + } + else + { + enable &= ~portc->mask; + intrmask |= portc->mask; + } + } + + if (portc->open_mode & OPEN_READ) + { + if (state & PCM_ENABLE_INPUT) + { + enable |= portc->mask; + intrmask &= ~portc->mask; + } + else + { + enable &= ~portc->mask; + intrmask |= portc->mask; + } + } + OUTB (devc->osdev, enable, devc->mt_base + 0x18); + OUTB (devc->osdev, intrmask, devc->mt_base + 0x03); + + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + + if (state) + dump_regs (devc, "trigger"); +} + +/*ARGSUSED*/ +static int +envy24ht_prepare_for_input (int dev, int bsize, int bcount) +{ + envy24ht_devc *devc = audio_engines[dev]->devc; + envy24ht_portc *portc = audio_engines[dev]->portc; + dmap_p dmap = audio_engines[dev]->dmap_in; + int buffsize, fragsize; + oss_native_word flags; + + if (audio_engines[dev]->flags & ADEV_NOINPUT) + return OSS_EACCES; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + setup_sample_rate (devc); + buffsize = dmap->bytes_in_use / 4 - 1; + fragsize = dmap->fragment_size / 4 - 1; + + OUTL (devc->osdev, dmap->dmabuf_phys, portc->base + 0x00); + OUTW (devc->osdev, buffsize, portc->base + 0x04); + OUTW (devc->osdev, fragsize, portc->base + 0x06); + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + + return 0; +} + +/*ARGSUSED*/ +static int +envy24ht_prepare_for_output (int dev, int bsize, int bcount) +{ + envy24ht_devc *devc = audio_engines[dev]->devc; + envy24ht_portc *portc = audio_engines[dev]->portc; + dmap_p dmap = audio_engines[dev]->dmap_out; + int buffsize, fragsize; + oss_native_word flags; + + if (audio_engines[dev]->flags & ADEV_NOOUTPUT) + return OSS_EACCES; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + setup_sample_rate (devc); + buffsize = dmap->bytes_in_use / 4 - 1; + fragsize = dmap->fragment_size / 4 - 1; + + if (portc->dev_flags & DF_MULTICH) + { + /* Multi ch device */ + OUTL (devc->osdev, dmap->dmabuf_phys, devc->mt_base + 0x10); + OUTL (devc->osdev, buffsize, devc->mt_base + 0x14); + OUTL (devc->osdev, fragsize, devc->mt_base + 0x1c); + } + else + { + OUTL (devc->osdev, dmap->dmabuf_phys, portc->base + 0x00); + OUTW (devc->osdev, buffsize, portc->base + 0x04); + OUTW (devc->osdev, fragsize, portc->base + 0x06); + } + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + + return 0; +} + +/*ARGSUSED*/ +static int +envy24ht_get_buffer_pointer (int dev, dmap_t * dmap, int direction) +{ + envy24ht_portc *portc = audio_engines[dev]->portc; + envy24ht_devc *devc; + int pos; + + devc = audio_engines[dev]->devc; + pos = (INW (devc->osdev, portc->base + 0x04) + 1) * 4; + return dmap->bytes_in_use - pos; +} + +static int +envy24ht_sync_control(int dev, int event, int mode) +{ + envy24ht_devc *devc = audio_engines[dev]->devc; + envy24ht_portc *portc = audio_engines[dev]->portc; + unsigned char enable, intrmask; + oss_native_word flags; + MUTEX_ENTER_IRQDISABLE(devc->mutex, flags); + if(event == SYNC_PREPARE) + { + devc->syncstart_mask |= portc->mask; + portc->state_bits = mode; + } + else if(event == SYNC_TRIGGER) + { + if(devc->syncstart_mask) + { + enable = INB (devc->osdev, devc->mt_base + 0x18); + intrmask = INB (devc->osdev, devc->mt_base + 0x03); + enable |= devc->syncstart_mask; + intrmask &= ~devc->syncstart_mask; + OUTB (devc->osdev, enable, devc->mt_base + 0x18); + OUTB (devc->osdev, intrmask, devc->mt_base + 0x03); + devc->syncstart_mask = 0; + } + } + MUTEX_EXIT_IRQRESTORE(devc->mutex, flags); + return 0; +} + +#if 0 +static int +envy24ht_check_output (int dev) +{ + int pos; + envy24ht_devc *devc = audio_engines[dev]->devc; + + pos = envy24ht_get_buffer_pointer (dev, audio_engines[dev]->dmap_out, 0); + + cmn_err (CE_CONT, + "Envy24ht: Output timeout on device %d (%d, %02x, %02x)\n", dev, + pos, INB (devc->osdev, devc->ccs_base + 0x02), INB (devc->osdev, + devc-> + ccs_base + + 0x00)); + return OSS_EIO; +} +#endif + +static audiodrv_t envy24ht_output_driver = { + envy24ht_open_output, + envy24ht_close, + envy24ht_output_block, + envy24ht_start_input, + envy24ht_ioctl, + envy24ht_prepare_for_input, + envy24ht_prepare_for_output, + envy24ht_reset, + NULL, + NULL, + NULL, + NULL, + envy24ht_trigger, + envy24ht_set_rate, + envy24ht_set_format, + envy24ht_set_channels, + NULL, + NULL, + NULL, /* check input */ + NULL, /* envy24ht_check_output */ + NULL, /* envy24ht_alloc_buffer */ + NULL, /* envy24ht_free_buffer */ + NULL, + NULL, + envy24ht_get_buffer_pointer, + NULL, + envy24ht_sync_control +}; + + +static audiodrv_t envy24ht_input_driver = { + envy24ht_open_input, + envy24ht_close, + envy24ht_output_block, + envy24ht_start_input, + envy24ht_ioctl, + envy24ht_prepare_for_input, + envy24ht_prepare_for_output, + envy24ht_reset, + NULL, + NULL, + NULL, + NULL, + envy24ht_trigger, + envy24ht_set_rate, + envy24ht_set_format, + envy24ht_set_channels, + NULL, + NULL, + NULL, + NULL, + NULL, /* envy24ht_alloc_buffer */ + NULL, /* envy24ht_free_buffer */ + NULL, + NULL, + envy24ht_get_buffer_pointer, + NULL, + envy24ht_sync_control +}; + +static const int bindings[MAX_ODEV] = { + DSP_BIND_FRONT, + DSP_BIND_CENTER_LFE, + DSP_BIND_SURR, + DSP_BIND_REAR +}; + +static int +init_play_device (envy24ht_devc * devc, int chmask, int offset, + unsigned char mask, char *name, int dev_flags, + char *port_id, char *devfile_name) +{ + int opts, dev, formats; + char tmp[80]; + envy24ht_portc *portc = NULL; + int i; + adev_p adev; + + sprintf (tmp, "%s %s out", devc->model_data->product, name); + + if (devc->nr_outdevs >= MAX_ODEV) + { + cmn_err (CE_CONT, "Envy24ht: Too many audio devices\n"); + return OSS_ENXIO; + } + + opts = ADEV_AUTOMODE | ADEV_NOINPUT; + + if (dev_flags & DF_SPDIF) + opts |= ADEV_SPECIAL; + + formats = SUPPORTED_FORMAT; + if (dev_flags & DF_AC3) + formats |= AFMT_AC3; + + if (dev_flags & DF_MULTICH) + opts |= ADEV_COLD; + else + opts |= ADEV_SPECIAL; + + if ((dev = oss_install_audiodev_with_devname (OSS_AUDIO_DRIVER_VERSION, + devc->osdev, + devc->osdev, + tmp, + &envy24ht_output_driver, + sizeof (audiodrv_t), + opts, formats, devc, -1, + devfile_name)) < 0) + { + return dev; + } + + if (devc->first_dev == -1) + { + devc->first_dev = dev; + audio_engines[dev]->outch_names = OUTCH_NAMES; + } + adev = audio_engines[dev]; + + portc = &devc->play_portc[devc->nr_outdevs]; + for (i = 0; speed_tab[i].speed != -1; i++) + adev->rates[adev->nrates++] = speed_tab[i].speed; + + portc->name = port_id; + audio_engines[dev]->portc = portc; + audio_engines[dev]->mixer_dev = devc->mixer_dev; + audio_engines[dev]->rate_source = devc->first_dev; + audio_engines[dev]->min_rate = 8000; + audio_engines[dev]->max_rate = 192000; + audio_engines[dev]->binding = bindings[devc->nr_outdevs]; + if (dev_flags & DF_SPDIF) + audio_engines[dev]->binding = DSP_BIND_SPDIF; + + portc->dev = dev; + portc->open_mode = 0; + portc->fmt = SUPPORTED_FORMAT; + portc->base = devc->mt_base + offset; + portc->mask = mask; + portc->dev_flags = dev_flags; + portc->chmask = chmask; + portc->state_bits = 0; + portc->direction = PCM_ENABLE_OUTPUT; + + audio_engines[dev]->min_channels = 2; + audio_engines[dev]->max_channels = 2; + + if (dev_flags & DF_SPDIF) + audio_engines[dev]->caps |= PCM_CAP_DIGITALOUT | DSP_CH_STEREO; + else + { + if (dev_flags & DF_MULTICH) + { + audio_engines[dev]->caps |= PCM_CAP_ANALOGOUT; + audio_engines[dev]->caps |= DSP_CH_STEREO; + audio_engines[dev]->min_channels = 2; + audio_engines[dev]->max_channels = devc->model_data->nr_outs; + } + else + audio_engines[dev]->caps |= PCM_CAP_ANALOGOUT | DSP_CH_STEREO; + } + devc->nr_outdevs++; + + return dev; +} + +static int +init_rec_device (envy24ht_devc * devc, int chmask, int offset, + unsigned char mask, char *name, int dev_flags, char *devfile_name) +{ + int opts, dev, formats; + adev_p adev; + int i; + envy24ht_portc *portc = NULL; + char tmp[80]; + sprintf (tmp, "%s %s in", devc->model_data->product, name); + + if (devc->nr_indevs >= MAX_IDEV) + { + cmn_err (CE_CONT, "Envy24ht: Too many audio devices\n"); + return OSS_ENXIO; + } + + opts = ADEV_AUTOMODE | ADEV_NOOUTPUT | ADEV_COLD; + + if (dev_flags & DF_SPDIF) + opts |= ADEV_SPECIAL; + + formats = SUPPORTED_FORMAT; + if (dev_flags & DF_AC3) + formats |= AFMT_AC3; + + if ((dev = oss_install_audiodev_with_devname (OSS_AUDIO_DRIVER_VERSION, + devc->osdev, + devc->osdev, + tmp, + &envy24ht_input_driver, + sizeof (audiodrv_t), + opts, formats, devc, -1, + devfile_name)) < 0) + { + return dev; + } + + if (devc->first_dev == -1) + devc->first_dev = dev; + portc = &devc->rec_portc[devc->nr_indevs]; + adev = audio_engines[dev]; + + for (i = 0; speed_tab[i].speed != -1; i++) + adev->rates[adev->nrates++] = speed_tab[i].speed; + + audio_engines[dev]->portc = portc; + audio_engines[dev]->mixer_dev = devc->mixer_dev; + audio_engines[dev]->rate_source = devc->first_dev; + audio_engines[dev]->min_rate = 8000; + audio_engines[dev]->max_rate = 192000; + + portc->dev = dev; + portc->name = "rec"; + portc->open_mode = 0; + portc->base = devc->mt_base + offset; + portc->mask = mask; + portc->state_bits = 0; + portc->fmt = SUPPORTED_FORMAT; + portc->dev_flags = dev_flags; + portc->chmask = chmask; + portc->direction = PCM_ENABLE_INPUT; + if (dev_flags & DF_SPDIF) + audio_engines[dev]->caps |= PCM_CAP_DIGITALIN | DSP_CH_STEREO; + else + audio_engines[dev]->caps |= PCM_CAP_ANALOGIN | DSP_CH_STEREO; + devc->nr_indevs++; + + return dev; +} + +static void +init_devices (envy24ht_devc * devc) +{ + int front_engine, rec_engine; + + OUTB (devc->osdev, 0x03, devc->mt_base + 0x19); /* Channel allocation */ + OUTB (devc->osdev, 0x00, devc->mt_base + 0x1b); /* Unpause ALL channels */ + + devc->first_dev = -1; + + front_engine=init_play_device (devc, 0x003, 0x10, 0x01, devc->channel_names[0], + DF_MULTICH, "front", ""); + + if (devc->model_data->nr_outs > 2) + init_play_device (devc, 0x00c, 0x70, 0x10, devc->channel_names[1], 0, + "C/LFE", ""); + + if (devc->model_data->nr_outs > 4) + init_play_device (devc, 0x030, 0x60, 0x20, devc->channel_names[2], 0, + "side", ""); + + if (devc->model_data->nr_outs > 6) + init_play_device (devc, 0x0c0, 0x50, 0x40, devc->channel_names[3], 0, + "rear", ""); + + if (devc->model_data->flags & MF_SPDIFOUT) + { + init_play_device (devc, 0x300, 0x40, 0x80, "digital", + DF_SPDIF | DF_AC3, "spdif", "spdout"); + } + + rec_engine = init_rec_device (devc, 0x003, 0x20, 0x02, "analog", 0, ""); + + if (devc->model_data->flags & MF_SPDIFIN) + { + init_rec_device (devc, 0x00c, 0x30, 0x04, "digital", DF_SPDIF, "spdin"); + } + +#ifdef CONFIG_OSS_VMIX + if (rec_engine < 0) + rec_engine = -1; /* Not available */ + + if (front_engine >= 0) + vmix_attach_audiodev(devc->osdev, front_engine, rec_engine, 0); +#endif +} + +static void +install_ac97_mixer (envy24ht_devc * devc) +{ + int tmp; + tmp = 0; + + DDB (cmn_err (CE_CONT, "Installing AC97 mixer\n")); + + devc->mixer_dev = + ac97_install (&devc->ac97devc, devc->model_data->product, ac97_read, + ac97_write, devc, devc->osdev); + if (devc->mixer_dev < 0) + { + cmn_err (CE_CONT, "Envy24ht: Mixer install failed\n"); + return; + } + ac97_init_ext (devc->mixer_dev, &devc->ac97devc, envy24ht_mix_init, 50); + +#if 1 + /* AD1616 specific stuff. Check this if there is some other AC97 chip */ + /* Maybe this should be moved to ac97.c in a way or another */ + + /* Turn surround dacs ON */ + tmp = ac97_read (devc, 0x2a); + tmp &= ~0x3800; + ac97_write (devc, 0x2a, tmp); + + tmp = ac97_read (devc, 0x5a); + tmp &= ~0x8000; + tmp |= 0x1800; + ac97_write (devc, 0x5a, tmp); +#endif + +#if 0 + for (tmp = 0; tmp < 0x3f; tmp += 2) + cmn_err (CE_CONT, "%02x: %04x\n", tmp, ac97_read (devc, tmp)); + for (tmp = 0x5a; tmp < 0x5d; tmp += 2) + cmn_err (CE_CONT, "%02x: %04x\n", tmp, ac97_read (devc, tmp)); + for (tmp = 0x7a; tmp < 0x7f; tmp += 2) + cmn_err (CE_CONT, "%02x: %04x\n", tmp, ac97_read (devc, tmp)); +#endif +} + +int +oss_envy24ht_attach (oss_device_t * osdev) +{ + envy24ht_devc *devc; + extern int envy24ht_model; + unsigned char pci_irq_line; + unsigned short pci_command, vendor, device; + unsigned int subvendor; + unsigned int pci_ioaddr, pci_ioaddr1; + int i, err; + + char *name = "Generic ENVY24HT"; + + DDB (cmn_err (CE_CONT, "Entered Envy24HT probe routine\n")); + + pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor); + pci_read_config_word (osdev, PCI_DEVICE_ID, &device); + + if (vendor != ICENSEMBLE_VENDOR_ID || device != ICENSEMBLE_ENVY24HT_ID) + return 0; + + if ((devc = PMALLOC (osdev, sizeof (*devc))) == NULL) + { + cmn_err (CE_WARN, "Out of memory\n"); + return 0; + } + + devc->osdev = osdev; + osdev->devc = devc; + MUTEX_INIT (osdev, devc->mutex, MH_DRV); + MUTEX_INIT (osdev, devc->low_mutex, MH_DRV + 1); + + pci_read_config_dword (osdev, PCI_BASE_ADDRESS_0, &pci_ioaddr); + pci_read_config_dword (osdev, PCI_BASE_ADDRESS_1, &pci_ioaddr1); + pci_read_config_word (osdev, PCI_COMMAND, &pci_command); + pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line); + pci_read_config_dword (osdev, 0x2c, &subvendor); + + DDB (cmn_err (CE_CONT, + "Device found at I/O %x, %x\n", pci_ioaddr & ~3, + pci_ioaddr1 & ~3)); + + devc->subvendor = subvendor; + + devc->ccs_base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr) & ~0x3; + DDB (cmn_err (CE_CONT, "CCS base %x/%lx\n", pci_ioaddr, devc->ccs_base)); + + devc->mt_base = MAP_PCI_IOADDR (devc->osdev, 1, pci_ioaddr1) & ~0x3; + DDB (cmn_err (CE_CONT, "MT base %x/%lx\n", pci_ioaddr1, devc->mt_base)); + + /* Reset the chip */ + OUTB (devc->osdev, 0x81, devc->ccs_base + 0x00); + oss_udelay (1000); + + /* Release reset */ + OUTB (devc->osdev, 0x00, devc->ccs_base + 0x00); + oss_udelay (1000); + + pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; + pci_write_config_word (osdev, PCI_COMMAND, pci_command); + + devc->nr_outdevs = devc->nr_indevs = 0; + i = 0; + + if ((envy24ht_model > -1) + && (envy24ht_model < (sizeof (models) / sizeof (card_spec)) - 1)) + i = envy24ht_model; + else + while (models[i].svid != 0) + { + if (models[i].svid == subvendor) + { + name = models[i].product; + devc->model_data = &models[i]; + DDB (cmn_err (CE_CONT, "Card id '%s'\n", name)); + + break; + } + + i++; + } + + if (models[i].svid == 0) + { + cmn_err (CE_CONT, "Unknown device ID (%08x).\n", subvendor); + cmn_err (CE_CONT, "This card may not be supported (yet).\n"); + i = 0; /* Assume AC97 based Envy23PT */ + } + + oss_register_device (osdev, name); + + if (devc->model_data == NULL) + { + cmn_err (CE_CONT, "Envy24ht: This card was not recognized: %08x\n", + subvendor); + return 0; + } + + /* Disable all interrupts */ + OUTB (devc->osdev, 0xff, devc->ccs_base + 0x01); + OUTB (devc->osdev, 0xff, devc->mt_base + 0x03); + + if (devc->model_data->flags & MF_ENVY24PT) + { + devc->codec_type = CODEC_AC97; + envy24pt_init (devc); + } + else if (devc->model_data->svid == SSID_JULIA) + { + julia_eeprom_init (devc); + } + else + load_eeprom (devc); + + devc->irq = pci_irq_line; + if ((err = + oss_register_interrupts (devc->osdev, 0, envy24htintr, NULL)) < 0) + { + cmn_err (CE_WARN, "Can't register interrupt handler, err=%d\n", err); + return 0; + } + + i = 0; + devc->max_ratesel = 0; + + while (speed_tab[i].speed != -1) + { + int rate = speed_tab[i].speed; + + if (verify_rate (devc, rate) == rate) + devc->max_ratesel = i; + + i++; + } + + OUTB (devc->osdev, ~0x10, devc->ccs_base + 0x01); /* Enable audio interrupts */ + + if (devc->model_data->flags & MF_MIDI) + { + attach_midi (devc); + } + i = 0; + devc->max_ratesel = 0; + + while (speed_tab[i].speed != -1) + { + int rate = speed_tab[i].speed; + + if (verify_rate (devc, rate) == rate) + devc->max_ratesel = i; + + i++; + } + + devc->syncstart_mask = 0; + devc->speedbits = 0; + devc->speed = 0; + devc->pending_speed = 0; + devc->prev_speed = 0; + devc->pending_speed_sel = 9; + devc->configured_rate_sel = devc->pending_speed_sel; + devc->open_count = 0; + memcpy (devc->channel_names, channel_names, sizeof (channel_names)); + + devc->nr_play_channels = 10; + devc->nr_rec_channels = 10; +#define setmask(m, b) m|=(1<<(b)) + + devc->inportmask = 0; + devc->outportmask = 0; + devc->busy_play_channels = 0; + devc->busy_rec_channels = 0; + + for (i = 0; i < devc->model_data->nr_outs; i++) + setmask (devc->outportmask, i); + if (devc->model_data->flags & MF_SPDIFOUT) + { + setmask (devc->outportmask, 8); /* SPDIF */ + setmask (devc->outportmask, 9); /* SPDIF */ + } + for (i = 0; i < devc->model_data->nr_ins; i++) + setmask (devc->inportmask, i); + if (devc->model_data->flags & MF_SPDIFIN) + { + setmask (devc->inportmask, 8); /* SPDIF */ + setmask (devc->inportmask, 9); /* SPDIF */ + } + + if (devc->model_data->auxdrv == NULL) + { + devc->auxdrv = &dummy_auxdrv; + } + else + { + devc->auxdrv = devc->model_data->auxdrv; + if (devc->auxdrv->card_init) + devc->auxdrv->card_init (devc); + } + + if (devc->codec_type == CODEC_AC97) + install_ac97_mixer (devc); + else + { + if ((devc->mixer_dev = oss_install_mixer (OSS_MIXER_DRIVER_VERSION, + devc->osdev, + devc->osdev, + devc->model_data-> + product, + &envy24ht_mixer_driver, + sizeof (mixer_driver_t), + devc)) >= 0) + { + int n = 50; + + mixer_devs[devc->mixer_dev]->hw_devc = devc; + mixer_ext_set_init_fn (devc->mixer_dev, envy24ht_mix_init, n); + mixer_devs[devc->mixer_dev]->priority = 1; /* Possible default mixer candidate */ + } + } + + if (devc->model_data->flags & (MF_SPDIFOUT | MF_SPDIFIN)) + { + int err; + + if ((err = oss_spdif_install (&devc->spdc, devc->osdev, + &default_spdif_driver, + sizeof (spdif_driver_t), devc, NULL, + devc->mixer_dev, SPDF_OUT, + DIG_PASSTHROUGH | DIG_EXACT | + DIG_CBITOUT_LIMITED | DIG_VBITOUT | + DIG_PRO | DIG_CONSUMER)) != 0) + { + cmn_err (CE_CONT, + "S/PDIF driver install failed. error %d\n", err); + return 0; + } + } + OUTB (devc->osdev, ~0x10, devc->ccs_base + 0x01); /* Enable audio interrupts */ + init_devices (devc); + setup_sample_rate (devc); + + + return 1; +} + +int +oss_envy24ht_detach (oss_device_t * osdev) +{ + envy24ht_devc *devc = osdev->devc; + + if (oss_disable_device (osdev) < 0) + return 0; + + /* Disable all interrupts */ + OUTB (devc->osdev, 0xff, devc->ccs_base + 0x01); + /* disable DMA interrupt mask */ + OUTB (devc->osdev, 0xff, devc->mt_base + 0x00); + + /* Stop playback */ + OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) & ~0x01, + devc->mt_base + 0x18); + oss_udelay (100); + OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) & ~0x01, + devc->mt_base + 0x18); + + /* Stop recording */ + OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) & ~0x04, + devc->mt_base + 0x18); + oss_udelay (100); + OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) & ~0x04, + devc->mt_base + 0x18); + + unload_midi (devc); + + if (devc->auxdrv->card_uninit) + devc->auxdrv->card_uninit(devc); + + oss_unregister_interrupts (devc->osdev); + + if (devc->model_data->flags & (MF_SPDIFOUT | MF_SPDIFIN)) + { + oss_spdif_uninstall (&devc->spdc); + } + + MUTEX_CLEANUP (devc->mutex); + MUTEX_CLEANUP (devc->low_mutex); + UNMAP_PCI_IOADDR (devc->osdev, 0); + UNMAP_PCI_IOADDR (devc->osdev, 1); + + oss_unregister_device (osdev); + return 1; +} diff --git a/kernel/drv/oss_envy24ht/oss_envy24ht.man b/kernel/drv/oss_envy24ht/oss_envy24ht.man new file mode 100644 index 0000000..f87d928 --- /dev/null +++ b/kernel/drv/oss_envy24ht/oss_envy24ht.man @@ -0,0 +1,24 @@ +NAME +oss_envy24ht - VIA Envy24HT/PT audio driver. + +DESCRIPTION +Open Sound System driver for Envy24HT, Envy24HT-S, Envy24PT based sound +cards. + +Envy24HT device characteristics: + o 8/16 bit playback/record + o mono/stereo/4ch/5.1ch/7.1ch playback + o mono/sterero recording + o 8KHz to 192Khz sample rate. + +OPTIONS + o envy24ht_model = -1|0|1 + Select the Model number if the card isn't autodetected + Values: 0 = Envy24ht 1=Envy24PT/HT-s compatible -1=Autodetect Default: -1 + +FILES +CONFIGFILEPATH/oss_envy24ht.conf Device configuration file + +AUTHOR +4Front Technologies + |