summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_envy24/envy24_default.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2013-05-03 21:08:42 +0400
committerIgor Pashev <pashev.igor@gmail.com>2013-05-03 21:08:42 +0400
commit1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch)
tree4495d23e7b54ab5700e3839081e797c1eafe0db9 /kernel/drv/oss_envy24/envy24_default.c
downloadoss4-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.c2384
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
+};