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_envy24/envy24_default.c | |
download | oss4-upstream/4.2-build2006.tar.gz |
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'kernel/drv/oss_envy24/envy24_default.c')
-rw-r--r-- | kernel/drv/oss_envy24/envy24_default.c | 2384 |
1 files changed, 2384 insertions, 0 deletions
diff --git a/kernel/drv/oss_envy24/envy24_default.c b/kernel/drv/oss_envy24/envy24_default.c new file mode 100644 index 0000000..21baa45 --- /dev/null +++ b/kernel/drv/oss_envy24/envy24_default.c @@ -0,0 +1,2384 @@ +/* + * Purpose: Card specific routines for several Envy24 based cards. + */ +/* + * + * 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_config.h" +#include "ac97.h" +#include "envy24.h" + +#if 0 +# define PRT_STATUS(v) outb(v&0xff, 0x378) +#else +# define PRT_STATUS(v) +#endif + +extern int envy24_gain_sliders; +extern int envy24_virtualout; +extern int envy24_zerolatency; /* Testing in progress */ + +#define EWX_DIG_ADDR 0x20 /* IIC address of digital traceiver chip of EWX2496 */ + +/* + * Playback engine management + */ + +static void +write_spdif (envy24_devc * devc, int d) +{ + int i, cmd; + + cmd = envy24_read_cci (devc, 0x20); + for (i = 7; i >= 0; i--) + { + cmd &= ~0x04; + envy24_write_cci (devc, 0x20, cmd); + oss_udelay (1); + if (d & (1 << i)) + cmd |= 0x08; + else + cmd &= ~0x08; + cmd |= 0x04; + envy24_write_cci (devc, 0x20, cmd); + oss_udelay (1); + } + cmd &= ~0x04; + envy24_write_cci (devc, 0x20, cmd); +} + +static void +write_codec (envy24_devc * devc, int codec, int addr, int data) +{ + unsigned char cmd; + int i; + int tmp; + cmd = envy24_read_cci (devc, 0x20); + cmd &= ~codec; + envy24_write_cci (devc, 0x20, cmd); + oss_udelay (1); + addr &= 0x07; + tmp = 0xa000 | (addr << 8) | data; + for (i = 15; i >= 0; i--) + { + cmd &= ~0x30; + if (tmp & (1 << i)) + cmd |= 0x10; + envy24_write_cci (devc, 0x20, cmd); + oss_udelay (1); + cmd |= 0x20; + envy24_write_cci (devc, 0x20, cmd); + oss_udelay (1); + } + cmd |= codec; + envy24_write_cci (devc, 0x20, cmd); + oss_udelay (1); +} + +#define CDC_CLK 1 /* Clock input to the CODEC's, rising edge clocks data. */ +#define CDC_DIN 2 /* Data input to Envy from the CODEC. */ +#define CDC_DOUT 3 /* Data output from Envy to the CODEC. */ +#define DIG_CS 4 /* Chip select (0=select) for the SPDIF tx/rx. */ +#define CDC_CS 5 /* Chip select (0=select) for the CODEC. */ +#define D410_MUTE 7 /* Delta 410 codec mute */ +#define CS_ASSERT 0 /* Asserted chip select (selects are inverted). */ +#define CS_RELEASE 1 /* Idle chip select (selects are inverted). */ + + +#define CS8_CLK CDC_CLK +#define CS8_DIN CDC_DIN +#define CS8_DOUT CDC_DOUT +#define CS8_CS DIG_CS +#define CS_1 CS_ASSERT +#define CS_0 CS_RELEASE +#define CS8_ADDR 0x20 /* Chip SPI/I2C address */ +#define CS8_RD 0x01 +#define CS8_WR 0x00 + +#define EWX_DIG_CS 0 +#define EWX_CDC_CLK 5 +#define EWX_CDC_DOUT 4 +#define EWX_CDC_DIN EWX_CDC_DOUT +#define EWX_IIC_WRITE 3 +#define CX_ASSERT 0 /* Asserted chip select (selects are inverted). */ +#define CX_RELEASE 1 /* Idle chip select (selects are inverted). */ + +void +WriteGPIObit (envy24_devc * devc, int sel, int what) +{ + unsigned char gpio; + + gpio = envy24_read_cci (devc, 0x20); + gpio &= ~(1 << sel); + gpio |= (what << sel); + envy24_write_cci (devc, 0x20, gpio); +} + +int +ReadGPIObit (envy24_devc * devc, int sel) +{ + unsigned char gpio; + + gpio = envy24_read_cci (devc, 0x20); + return !!(gpio & (1 << sel)); +} + +static void +write_ap_codec (envy24_devc * devc, int bRegister, unsigned char bData) +/* + +***************************************************************************** +* Writes a byte to a specific register of the Delta-AP CODEC. +* Register must be (0..15). +****************************************************************************/ +{ + unsigned char bMask; + + if (devc->model_data->flags & MF_D410) + bRegister = (bRegister & 0x0F) | 0x20; /* Add I2C address field. */ + else + bRegister = (bRegister & 0x0F) | 0xA0; /* Add I2C address field. */ + + /* Assert the CODEC chip select and wait at least 150 nS. */ + /* */ + WriteGPIObit (devc, CDC_CS, CS_ASSERT); + WriteGPIObit (devc, DIG_CS, CS_RELEASE); + + /* Write the register address byte. */ + /* */ + for (bMask = 0x80; bMask; bMask = (bMask >> 1) & 0x7F) + { + /* Drop SPI clock low. */ + WriteGPIObit (devc, CDC_CLK, 0); + + /* Write current data bit. */ + if (bMask & bRegister) + WriteGPIObit (devc, CDC_DOUT, 1); + else + WriteGPIObit (devc, CDC_DOUT, 0); + + /* Raise SPI clock to "clock data in". */ + WriteGPIObit (devc, CDC_CLK, 1); + } + + + /* Write the data byte. */ + /* */ + for (bMask = 0x80; bMask; bMask = (bMask >> 1) & 0x7F) + { + /* Drop SPI clock low. */ + WriteGPIObit (devc, CDC_CLK, 0); + + /* Write current data bit. */ + if (bMask & bData) + WriteGPIObit (devc, CDC_DOUT, 1); + else + WriteGPIObit (devc, CDC_DOUT, 0); + + /* Raise SPI clock to "clock data in". */ + WriteGPIObit (devc, CDC_CLK, 1); + } + + /* De-assert chip select. */ + /* */ + WriteGPIObit (devc, CDC_CS, CS_RELEASE); +} +static int +read_ap_spdif_reg (envy24_devc * devc, int bRegister) +/* +***************************************************************************** +* Reads a byte from a specific CS8427 register. +****************************************************************************/ +{ + unsigned char bMask; + unsigned char bRet = 0; + unsigned char bSPI; + + /****** WRITE MAP ADDRESS FIRST ******/ + + /* Drop the chip select low. */ + /* Wait at least 150 nS. */ + /* */ + WriteGPIObit (devc, DIG_CS, CS_ASSERT); + + /* Write the SPI address/cmd byte. */ + /* */ + bSPI = CS8_ADDR + CS8_WR; /* SPI address field plus WRITE operation. */ + /* */ + for (bMask = 0x80; bMask; bMask = (bMask >> 1) & 0x7F) + { + /* Drop clock (GPIO5) low. */ + WriteGPIObit (devc, CDC_CLK, 0); + + /* Write current data bit. */ + if (bMask & bSPI) + WriteGPIObit (devc, CDC_DOUT, 1); + else + WriteGPIObit (devc, CDC_DOUT, 0); + + /* Raise clock (GPIO5). */ + WriteGPIObit (devc, CDC_CLK, 1); + } + + + /* Write the address (MAP) byte. */ + /* */ + for (bMask = 0x80; bMask; bMask = (bMask >> 1) & 0x7F) + { + /* Drop clock (GPIO5) low. */ + WriteGPIObit (devc, CDC_CLK, 0); + + /* Write current data bit. */ + if (bMask & bRegister) + WriteGPIObit (devc, CDC_DOUT, 1); + else + WriteGPIObit (devc, CDC_DOUT, 0); + + /* Raise clock (GPIO5). */ + WriteGPIObit (devc, CDC_CLK, 1); + } + + /* De-assert chip select(s). */ + /* */ + WriteGPIObit (devc, DIG_CS, CS_RELEASE); + + + /****** NOW READ THE DATA ******/ + + /* Drop the chip select low. */ + /* Wait at least 150 nS. */ + /* */ + WriteGPIObit (devc, DIG_CS, CS_ASSERT); + + + /* Write the SPI address/cmd byte. */ + /* */ + bSPI = CS8_ADDR + CS8_RD; /* SPI address field plus READ operation. */ + /* */ + for (bMask = 0x80; bMask; bMask = (bMask >> 1) & 0x7F) + { + /* Drop clock (GPIO5) low. */ + WriteGPIObit (devc, CDC_CLK, 0); + + /* Write current data bit. */ + if (bMask & bSPI) + WriteGPIObit (devc, CDC_DOUT, 1); + else + WriteGPIObit (devc, CDC_DOUT, 0); + + /* Raise clock (GPIO5). */ + WriteGPIObit (devc, CDC_CLK, 1); + } + + + /* Read the data byte. */ + /* */ + bRet = 0; + /* */ + for (bMask = 0x80; bMask; bMask = (bMask >> 1) & 0x7F) + { + /* Drop clock low. */ + WriteGPIObit (devc, CDC_CLK, 0); + + /* Read current data bit. */ + if (ReadGPIObit (devc, CDC_DIN)) + bRet |= bMask; + + /* Raise clock. */ + WriteGPIObit (devc, CDC_CLK, 1); + } + + + /* De-assert chip selects. */ + /* */ + WriteGPIObit (devc, DIG_CS, CS_RELEASE); + + /* Return value. */ + + return bRet; +} + +static unsigned char +ewx2496_iic_read (envy24_devc * devc, int addr, unsigned char bRegIdx); + +static int +read_ewx2496_spdif_reg (envy24_devc * devc, int bRegister) +/* +***************************************************************************** +* Reads a byte from a specific CS8427 register. +****************************************************************************/ +{ + unsigned char ret; + + ret = ewx2496_iic_read (devc, EWX_DIG_ADDR, bRegister); + + return ret; +} + +#define read_cs8427_spdif_reg(d, r) devc->model_data->auxdrv->spdif_read_reg(d, r) + +static void +write_ap_spdif_reg (envy24_devc * devc, int bRegister, int bData) +/* +***************************************************************************** +* Writes a byte to a specific register of the Delta-AP S/PDIF chip. +* Register must be (0..55). +****************************************************************************/ +{ + unsigned char bMask; + unsigned char bSPI; + + /* Assert the CODEC chip select and wait at least 150 nS. */ + /* */ + WriteGPIObit (devc, CDC_CS, CS_0); + WriteGPIObit (devc, CS8_CS, CS_1); + + /* Write the SPI address/cmd byte. */ + /* */ + bSPI = CS8_ADDR | CS8_WR; + /* */ + for (bMask = 0x80; bMask; bMask = (bMask >> 1) & 0x7F) + { + /* Drop SPI clock low. */ + WriteGPIObit (devc, CS8_CLK, 0); + + /* Write current data bit. */ + if (bMask & bSPI) + WriteGPIObit (devc, CS8_DOUT, 1); + else + WriteGPIObit (devc, CS8_DOUT, 0); + + /* Raise SPI clock to "clock data in". */ + WriteGPIObit (devc, CS8_CLK, 1); + } + + /* Write the address (MAP) byte. */ + /* */ + for (bMask = 0x80; bMask; bMask = (bMask >> 1) & 0x7F) + { + /* Drop SPI clock low. */ + WriteGPIObit (devc, CS8_CLK, 0); + + /* Write current data bit. */ + if (bMask & bRegister) + WriteGPIObit (devc, CS8_DOUT, 1); + else + WriteGPIObit (devc, CS8_DOUT, 0); + + /* Raise SPI clock to "clock data in". */ + WriteGPIObit (devc, CS8_CLK, 1); + } + + + /* Write the data byte. */ + /* */ + for (bMask = 0x80; bMask; bMask = (bMask >> 1) & 0x7F) + { + /* Drop SPI clock low. */ + WriteGPIObit (devc, CS8_CLK, 0); + + /* Write current data bit. */ + if (bMask & bData) + WriteGPIObit (devc, CS8_DOUT, 1); + else + WriteGPIObit (devc, CS8_DOUT, 0); + + /* Raise SPI clock to "clock data in". */ + WriteGPIObit (devc, CS8_CLK, 1); + } + + /* De-assert chip select. */ + /* */ + WriteGPIObit (devc, CS8_CS, CS_0); +} + +static void +ewx2496_iic_write (envy24_devc * devc, int addr, + unsigned char bRegIdx, unsigned char bRegData); + +static void +write_ewx2496_spdif_reg (envy24_devc * devc, int bRegister, int bData) +{ + ewx2496_iic_write (devc, EWX_DIG_ADDR, bRegister, bData); + +} + +#define write_cs8427_spdif_reg(d, r, b) devc->model_data->auxdrv->spdif_write_reg(d, r, b) + +#define BIT0 0x01 +#define BIT1 0x02 +#define BIT2 0x04 +#define BIT3 0x08 +#define BIT4 0x10 +#define BIT5 0x20 +#define BIT6 0x40 +#define BIT7 0x80 + +static void +lock_cs8427_spdif (envy24_devc * devc) +{ + write_cs8427_spdif_reg (devc, 18, read_cs8427_spdif_reg (devc, 18) & ~BIT5); + write_cs8427_spdif_reg (devc, 18, read_cs8427_spdif_reg (devc, 18) | BIT2); +} + +static void +unlock_cs8427_spdif (envy24_devc * devc) +{ + write_cs8427_spdif_reg (devc, 18, read_cs8427_spdif_reg (devc, 18) & ~BIT2); +} + +static unsigned char +bitswap (unsigned char bIn) +/* +***************************************************************************** +* Endian reversing routine. +****************************************************************************/ +{ + unsigned char bOut = 0; + unsigned char bImask = 0x01; + unsigned char bOmask = 0x80; + + while (bImask) + { + if (bIn & bImask) + bOut |= bOmask; + bImask = bImask << 1; + bOmask = (bOmask >> 1) & 0x7F; + } + + return bOut; +} + + +static unsigned char +ReadCsByte (envy24_devc * devc, unsigned char bByteNum) +/* +***************************************************************************** +* Reads a byte from Channel Status block buffer in CS8427. +* +* bByteNum is in the range (0..23) +* +* This routine assumes that CS8427 register 18 bit 5 is cleared so that the +* CS buffer is windowed, and that register 18 bit 2 is set so that CS output +* transfers are currently disabled. +****************************************************************************/ +{ + unsigned char bTemp; + + /* CS block window starts at reg #32... */ + bTemp = read_cs8427_spdif_reg (devc, bByteNum + 32); + + /* CS block access is reverse endian. */ + return bitswap (bTemp); +} + +static void +WriteCsByte (envy24_devc * devc, unsigned char bByteNum, unsigned char bData) +/* +***************************************************************************** +* Writes a byte to Channel Status block buffer in CS8427. +* +* bByteNum is in the range (0..23) +* +* This routine assumes that CS8427 register 18 bit 5 is cleared so that the +* CS buffer is windowed, and that register 18 bit 2 is set so that CS output +* transfers are currently disabled. +****************************************************************************/ +{ + /* CS block access is reverse endian. */ + bData = bitswap (bData); + + /* CS Window starts at reg #32... */ + write_cs8427_spdif_reg (devc, bByteNum + 32, bData); +} + +void +InitConsumerModeCS (envy24_devc * devc) +{ + int i; + + /* Set CS8427 registers 32-55 to window CS block, and disable CS output. */ + lock_cs8427_spdif (devc); + + /* Zero all the general CS bits. */ + /* */ + for (i = 0; i < 24; i++) + WriteCsByte (devc, i, 0x00); + /* */ + /* Consumer (usually SPDIF or AC3) mode static bit settings. */ + /* */ + WriteCsByte (devc, 0, 0x00); /* Consumer format (bit0 = 0). */ + WriteCsByte (devc, 1, 0x02); /* Category = PCM encoder/decoder. */ + + unlock_cs8427_spdif (devc); +} + +void +init_cs8427_spdif (envy24_devc * devc) +{ + int tmp; + + /* Select iunternal sync */ + write_cs8427_spdif_reg (devc, 4, read_cs8427_spdif_reg (devc, 4) & (~BIT0)); +/* +***************************************************************************** +* Initializes core (mainly static) registers of the CS8427. +* Returns 1 if initialization OK, otherwise 0. +****************************************************************************/ + /* Assumes Envy24 GPIO's have been initialized. They should be just fine */ + /* in the Windows driver as they are initialized from EEPROM info. */ + + /* Verify device ID register. Must be 0x71. */ + if ((tmp = read_cs8427_spdif_reg (devc, 127)) != 0x71 && tmp != 0) + { + cmn_err (CE_CONT, "Envy24: Unrecognized S/PDIF chip ID %02x\n", + read_cs8427_spdif_reg (devc, 127)); + cmn_err (CE_CONT, + " Hardware stalled. Please reboot and try again.\n"); + return; + } + + /* Turn off RUN bit while making changes to configuration. */ + write_cs8427_spdif_reg (devc, 4, read_cs8427_spdif_reg (devc, 4) & (~BIT6)); + + /* RMCK default function, set Validity, disable mutes, TCBL=output. */ + write_cs8427_spdif_reg (devc, 1, 0x01); /* validity* is BIT6. */ + + /* Hold last valid audio sample, RMCK=256*Fs, normal stereo operation. */ + write_cs8427_spdif_reg (devc, 2, 0x00); + /* Output drivers normal operation, Tx <== serial audio port, */ + /* Rx ==> serial audio port. */ + write_cs8427_spdif_reg (devc, 3, 0x0C); + + /* RUN off, OMCK=256xFs, output time base = OMCK, input time base = */ + /* recovered input clock, recovered input clock source is Envy24. */ + write_cs8427_spdif_reg (devc, 4, 0x00); + + /* Serial audio input port data format = I2S. */ + write_cs8427_spdif_reg (devc, 5, BIT2 | BIT0); /* SIDEL=1, SILRPOL=1. */ + + /* Serial audio output port data format = I2S. */ + write_cs8427_spdif_reg (devc, 6, BIT2 | BIT0); /* SODEL=1, SOLRPOL=1. */ + + /* Turn off CS8427 interrupt stuff that we don't implement in our hardware. */ + write_cs8427_spdif_reg (devc, 9, 0x00); + write_cs8427_spdif_reg (devc, 10, 0x00); + write_cs8427_spdif_reg (devc, 11, 0x00); + write_cs8427_spdif_reg (devc, 12, 0x00); + write_cs8427_spdif_reg (devc, 13, 0x00); + write_cs8427_spdif_reg (devc, 14, 0x00); + + /* Unmask the input PLL lock, V, confidence, biphase, parity status bits. */ + write_cs8427_spdif_reg (devc, 17, + (unsigned char) BIT4 | BIT3 | BIT2 | BIT1 | BIT0); + + /* Registers 32-55 window to CS buffer. */ + /* Inhibit D->E transfers from overwriting first 5 bytes of CS data. */ + /* Inhibit D->E transfers (all) of CS data. */ + /* Allow E->F transfers of CS data. */ + /* One-byte mode: both A/B channels get same written CS data. */ + /* A channel info is output to chip's EMPH* pin. */ + /* */ + write_cs8427_spdif_reg (devc, 18, 0x18); + + /* Use internal buffer to transmit User (U) data. */ + /* Chip's U pin is an output. */ + /* Transmit all 0's for user data. */ + /* */ + write_cs8427_spdif_reg (devc, 19, 0x10); + + /* Turn on chip's RUN bit, rock and roll! */ + /* */ + write_cs8427_spdif_reg (devc, 4, read_cs8427_spdif_reg (devc, 4) | BIT6); + InitConsumerModeCS (devc); + +} + +#if 0 +static void +WriteCsField (envy24_devc * devc, unsigned char bByteNum, + unsigned short bMask, unsigned short bBits) +/* +***************************************************************************** +* Writes a specific field within the Channel Status block buffer. +* bByteNum is CS byte number to access (0..23). +* bMask is the field to be written (set bits define field). +* bBits is the value to write into that field. +* +* Assumes: Reg 18 Bit 5 is cleared so CS buffer is accessible. +* Reg 18 Bit 2 is set so E->F buffer transfer is stopped. +****************************************************************************/ +{ + /* Get current reg value. */ + unsigned char bTemp = ReadCsByte (devc, bByteNum); + + /* Clear field to be written. */ + bTemp &= ~(bMask); + + /* Set new values. */ + WriteCsByte (devc, bByteNum, (unsigned char) (bTemp | (bBits & bMask))); +} +#endif + +/*ARGSUSED*/ +void +write_cs8427_spdif (envy24_devc * devc, int d) +{ + int i; + + if (devc->syncsource == SYNC_INTERNAL) + { + write_cs8427_spdif_reg (devc, 4, + read_cs8427_spdif_reg (devc, 4) & ~BIT0); + } + else + { + write_cs8427_spdif_reg (devc, 4, + read_cs8427_spdif_reg (devc, 4) | BIT0); + } + + lock_cs8427_spdif (devc); + for (i = 0; i < 24; i++) + { + WriteCsByte (devc, i, devc->spdif_cbits[i]); + } + unlock_cs8427_spdif (devc); +} + +/* + * Terratec stuff + */ + +/*============================================================================= + Function : IIC_GetSDA +------------------------------------------------------------------------------- + Description : + Returns : unsigned char -> + Parameters : unsigned long dwPortAddr -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +/*ARGSUSED*/ +static unsigned char +IIC_GetSDA (envy24_devc * devc, unsigned long dwPortAddr) +{ + + unsigned char bReg, bSDA; + + /* FMB TEST: RW line */ + /* set write mask */ + bReg = ~(0x08); /* writeable */ + envy24_write_cci (devc, 0x21, bReg); + + /* set RW line LOW */ + bReg = 0; /* writeable */ + envy24_write_cci (devc, 0x20, bReg); + + /* Set direction: SDA to input and SCL to output */ + bReg = envy24_read_cci (devc, 0x22); + bReg |= 0x20; /* SCL output = 1 */ + bReg &= ~0x10; /* SDA input = 0 */ + envy24_write_cci (devc, 0x22, bReg); + + /* Get SDA line state */ + bSDA = envy24_read_cci (devc, 0x20); + + + /* set RW line HIGH */ + bReg = 0x08; /* writeable */ + envy24_write_cci (devc, 0x20, bReg); + + return 1; + /* return ((bSDA & 0x10) == 0x10); */ + +} + +/*============================================================================= + Function : IIC_SetIic +------------------------------------------------------------------------------- + Description : + Returns : void -> + Parameters : unsigned int dwPortAddr -> + : unsigned char fSDA -> + : unsigned char fSCL -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +/*ARGSUSED*/ +static void +IIC_SetIic (envy24_devc * devc, unsigned int dwPortAddr, unsigned char fSDA, + unsigned char fSCL) +{ + unsigned char bReg; + + /* Set direction: SDA and SCL to output */ + bReg = envy24_read_cci (devc, 0x22); + bReg |= (0x20 | 0x10); /* 1 -> output */ + envy24_write_cci (devc, 0x22, bReg); + + /* set write mask */ + bReg = ~(0x20 | 0x10); /* writeable */ + envy24_write_cci (devc, 0x21, bReg); + + /* Set line state */ + /* FMB TEST: RW line */ + bReg = 0x08; +/* bReg = 0; */ + if (fSDA) + bReg += 0x10; + if (fSCL) + bReg += 0x20; + envy24_write_cci (devc, 0x20, bReg); + +} + +/*============================================================================= + Function : IIC_Start +------------------------------------------------------------------------------- + Description : + Returns : void -> + Parameters : unsigned int dwPortAddr -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +/*__inline */ +static void +IIC_Start (envy24_devc * devc, unsigned int dwPortAddr) +{ + /* falling edge of SDA while SCL is HIGH */ + IIC_SetIic (devc, dwPortAddr, 1, 1); + IIC_SetIic (devc, dwPortAddr, 0, 1); +} + +/*============================================================================= + Function : IIC_Stop +------------------------------------------------------------------------------- + Description : + Returns : void -> + Parameters : unsigned int dwPortAddr -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +/*__inline */ + +static void +IIC_Stop (envy24_devc * devc, unsigned int dwPortAddr) +{ + /* rising edge of SDA while SCL is HIGH */ + IIC_SetIic (devc, dwPortAddr, 0, 1); + IIC_SetIic (devc, dwPortAddr, 1, 1); +} + + +/*============================================================================= + Function : IIC_SendByte +------------------------------------------------------------------------------- + Description : + Returns : unsigned char -> + Parameters : unsigned int dwPortAddr -> + : unsigned char bByte -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +/*__inline */ +static unsigned char +IIC_SendByte (envy24_devc * devc, unsigned int dwPortAddr, + unsigned char bByte) +{ + unsigned char bDataBit, bAck; + int i; + + for (i = 7; i >= 0; i--) /* send byte (MSB first) */ + { + bDataBit = (bByte >> i) & 0x01; + + IIC_SetIic (devc, dwPortAddr, bDataBit, 0); + IIC_SetIic (devc, dwPortAddr, bDataBit, 1); + } /* end for i */ + + IIC_SetIic (devc, dwPortAddr, 1, 0); + + /* Get acknowledge */ + IIC_SetIic (devc, dwPortAddr, 1, 1); + bAck = IIC_GetSDA (devc, dwPortAddr); + /* FMB this is a start condition but never mind */ + IIC_SetIic (devc, dwPortAddr, 0, 0); + return 1; + /* return (!bAck); *//* bAck = 0 --> success */ +} + +/*============================================================================= + Function : IIC_WriteByte +------------------------------------------------------------------------------- + Description : + Returns : unsigned char -> + Parameters : unsigned int dwPortAddr -> + : unsigned char bIicAddress -> + : unsigned char bByte -> +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static unsigned char +IIC_WriteByte (envy24_devc * devc, int dwPortAddr, unsigned char bIicAddress, + unsigned char bByte) +{ + IIC_Start (devc, dwPortAddr); + + /* send IIC address and data byte */ + if (!IIC_SendByte (devc, dwPortAddr, bIicAddress)) + { + cmn_err (CE_CONT, "IIC_SendByte 1 failed\n"); + goto FAILED; + } + if (!IIC_SendByte (devc, dwPortAddr, bByte)) + { + cmn_err (CE_CONT, "IIC_SendByte 2 failed\n"); + goto FAILED; + } + + IIC_Stop (devc, dwPortAddr); + return 1; + +FAILED: + IIC_Stop (devc, dwPortAddr); + return 0; +} + +/*============================================================================= + FUNCTION: HW_AK4525_SetChipSelect +------------------------------------------------------------------------------- + PURPOSE: + PARAMETERS: PDEVICE_EXTENSION pDevExt - contains the port address + and the OUT_SEL state + unsigned char bCsMask - AK4525 CS Mask: 1 means selected + RETURNS: int +------------------------------------------------------------------------------- + NOTES: - We use the fact that the parameter bCsMask has the same layout + as the IIC command byte we have to send to the PCF8574 + (wrong polarity, however) + - We also have to care that we don't change the output switch + setting. We remember the value in the Device Extension + - It is important that the CODECS are deselected (CS to HIGH) + after sue. Otherwise some IIC functions might accidentally + change our CODEC registers!!! +=============================================================================*/ +static unsigned char +HW_AK4525_SetChipSelect (envy24_devc * devc, unsigned char bCsMask) +{ + unsigned char bDataByte; + /* check validity */ + if (bCsMask > 0x0F) + { + cmn_err (CE_CONT, "Invalid bCsMask %02x\n", bCsMask); + return 0; /* invalid */ + } + + /* Format data byte */ + bDataByte = (~bCsMask) & 0x0F; + /* FMB PCF8574 HIGH means -10dbV, */ + /* OutputLevelSwitch is 1 for +4dbU */ + /* if (!pDevExt->Ews88mt.OutputLevelSwitch) */ + /* bDataByte += 0x40; */ + + /* Set Chip select to LOW for selected Codecs (via IIC) */ + if (!IIC_WriteByte (devc, devc->ccs_base, 0x48, bDataByte)) + { + cmn_err (CE_CONT, "IIC_WriteByte failed\n"); + return 0; + } + + return 1; +} + +#define HW_AK4525_SetLines IIC_SetIic + +/*============================================================================= + FUNCTION: HW_AK4525_WriteRegister +------------------------------------------------------------------------------- + PURPOSE: + PARAMETERS: unsigned int port - port address for IIC port + unsigned char bCsMask - CoDec bitmask (bits 0..3) + or 0xFF for all codecs together + unsigned char bRegIdx - offset to desired register + unsigned char bReg - new register value + RETURNS: +------------------------------------------------------------------------------- + NOTES: The Chip Selects must have been set prior to this call + PCF8574_CODEC_IIC_ADDRESS + + +-----------------------------------------------------------------+ + | B15 B14 B13 B12 B11 B10 B09 B08|B07 B06 B05 B04 B03 B02 B01 B00 | + +--------+---+-------------------+--------------------------------+ + | C1 C0 |R/W|A4 A3 A2 A1 A0 |D7 D6 D5 D4 D3 D2 D1 D0 | + | 1 0 |1 | <= 7 | data | + +--------+---+-------------------+--------------------------------+ + +=============================================================================*/ +static unsigned char +HW_AK4525_WriteRegister (envy24_devc * devc, unsigned char bCsMask, + unsigned char bRegIdx, unsigned char bRegData) +{ + + unsigned short wCmd; + unsigned char i; + unsigned int port = devc->ccs_base; + unsigned char fData; + + /* format buffer */ + wCmd = 0xA000 + /* chip address + R/W */ + (((unsigned short) bRegIdx) << 8) + /* register address */ + bRegData; + + /* start write cycle */ + if (!HW_AK4525_SetChipSelect (devc, bCsMask)) + { + cmn_err (CE_CONT, "HW_AK4525_SetChipSelect failed\n"); + return 0; /* = CS to LOW -> data latched */ + } + + + for (i = 0; i < 16; i++) + { + fData = (wCmd & 0x8000) ? 1 : 0; + HW_AK4525_SetLines (devc, port, fData, 0); + HW_AK4525_SetLines (devc, port, fData, 1); /* data is clocked in on rising edge of CCLK */ + wCmd <<= 1; + } + + /* leave data line HIGH (= default for IIC) */ + HW_AK4525_SetLines (devc, port, fData, 0); /* data is clocked in on rising edge of CCLK */ + + /* end write cycle */ + if (!HW_AK4525_SetChipSelect (devc, 0x00)) + { + cmn_err (CE_CONT, "HW_AK4525_SetChipSelect 2 failed\n"); + return 0; /* = all CS to HIGH -> CS to default */ + } + + /* default */ + HW_AK4525_SetLines (devc, port, 1, 1); /* data is clocked in on rising edge of CCLK */ + + return 1; +} + +#define EWX_CLK 0x20 +#define EWX_DTA 0x10 + +static void +ewx2496_iic_write_byte (envy24_devc * devc, unsigned char data, + unsigned char save) +{ + int i; + unsigned char gpio; + + for (i = 0; i < 8; i++) + { + int fData = (data & 0x80) ? 1 : 0; + + gpio = 0x08; + if (fData) + gpio |= EWX_DTA; /* DATA */ + envy24_write_cci (devc, 0x20, gpio | save); + envy24_write_cci (devc, 0x20, gpio | EWX_CLK | save); /* Clock pulse */ + envy24_write_cci (devc, 0x20, gpio | save); + + data <<= 1; + } + + envy24_write_cci (devc, 0x20, EWX_DTA | save); + oss_udelay (3); + envy24_write_cci (devc, 0x20, EWX_DTA | EWX_CLK | save); /* Clock pulse (ACK) */ + oss_udelay (1); + envy24_write_cci (devc, 0x20, EWX_DTA | save); + oss_udelay (1); +} + +static unsigned char +ewx2496_iic_read_byte (envy24_devc * devc, unsigned char save) +{ + int i; + unsigned char data = 0; + int b; + + PRT_STATUS (0x01); + save |= EWX_DTA; + envy24_write_cci (devc, 0x20, save); + oss_udelay (10); +#if 0 + save &= ~8; /* R/W bit */ + envy24_write_cci (devc, 0x22, ~EWX_DTA); /* GPIO direction */ + envy24_write_cci (devc, 0x21, EWX_DTA); /* GPIO write mask */ +#endif + + for (i = 0; i < 8; i++) + { + + envy24_write_cci (devc, 0x20, EWX_CLK | save); /* Clock pulse */ + b = envy24_read_cci (devc, 0x20); + if (b & EWX_DTA) + data |= 0x80; + envy24_write_cci (devc, 0x20, save); + + data >>= 1; + } + + envy24_write_cci (devc, 0x22, 0xff); /* GPIO direction */ + envy24_write_cci (devc, 0x21, 0x00); /* GPIO write mask */ + envy24_write_cci (devc, 0x20, (save & ~EWX_DTA) | 0x08); /* Low for the ACK bit */ + envy24_write_cci (devc, 0x20, (save & ~EWX_DTA) | 0x08 | EWX_CLK); /* Clock for the ACK bit */ + envy24_write_cci (devc, 0x20, save | 0x08); + oss_udelay (10); + return data; +} + +static void +ewx2496_iic_write (envy24_devc * devc, int addr, + unsigned char bRegIdx, unsigned char bRegData) +{ + unsigned char save; + + /* start write cycle */ + envy24_write_cci (devc, 0x22, 0xff); /* GPIO direction */ + envy24_write_cci (devc, 0x21, 0x00); /* GPIO write mask */ + + save = (envy24_read_cci (devc, 0x20) & ~(EWX_CLK | EWX_DTA)) | 0x08 /* R/W bit */ + ; + /* Send start */ + oss_udelay (1); + envy24_write_cci (devc, 0x20, save | EWX_CLK | EWX_DTA); + oss_udelay (1); + envy24_write_cci (devc, 0x20, save | EWX_CLK); + oss_udelay (1); + envy24_write_cci (devc, 0x20, save); + oss_udelay (1); + + ewx2496_iic_write_byte (devc, addr, save); + ewx2496_iic_write_byte (devc, bRegIdx, save); + ewx2496_iic_write_byte (devc, bRegData, save); + + /* Send stop */ + oss_udelay (1); + envy24_write_cci (devc, 0x20, save); + oss_udelay (1); + envy24_write_cci (devc, 0x20, save | EWX_CLK); + oss_udelay (1); + envy24_write_cci (devc, 0x20, save | EWX_CLK | EWX_DTA); +} + +static unsigned char +ewx2496_iic_read (envy24_devc * devc, int addr, unsigned char bRegIdx) +{ + unsigned char save, data; + + /* ewx2496_iic_write(devc, addr, bRegIdx, 0x55); */ + PRT_STATUS (0x80); + + envy24_write_cci (devc, 0x22, 0xff); /* GPIO direction */ + envy24_write_cci (devc, 0x21, 0x00); /* GPIO write mask */ + + save = (envy24_read_cci (devc, 0x20) & ~(EWX_DTA | EWX_CLK)) | 0x08 /* R/W bit */ + ; + PRT_STATUS (0x02); + /* Send start */ + oss_udelay (1); + envy24_write_cci (devc, 0x20, save | EWX_CLK | EWX_DTA); + oss_udelay (1); + envy24_write_cci (devc, 0x20, save | EWX_CLK); + oss_udelay (1); + envy24_write_cci (devc, 0x20, save); + oss_udelay (1); + + ewx2496_iic_write_byte (devc, addr | 0x01, save); + oss_udelay (10); + data = ewx2496_iic_read_byte (devc, save); + + /* Send stop */ + oss_udelay (1); + envy24_write_cci (devc, 0x20, save); + oss_udelay (1); + envy24_write_cci (devc, 0x20, save | EWX_CLK); + oss_udelay (1); + envy24_write_cci (devc, 0x20, save | EWX_CLK | EWX_DTA); + oss_udelay (1); + PRT_STATUS (0x00); + return data; +} + +static unsigned char +write_ewx2496_codec (envy24_devc * devc, + unsigned char bRegIdx, unsigned char bRegData) +{ + + unsigned short wCmd; + unsigned char i; + unsigned char fData; + unsigned char gpio, save; + + /* format buffer */ + wCmd = 0xA000 + /* chip address + R/W */ + (((unsigned short) bRegIdx) << 8) + /* register address */ + bRegData; + + /* start write cycle */ + envy24_write_cci (devc, 0x22, 0xff); /* GPIO direction */ + envy24_write_cci (devc, 0x21, 0x00); /* GPIO write mask */ + + save = envy24_read_cci (devc, 0x20) & ~0x39; + envy24_write_cci (devc, 0x20, save | 0x09); /* CS inactive */ + oss_udelay (200); + envy24_write_cci (devc, 0x20, 0x08); /* CS active */ + oss_udelay (200); + + gpio = 0x08; + + for (i = 0; i < 16; i++) + { + fData = (wCmd & 0x8000) ? 1 : 0; + + gpio = 0x08; + if (fData) + gpio |= 0x10; /* DATA */ + envy24_write_cci (devc, 0x20, gpio | save); + oss_udelay (20); + envy24_write_cci (devc, 0x20, gpio | 0x20 | save); /* Clock pulse */ + oss_udelay (200); + envy24_write_cci (devc, 0x20, gpio | save); + + wCmd <<= 1; + } + + oss_udelay (50); + envy24_write_cci (devc, 0x20, 0x01); /* CS inactive */ + oss_udelay (20); + + return 1; +} + +/* Register offsets */ +enum +{ + AK4525_REG_POWER, + AK4525_REG_RESET, + AK4525_REG_FORMAT, + AK4525_REG_DEEMPHASIS, + AK4525_REG_LEFT_INPUT, + AK4525_REG_RIGHT_INPUT, + AK4525_REG_LEFT_OUTPUT, + AK4525_REG_RIGHT_OUTPUT +}; + + +/*============================================================================= + Function : HW_AK4525_Reset +------------------------------------------------------------------------------- + Description : + Returns : unsigned char + Parameters : PCUST_HW_INSTANCE_DATA devc - + int nCodecIdx -> 0..3 index of Codec ,-1 -> all +------------------------------------------------------------------------------- + Notes : +=============================================================================*/ +static unsigned char +HW_AK4525_Reset (envy24_devc * devc, int nCodecIdx) +{ + unsigned char bCodecMask = 0; + if (nCodecIdx == -1) + bCodecMask = 0x0F; + else + bCodecMask = 0x01 << (nCodecIdx); + + if (!HW_AK4525_WriteRegister (devc, bCodecMask, /* 0x0F -> set mode for all codecs */ + AK4525_REG_RESET, 0x00)) /* DACs,ADCs -> reset */ + { + cmn_err (CE_CONT, "REG_RESET failed\n"); + return 0; + } + + if (!HW_AK4525_WriteRegister (devc, bCodecMask, /* 0x0F -> set mode for all codecs */ + AK4525_REG_FORMAT, 0x60)) /* IIS, 256 fsn, normal speed */ + return 0; + + if (!HW_AK4525_WriteRegister (devc, bCodecMask, /* 0x0F -> set mode for all codecs */ + AK4525_REG_RESET, 0x03)) /* DACs,ADCs -> normal operation */ + return 0; + + if (!HW_AK4525_WriteRegister (devc, bCodecMask, /* 0x0F -> set mode for all codecs */ + AK4525_REG_POWER, 0x07)) /* power on */ + return 0; + + if (!HW_AK4525_WriteRegister (devc, bCodecMask, /* soft mute timeout --> short */ + AK4525_REG_DEEMPHASIS, 0x19)) + return 0; + + if (!HW_AK4525_WriteRegister (devc, + bCodecMask, AK4525_REG_LEFT_INPUT, 0x7f)) + return 0; + + if (!HW_AK4525_WriteRegister (devc, + bCodecMask, AK4525_REG_RIGHT_INPUT, 0x7f)) + return 0; + + if (!HW_AK4525_WriteRegister (devc, + bCodecMask, AK4525_REG_LEFT_OUTPUT, 0x7f)) + return 0; + + if (!HW_AK4525_WriteRegister (devc, + bCodecMask, AK4525_REG_RIGHT_OUTPUT, 0x7f)) + return 0; + + return 1; +} + +unsigned char +ewx2496_AK4525_Reset (envy24_devc * devc, int nCodecIdx) +{ + unsigned char bCodecMask = 0; + if (nCodecIdx == -1) + bCodecMask = 0x0F; + else + bCodecMask = 0x01 << (nCodecIdx); + envy24_write_cci (devc, 0x20, 0x03); /* Default value */ + + if (!write_ewx2496_codec (devc, AK4525_REG_RESET, 0x00)) /* DACs,ADCs -> reset */ + { + cmn_err (CE_CONT, "REG_RESET failed\n"); + return 0; + } + + if (!write_ewx2496_codec (devc, AK4525_REG_FORMAT, 0x60)) /* IIS, 256 fsn, normal speed */ + return 0; + + if (!write_ewx2496_codec (devc, AK4525_REG_RESET, 0x03)) /* DACs,ADCs -> normal operation */ + return 0; + + if (!write_ewx2496_codec (devc, AK4525_REG_POWER, 0x07)) /* power on */ + return 0; + + if (!write_ewx2496_codec (devc, AK4525_REG_DEEMPHASIS, 0x19)) + return 0; + + if (!write_ewx2496_codec (devc, AK4525_REG_LEFT_INPUT, 0x7f)) + return 0; + + if (!write_ewx2496_codec (devc, AK4525_REG_RIGHT_INPUT, 0x7f)) + return 0; + + if (!write_ewx2496_codec (devc, AK4525_REG_LEFT_OUTPUT, 0x7f)) + return 0; + + if (!write_ewx2496_codec (devc, AK4525_REG_RIGHT_OUTPUT, 0x7f)) + return 0; + + return 1; +} + +static void +ews88_init_codecs (envy24_devc * devc) +{ + if (!HW_AK4525_Reset (devc, -1)) + cmn_err (CE_CONT, "Envy24: EWS88MT reset failed\n");; +} + +static void +ewx2496_init_codecs (envy24_devc * devc) +{ + if (!ewx2496_AK4525_Reset (devc, -1)) + cmn_err (CE_CONT, "Envy24: EWS88MT reset failed\n");; +} + +static int +envy24_set_akm (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24_devc *devc = mixer_devs[dev]->devc; + int codec, level, i; + static unsigned char levels[] = { 0x7f, 0x8c, 0x98 }; + + if (ctrl >= 0xff) + return OSS_EINVAL; + + if (cmd == SNDCTL_MIX_READ) + { + if (envy24_gain_sliders) + return devc->akm_gains[ctrl]; + return devc->akm_gains[ctrl]; + } + else if (cmd == SNDCTL_MIX_WRITE) + { + + if (envy24_gain_sliders) + level = value & 0xff; + else + { + if (value > 2) + return OSS_EINVAL; + level = levels[value]; + } + + codec = ctrl & 0xf0; + + for (i = 0; i < 4; i++) + { + if (!(ctrl & (1 << i))) + continue; + + write_codec (devc, codec, 4 + i, level); + } + return devc->akm_gains[ctrl] = value; + } + return OSS_EINVAL; +} + +static int +envy24_set_ap (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24_devc *devc = mixer_devs[dev]->devc; + int level; + static unsigned char levels[] = { 0x60, 0x6f, 0x7f }; + + if (ctrl >= 0xff) + return OSS_EINVAL; + + if (cmd == SNDCTL_MIX_READ) + { + if (envy24_gain_sliders) + return devc->akm_gains[ctrl]; + return devc->akm_gains[ctrl]; + } + else if (cmd == SNDCTL_MIX_WRITE) + { + + if (envy24_gain_sliders) + level = value & 0xff; + else + { + if (value > 2) + return OSS_EINVAL; + level = levels[value]; + } + + write_ap_codec (devc, 4, level); + write_ap_codec (devc, 5, level); + return devc->akm_gains[ctrl] = value; + } + return OSS_EINVAL; +} + +static int +envy24_set_d410 (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24_devc *devc = mixer_devs[dev]->devc; + int level, n; + static unsigned char levels[] = { 0x00, 0x00, 0x0c }; + + if (ctrl >= 8) + return OSS_EINVAL; + + if (cmd == SNDCTL_MIX_READ) + { + if (envy24_gain_sliders) + return devc->akm_gains[ctrl]; + return devc->akm_gains[ctrl]; + } + else if (cmd == SNDCTL_MIX_WRITE) + { + + if (envy24_gain_sliders) + level = 255 - value * 2; + else + { + if (value > 2) + return OSS_EINVAL; + level = levels[value]; + } + + if (ctrl < 6) + n = ctrl + 2; + else + n = ctrl + 5; + write_ap_codec (devc, n, level); + if (devc->skipdevs == 2) + write_ap_codec (devc, n + 1, level); + return devc->akm_gains[ctrl] = value; + } + return OSS_EINVAL; +} + +static unsigned char +write_ewx2496_codec (envy24_devc * devc, + unsigned char bRegIdx, unsigned char bRegData); + +static int +envy24_set_ewx2496 (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24_devc *devc = mixer_devs[dev]->devc; + int level; + static unsigned char levels[] = { 0x60, 0x6f, 0x7f }; + + if (ctrl >= 0xff) + return OSS_EINVAL; + + if (cmd == SNDCTL_MIX_READ) + { + if (envy24_gain_sliders) + return devc->akm_gains[ctrl]; + return devc->akm_gains[ctrl]; + } + else if (cmd == SNDCTL_MIX_WRITE) + { + + if (envy24_gain_sliders) + level = value & 0xff; + else + { + if (value > 2) + return OSS_EINVAL; + level = levels[value]; + } + + write_ewx2496_codec (devc, 6, level); + write_ewx2496_codec (devc, 7, level); + return devc->akm_gains[ctrl] = value; + } + return OSS_EINVAL; +} + +static unsigned char +HW_AK4525_WriteRegister (envy24_devc * devc, unsigned char bCsMask, + unsigned char bRegIdx, unsigned char bRegData); + +static int +envy24_set_ews88 (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24_devc *devc = mixer_devs[dev]->devc; + int codec, level, i; + static unsigned char levels[] = { 0x7f, 0x8c, 0x98 }; + + if (ctrl >= 0xff) + return OSS_EINVAL; + + if (cmd == SNDCTL_MIX_READ) + { + if (envy24_gain_sliders) + return devc->akm_gains[ctrl]; + return devc->akm_gains[ctrl]; + } + else if (cmd == SNDCTL_MIX_WRITE) + { + + if (envy24_gain_sliders) + level = value & 0xff; + else + { + if (value > 2) + return OSS_EINVAL; + level = levels[value]; + } + + codec = ctrl & 0xf0; + + for (i = 0; i < 4; i++) + { + if (!(ctrl & (1 << i))) + continue; + + if (!HW_AK4525_WriteRegister (devc, codec >> 4, 4 + i, level)) + return OSS_EIO; + } + return devc->akm_gains[ctrl] = value; + } + return OSS_EINVAL; +} + +/*ARGSUSED*/ +static int +create_akm_mixer (int dev, envy24_devc * devc, int root) +{ + int i, mask = devc->outportmask, group, err, skip, codec, ports; + int typ = MIXT_ENUM, range = 3; + char tmp[64]; + + if ((group = mixer_ext_create_group (dev, 0, "ENVY24_GAIN")) < 0) + return group; + + skip = devc->skipdevs; + if (skip != 2) + skip = 1; + + if (envy24_gain_sliders) + { + typ = MIXT_MONOSLIDER; + range = 144; + + for (i = 0; i < 0xff; i++) + devc->akm_gains[i] = 0x7f; + } + else + for (i = 0; i < 0xff; i++) + devc->akm_gains[i] = 0; + + for (i = 0; i < 8; i += skip) + { + + if (!(mask & (1 << i))) + continue; /* Not present */ + + if (devc->skipdevs == 2) + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDOUT"); + else + sprintf (tmp, "ENVY24_OUT%d/%d", i + 1, i + 2); + + codec = (i > 1) ? AKM_B : AKM_A; + ports = 0x0c; /* Both output ports */ + if ((err = mixer_ext_create_control (dev, group, + codec | ports, envy24_set_akm, + typ, + tmp, range, + MIXF_MAINVOL | + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return err; + } + else + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDOUTL"); + else if (i == 9) + strcpy (tmp, "ENVY24_SPDOUTR"); + else + sprintf (tmp, "ENVY24_OUT%d", i + 1); + + codec = (i > 1) ? AKM_B : AKM_A; + ports = (i & 1) ? 0x08 : 0x04; + if ((err = mixer_ext_create_control (dev, group, + codec | ports, envy24_set_akm, + typ, + tmp, range, + MIXF_MAINVOL | + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return err; + } + } + + mask = devc->inportmask; + for (i = 0; i < 8; i += skip) + { + + if (!(mask & (1 << i))) + continue; /* Not present */ + + if (devc->skipdevs == 2) + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDIN"); + else + sprintf (tmp, "ENVY24_IN%d/%d", i + 1, i + 2); + + codec = (i > 1) ? AKM_B : AKM_A; + ports = 0x03; /* Both output ports */ + if ((err = mixer_ext_create_control (dev, group, + codec | ports, envy24_set_akm, + typ, + tmp, range, + MIXF_RECVOL | + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return err; + } + else + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDINL"); + else if (i == 9) + strcpy (tmp, "ENVY24_SPDINR"); + else + sprintf (tmp, "ENVY24_IN%d", i + 1); + + codec = (i > 1) ? AKM_B : AKM_A; + ports = (i & 1) ? 0x02 : 0x01; + if ((err = mixer_ext_create_control (dev, group, + codec | ports, envy24_set_akm, + typ, + tmp, range, + MIXF_RECVOL | + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return err; + } + } + + return 0; +} + +/*ARGSUSED*/ +static int +create_ap_mixer (int dev, envy24_devc * devc, int root) +{ + int i, mask = devc->outportmask, group, err, skip, codec, ports; + int typ = MIXT_ENUM, range = 3; + char tmp[64]; + + if ((group = mixer_ext_create_group (dev, 0, "ENVY24_GAIN")) < 0) + return group; + + skip = devc->skipdevs; + if (skip != 2) + skip = 1; + + if (envy24_gain_sliders) + { + typ = MIXT_MONOSLIDER; + range = 164; + + for (i = 0; i < 0xff; i++) + devc->akm_gains[i] = 0x7f; + } + else + for (i = 0; i < 0xff; i++) + devc->akm_gains[i] = 0; + + for (i = 0; i < 8; i += skip) + { + + if (!(mask & (1 << i))) + continue; /* Not present */ + + if (devc->skipdevs == 2) + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDOUT"); + else + sprintf (tmp, "ENVY24_OUT%d/%d", i + 1, i + 2); + + codec = (i > 1) ? AKM_B : AKM_A; + ports = 0x0c; /* Both output ports */ + if ((err = mixer_ext_create_control (dev, group, + codec | ports, envy24_set_ap, + typ, + tmp, range, + MIXF_MAINVOL | + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return err; + } + else + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDOUTL"); + else if (i == 9) + strcpy (tmp, "ENVY24_SPDOUTR"); + else + sprintf (tmp, "ENVY24_OUT%d", i + 1); + + codec = (i > 1) ? AKM_B : AKM_A; + ports = (i & 1) ? 0x08 : 0x04; + if ((err = mixer_ext_create_control (dev, group, + codec | ports, envy24_set_ap, + typ, + tmp, range, + MIXF_MAINVOL | + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return err; + } + } + + return 0; +} + +/*ARGSUSED*/ +static int +create_d410_mixer (int dev, envy24_devc * devc, int root) +{ + int i, mask = devc->outportmask, group, err, skip; + int typ = MIXT_ENUM, range = 3; + int enum_mask = 0xffffff; + char tmp[64]; + + if ((group = mixer_ext_create_group (dev, 0, "ENVY24_GAIN")) < 0) + return group; + + skip = devc->skipdevs; + if (skip != 2) + skip = 1; + + if (envy24_gain_sliders) + { + typ = MIXT_MONOSLIDER; + range = 127; + + for (i = 0; i < 0xff; i++) + devc->akm_gains[i] = 127; + } + else + { + for (i = 0; i < 0xff; i++) + devc->akm_gains[i] = 1; + enum_mask = 0x6; + } + + for (i = 0; i < 8; i += skip) + { + + if (!(mask & (1 << i))) + continue; /* Not present */ + + if (devc->skipdevs == 2) + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDOUT"); + else + sprintf (tmp, "ENVY24_OUT%d/%d", i + 1, i + 2); + + if ((err = mixer_ext_create_control (dev, group, + i, envy24_set_d410, + typ, + tmp, range, + MIXF_MAINVOL | + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return err; + } + else + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDOUTL"); + else if (i == 9) + strcpy (tmp, "ENVY24_SPDOUTR"); + else + sprintf (tmp, "ENVY24_OUT%d", i + 1); + + if ((err = mixer_ext_create_control (dev, group, + i, envy24_set_d410, + typ, + tmp, range, + MIXF_MAINVOL | + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return err; + } + envy24_set_enum_mask (dev, err, enum_mask); + } + + return 0; +} + +/*ARGSUSED*/ +static int +create_ewx2496_mixer (int dev, envy24_devc * devc, int root) +{ + int i, mask = devc->outportmask, group, err, skip, codec, ports; + int typ = MIXT_ENUM, range = 3; + char tmp[64]; + + if ((group = mixer_ext_create_group (dev, 0, "ENVY24_GAIN")) < 0) + return group; + + skip = devc->skipdevs; + if (skip != 2) + skip = 1; + + if (envy24_gain_sliders) + { + typ = MIXT_MONOSLIDER; + range = 164; + + for (i = 0; i < 0xff; i++) + devc->akm_gains[i] = 0x7f; + } + else + for (i = 0; i < 0xff; i++) + devc->akm_gains[i] = 0; + + for (i = 0; i < 8; i += skip) + { + + if (!(mask & (1 << i))) + continue; /* Not present */ + + if (devc->skipdevs == 2) + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDOUT"); + else + sprintf (tmp, "ENVY24_OUT%d/%d", i + 1, i + 2); + + codec = (i > 1) ? AKM_B : AKM_A; + ports = 0x0c; /* Both output ports */ + if ((err = mixer_ext_create_control (dev, group, + codec | ports, + envy24_set_ewx2496, typ, tmp, + range, + MIXF_MAINVOL | + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return err; + } + else + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDOUTL"); + else if (i == 9) + strcpy (tmp, "ENVY24_SPDOUTR"); + else + sprintf (tmp, "ENVY24_OUT%d", i + 1); + + codec = (i > 1) ? AKM_B : AKM_A; + ports = (i & 1) ? 0x08 : 0x04; + if ((err = mixer_ext_create_control (dev, group, + codec | ports, + envy24_set_ewx2496, typ, tmp, + range, + MIXF_MAINVOL | + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return err; + } + } + + return 0; +} + +/*ARGSUSED*/ +static int +create_ews88_mixer (int dev, envy24_devc * devc, int root) +{ + int i, mask = devc->outportmask, group, err, skip, codec, ports; + int typ = MIXT_ENUM, range = 3; + char tmp[64]; + + if ((group = mixer_ext_create_group (dev, 0, "ENVY24_GAIN")) < 0) + return group; + + skip = devc->skipdevs; + if (skip != 2) + skip = 1; + + if (envy24_gain_sliders) + { + typ = MIXT_MONOSLIDER; + range = 164; + + for (i = 0; i < 0xff; i++) + devc->akm_gains[i] = 0x7f; + } + else + for (i = 0; i < 0xff; i++) + devc->akm_gains[i] = 0; + + for (i = 0; i < 8; i += skip) + { + + if (!(mask & (1 << i))) + continue; /* Not present */ + + if (devc->skipdevs == 2) + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDOUT"); + else + sprintf (tmp, "ENVY24_OUT%d/%d", i + 1, i + 2); + + codec = 0x10 << (i / 2); + ports = 0x0c; /* Both output ports */ + if ((err = mixer_ext_create_control (dev, group, + codec | ports, + envy24_set_ews88, typ, tmp, + range, + MIXF_MAINVOL | + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return err; + } + else + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDOUTL"); + else if (i == 9) + strcpy (tmp, "ENVY24_SPDOUTR"); + else + sprintf (tmp, "ENVY24_OUT%d", i + 1); + + codec = 0x10 << (i / 2); + ports = (i & 1) ? 0x08 : 0x04; + if ((err = mixer_ext_create_control (dev, group, + codec | ports, + envy24_set_ews88, typ, tmp, + range, + MIXF_MAINVOL | + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return err; + } + } + + mask = devc->inportmask; + for (i = 0; i < 8; i += skip) + { + + if (!(mask & (1 << i))) + continue; /* Not present */ + + if (devc->skipdevs == 2) + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDIN"); + else + sprintf (tmp, "ENVY24_IN%d/%d", i + 1, i + 2); + + codec = 0x10 << (i / 2); + ports = 0x03; /* Both output ports */ + if ((err = mixer_ext_create_control (dev, group, + codec | ports, + envy24_set_ews88, typ, tmp, + range, + MIXF_RECVOL | + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return err; + } + else + { + if (i == 8) + strcpy (tmp, "ENVY24_SPDINL"); + else if (i == 9) + strcpy (tmp, "ENVY24_SPDINR"); + else + sprintf (tmp, "ENVY24_IN%d", i + 1); + + codec = 0x10 << (i / 2); + ports = (i & 1) ? 0x02 : 0x01; + if ((err = mixer_ext_create_control (dev, group, + codec | ports, + envy24_set_ews88, typ, tmp, + range, + MIXF_RECVOL | + MIXF_READABLE | + MIXF_WRITEABLE)) < 0) + return err; + } + } + + return 0; +} + +/* + * Hoontech DSP24 family + */ + + +typedef union +{ + struct + { + unsigned int data:5; + unsigned int clock:1; + unsigned int res0:2; + } + b; + + unsigned int dwVal; +} +CLBYTE; + +#define HOONTECH_CLOCK 0x20 + +static void +hoontech_write_gpio (envy24_devc * devc, unsigned char data) +{ + envy24_write_cci (devc, 0x22, 0xff); /* GPIO direction */ + envy24_write_cci (devc, 0x21, 0xc0); /* GPIO write mask */ + + envy24_write_cci (devc, 0x20, data | HOONTECH_CLOCK); /* GPIO data */ + envy24_write_cci (devc, 0x20, data & ~HOONTECH_CLOCK); /* GPIO data */ + envy24_write_cci (devc, 0x20, data | HOONTECH_CLOCK); /* GPIO data */ +} + +static void +hoontech_init (envy24_devc * devc) +{ + devc->adsp.b.box = 0; + devc->adsp.b.darear = 0; + devc->adsp.b.id0 = 0; + devc->adsp.b.clock0 = 1; + devc->adsp.b.res0 = 0x0; + + devc->adsp.b.ch1 = 0x01; + devc->adsp.b.ch2 = 0x01; + devc->adsp.b.ch3 = 0x01; + devc->adsp.b.id1 = 1; + devc->adsp.b.clock1 = 1; + devc->adsp.b.res1 = 0x0; + + devc->adsp.b.ch4 = 0x01; + devc->adsp.b.midiin = 0x01; + devc->adsp.b.midi1 = 0x0; + devc->adsp.b.id2 = 2; + devc->adsp.b.clock2 = 1; + devc->adsp.b.res2 = 0x0; + + devc->adsp.b.midi2 = 0x0; + devc->adsp.b.mute = 0x0; + devc->adsp.b.insel = 0x1; + devc->adsp.b.id3 = 3; + devc->adsp.b.clock3 = 1; + devc->adsp.b.res3 = 0x0; + + hoontech_write_gpio (devc, devc->adsp.w.b0); + hoontech_write_gpio (devc, devc->adsp.w.b1); + hoontech_write_gpio (devc, devc->adsp.w.b2); + hoontech_write_gpio (devc, devc->adsp.w.b3); +} + +/**************************************************/ + +static void +default_card_init (envy24_devc * devc) +{ + + if (devc->model_data->flags & MF_AKMCODEC) + { + write_codec (devc, AKM_A | AKM_B, 0, 0x07); + write_codec (devc, AKM_A | AKM_B, 1, 0x03); + write_codec (devc, AKM_A | AKM_B, 2, 0x60); + write_codec (devc, AKM_A | AKM_B, 3, 0x19); + write_codec (devc, AKM_A | AKM_B, 4, 0x7f); + write_codec (devc, AKM_A | AKM_B, 5, 0x7f); + write_codec (devc, AKM_A | AKM_B, 6, 0x7f); + write_codec (devc, AKM_A | AKM_B, 7, 0x7f); + } + + if (devc->model_data->flags & MF_AP) + { /* Init the AK4528 codec of Audiophile 2496 */ + write_ap_codec (devc, 0, 0x07); + write_ap_codec (devc, 1, 0x00); + write_ap_codec (devc, 2, 0x60); + write_ap_codec (devc, 3, 0x0d); + write_ap_codec (devc, 4, 0x7f); + write_ap_codec (devc, 1, 0x03); + write_ap_codec (devc, 5, 0x7f); + } + + if (devc->model_data->flags & MF_D410) + { /* Init the AK4529 codec of Delta 410 */ + OUTB (devc->osdev, + INB (devc->osdev, devc->mt_base + 0x02) & ~0x04, + devc->mt_base + 0x02); + + write_ap_codec (devc, 0x00, 0x0c); + write_ap_codec (devc, 0x01, 0x02); + write_ap_codec (devc, 0x02, 0x00); + write_ap_codec (devc, 0x03, 0x00); + write_ap_codec (devc, 0x04, 0x00); + write_ap_codec (devc, 0x05, 0x00); + write_ap_codec (devc, 0x06, 0x00); + write_ap_codec (devc, 0x07, 0x00); + write_ap_codec (devc, 0x0b, 0x00); + write_ap_codec (devc, 0x0c, 0x00); + write_ap_codec (devc, 0x08, 0x15); + write_ap_codec (devc, 0x0a, 0x3f); + } + + if (devc->model_data->flags & MF_EWS88) + ews88_init_codecs (devc); + + if (devc->model_data->flags & MF_EWX2496) + ewx2496_init_codecs (devc); + + if (devc->model_data->flags & MF_HOONTECH) + hoontech_init (devc); +} + +static int +default_mix_init (envy24_devc * devc, int dev, int group) +{ + int err; + + if (devc->model_data->flags & MF_AKMCODEC) + { + if ((err = create_akm_mixer (dev, devc, group)) < 0) + return err; + } + else if (devc->model_data->flags & MF_AP) + { + if ((err = create_ap_mixer (dev, devc, group)) < 0) + return err; + } + else if (devc->model_data->flags & MF_D410) + { + if ((err = create_d410_mixer (dev, devc, group)) < 0) + return err; + } + else if (devc->model_data->flags & MF_EWS88) + { + if ((err = create_ews88_mixer (dev, devc, group)) < 0) + return err; + } + else if (devc->model_data->flags & MF_EWX2496) + { + if ((err = create_ewx2496_mixer (dev, devc, group)) < 0) + return err; + } + return 0; +} + +/*ARGSUSED*/ +int +cs8427_spdif_ioctl (envy24_devc * devc, int dev, unsigned int cmd, + ioctl_arg arg) +{ + oss_digital_control *ctrl = (oss_digital_control *) arg; + + if (arg == NULL) + return OSS_EINVAL; + + switch (cmd) + { + case SNDCTL_DSP_READCTL: + { + int i, rq; + unsigned char status; + + rq = ctrl->valid; + memset (ctrl, 0, sizeof (*ctrl)); + + ctrl->caps = DIG_CBITIN_FULL | DIG_CBITOUT_FULL; + + if (rq & VAL_CBITIN) + { + for (i = 0; i < 24; i++) + ctrl->cbitin[i] = ReadCsByte (devc, i); + ctrl->valid |= VAL_CBITIN; + } + + if (rq & VAL_CBITOUT) + { + for (i = 0; i < 24; i++) + ctrl->cbitin[i] = devc->spdif_cbits[i]; + ctrl->valid |= VAL_CBITOUT; + } + + + if (rq & VAL_ISTATUS) + { + ctrl->valid |= VAL_ISTATUS; + status = read_cs8427_spdif_reg (devc, 15); + if (status & 0x04) + ctrl->in_data = IND_DATA; + else + ctrl->in_data = IND_AUDIO; + + status = read_cs8427_spdif_reg (devc, 16); + if (status & 0x40) + ctrl->in_errors |= INERR_QCODE_CRC; + if (status & 0x20) + ctrl->in_errors |= INERR_CRC; + + if (status & 0x10) + ctrl->in_locked = LOCK_UNLOCKED; + else + ctrl->in_locked = LOCK_LOCKED; + + if (status & 0x08) + ctrl->in_vbit = VBIT_ON; + else + ctrl->in_vbit = VBIT_OFF; + + if (status & 0x04) + ctrl->in_quality = IN_QUAL_POOR; + else + ctrl->in_quality = IN_QUAL_GOOD; + + if (status & 0x02) + ctrl->in_errors |= INERR_BIPHASE; + + if (status & 0x01) + ctrl->in_errors |= INERR_PARITY; + +#if 1 + /* TODO: Better handling required */ + write_cs8427_spdif_reg (devc, 18, 0x00); +#endif + } + } + return 0; + break; + + case SNDCTL_DSP_WRITECTL: + { + int i, rq; + + rq = ctrl->valid; + memset (ctrl, 0, sizeof (*ctrl)); + + ctrl->caps = DIG_CBITOUT_FULL; + + if (rq & VAL_CBITOUT) + { + for (i = 0; i < 24; i++) + devc->spdif_cbits[i] = ctrl->cbitout[i]; + ctrl->valid |= VAL_CBITOUT; + + lock_cs8427_spdif (devc); + for (i = 0; i < 24; i++) + { + WriteCsByte (devc, i, devc->spdif_cbits[i]); + } + unlock_cs8427_spdif (devc); + } + } + return 0; + break; + + default:; + } + return OSS_EINVAL; +} + +static int +set_spdif_control (int dev, int ctrl, unsigned int cmd, int value) +{ + envy24_devc *devc = mixer_devs[dev]->devc; + + if (cmd == SNDCTL_MIX_READ) + { + switch (ctrl) + { + case 1: + return devc->spdif_pro_mode; + break; + } + + return OSS_EIO; + } + + if (cmd == SNDCTL_MIX_WRITE) + { + switch (ctrl) + { + case 1: + return devc->spdif_pro_mode = !!value; + break; + } + + return OSS_EIO; + } + + return OSS_EIO; +} + +/*ARGSUSED*/ +int +cs8427_spdif_mixer_init (envy24_devc * devc, int dev, int group) +{ + int err; + + if ((group = mixer_ext_create_group (dev, 0, "ENVY24_SPDIF")) < 0) + return group; + + if ((err = mixer_ext_create_control (dev, group, + 1, set_spdif_control, + MIXT_ENUM, + "SPDIF_MODE", 2, + MIXF_READABLE | MIXF_WRITEABLE)) < 0) + return err; + + return 0; +} + +static void +set_ap_speed (envy24_devc * devc) +{ + unsigned char gpio, tmp; + + tmp = devc->speedbits; + if (devc->syncsource != SYNC_INTERNAL) + { + tmp |= 0x10; /* S/PDIF input clock select */ + if (devc->model_data->flags & MF_WCLOCK) /* Has world clock too */ + { + int cmd = envy24_read_cci (devc, 0x20); + cmd |= 0x10; /* S/PDIF */ + if (devc->syncsource == SYNC_WCLOCK) + cmd &= ~0x10; /* World clock */ + envy24_write_cci (devc, 0x20, cmd); + } + } + OUTB (devc->osdev, tmp, devc->mt_base + 0x01); + + if (devc->speed > 48000) + { + write_ap_codec (devc, 0x01, 0x00); + gpio = envy24_read_cci (devc, 0x20); + gpio |= 0x01; /* Turn DFS ON */ + envy24_write_cci (devc, 0x20, gpio); + write_ap_codec (devc, 0x02, 0x65); + write_ap_codec (devc, 0x01, 0x03); + } + else + { + write_ap_codec (devc, 0x01, 0x00); + gpio = envy24_read_cci (devc, 0x20); + gpio &= ~0x01; /* Turn DFS OFF */ + envy24_write_cci (devc, 0x20, gpio); + write_ap_codec (devc, 0x02, 0x60); + write_ap_codec (devc, 0x01, 0x03); + } +} + +envy24_auxdrv_t default_auxdrv = { + default_card_init, + default_mix_init, + NULL, + write_spdif +}; + +envy24_auxdrv_t ap2496_auxdrv = { + default_card_init, + default_mix_init, + init_cs8427_spdif, + write_cs8427_spdif, + cs8427_spdif_ioctl, + read_ap_spdif_reg, + write_ap_spdif_reg, + set_ap_speed, + NULL, + cs8427_spdif_mixer_init +}; + +envy24_auxdrv_t d410_auxdrv = { + default_card_init, + default_mix_init, + init_cs8427_spdif, + write_cs8427_spdif, + cs8427_spdif_ioctl, + read_ap_spdif_reg, + write_ap_spdif_reg, + NULL, + NULL, + cs8427_spdif_mixer_init +}; + +envy24_auxdrv_t ewx2496_auxdrv = { + default_card_init, + default_mix_init, + init_cs8427_spdif, + write_cs8427_spdif, + NULL, + read_ewx2496_spdif_reg, + write_ewx2496_spdif_reg, + NULL, + NULL, + cs8427_spdif_mixer_init +}; |