Open Sound System |
Do you have problems with sound/audio application development? Don't panic! Click here for help! |
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 #define DEFAULT_FRONTBOX_SETUP 0xfd extern int envy24_gain_sliders; extern int envy24_virtualout; #define EWX_DIG_ADDR 0x24 /* IIC address of digital traceiver chip of 6fire */
Function : IIC_GetSDA ------------------------------------------------------------------------------- Description : Returns : unsigned char -> Parameters : unsigned long dwPortAddr -> ------------------------------------------------------------------------------- Notes : =============================================================================
static unsigned char IIC_GetSDA (envy24_devc * devc) { 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 : =============================================================================
static void IIC_SetIic (envy24_devc * devc, 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; 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) { /* falling edge of SDA while SCL is HIGH */ IIC_SetIic (devc, 1, 1); oss_udelay (30); IIC_SetIic (devc, 0, 1); }
Function : IIC_Stop ------------------------------------------------------------------------------- Description : Returns : void -> Parameters : unsigned int dwPortAddr -> ------------------------------------------------------------------------------- Notes : =============================================================================
/*__inline */ static void IIC_Stop (envy24_devc * devc) { /* rising edge of SDA while SCL is HIGH */ IIC_SetIic (devc, 0, 1); IIC_SetIic (devc, 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 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, bDataBit, 0); IIC_SetIic (devc, bDataBit, 1); IIC_SetIic (devc, bDataBit, 0); } /* end for i */ IIC_SetIic (devc, 1, 0); /* Get acknowledge */ IIC_SetIic (devc, 1, 1); bAck = IIC_GetSDA (devc); /* FMB this is a start condition but never mind */ IIC_SetIic (devc, 0, 0); #if 0 if (bAck) cmn_err (CE_CONT, "SendByte failed %02x\n", bByte); else cmn_err (CE_CONT, "SendByte OK %02x\n", bByte); #endif return 1; /* return (!bAck); *//* bAck = 0 --> success */ } #if 0
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, unsigned char bIicAddress, unsigned char bByte) { IIC_Start (devc); /* send IIC address and data byte */ if (!IIC_SendByte (devc, bIicAddress)) { cmn_err (CE_CONT, "IIC_SendByte 1 failed\n"); goto FAILED; } if (!IIC_SendByte (devc, bByte)) { cmn_err (CE_CONT, "IIC_SendByte 2 failed\n"); goto FAILED; } IIC_Stop (devc); return 1; FAILED: IIC_Stop (devc); return 0; } #endif static unsigned char IIC_WriteWord (envy24_devc * devc, unsigned char bIicAddress, unsigned char reg, unsigned char bByte) { IIC_Start (devc); /* send IIC address and data byte */ if (!IIC_SendByte (devc, bIicAddress)) { cmn_err (CE_CONT, "IIC_SendByte 1 failed\n"); goto FAILED; } if (!IIC_SendByte (devc, reg)) { cmn_err (CE_CONT, "IIC_SendByte 2 failed\n"); goto FAILED; } if (!IIC_SendByte (devc, bByte)) { cmn_err (CE_CONT, "IIC_SendByte 3 failed\n"); goto FAILED; } IIC_Stop (devc); return 1; FAILED: IIC_Stop (devc); return 0; } static void FrontboxControl (envy24_devc * devc, int b) { IIC_WriteWord (devc, 0x40, 0x01, b); /* Output register */ devc->gpio_tmp = b; } static int HW_AK4525_SetChipSelect (envy24_devc * devc, unsigned char bCsMask) { unsigned char b; /* check validity */ if (bCsMask > 0x07) { cmn_err (CE_CONT, "Invalid bCsMask %02x\n", bCsMask); return 0; /* invalid */ } envy24_write_cci (devc, 0x21, 0x00); envy24_write_cci (devc, 0x22, 0xff); b = envy24_read_cci (devc, 0x20); b &= ~0x07; b |= bCsMask & 0x07; envy24_write_cci (devc, 0x20, b); 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 char fData; /* format buffer */ wCmd = 0xA000 + /* chip address + R/W */ (((unsigned short) bRegIdx) << 8) + /* register address */ bRegData; HW_AK4525_SetLines (devc, 0, 0); /* 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, fData, 0); HW_AK4525_SetLines (devc, fData, 1); /* data is clocked in on rising edge of CCLK */ HW_AK4525_SetLines (devc, fData, 0); wCmd <<= 1; } /* leave data line HIGH (= default for IIC) */ HW_AK4525_SetLines (devc, 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, 1, 1); /* data is clocked in on rising edge of CCLK */ 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 = 0x07; 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; } static void set_dmx6fire_speed (envy24_devc * devc) { unsigned char 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 0
S/PDIF register access doesn't work so this feature is not enabled.
#define EWX_CLK 0x20 #define EWX_DTA 0x10 static void dmx6fire_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); envy24_write_cci (devc, 0x20, EWX_DTA | EWX_CLK | save); /* Clock pulse (ACK) */ envy24_write_cci (devc, 0x20, EWX_DTA | save); } static unsigned char dmx6fire_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 1 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; } #if 0 static void dmx6fire_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 */ envy24_write_cci (devc, 0x20, save | EWX_CLK | EWX_DTA); envy24_write_cci (devc, 0x20, save | EWX_CLK); envy24_write_cci (devc, 0x20, save); dmx6fire_iic_write_byte (devc, addr, save); dmx6fire_iic_write_byte (devc, bRegIdx, save); dmx6fire_iic_write_byte (devc, bRegData, save); /* Send stop */ envy24_write_cci (devc, 0x20, save); envy24_write_cci (devc, 0x20, save | EWX_CLK); envy24_write_cci (devc, 0x20, save | EWX_CLK | EWX_DTA); } #endif static unsigned char dmx6fire_iic_read (envy24_devc * devc, int addr, unsigned char bRegIdx) { unsigned char save, data; /* dmx6fire_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 */ envy24_write_cci (devc, 0x20, save | EWX_CLK | EWX_DTA); envy24_write_cci (devc, 0x20, save | EWX_CLK); envy24_write_cci (devc, 0x20, save); dmx6fire_iic_write_byte (devc, addr | 0x01, save); data = dmx6fire_iic_read_byte (devc, save); /* Send stop */ envy24_write_cci (devc, 0x20, save); envy24_write_cci (devc, 0x20, save | EWX_CLK); envy24_write_cci (devc, 0x20, save | EWX_CLK | EWX_DTA); PRT_STATUS (0x00); return data; } static int read_dmx6fire_spdif_reg (envy24_devc * devc, int bRegister)
Reads a byte from a specific CS8427 register.
{ unsigned char ret; ret = dmx6fire_iic_read (devc, EWX_DIG_ADDR, bRegister); return ret; } static void write_dmx6fire_spdif_reg (envy24_devc * devc, int bRegister, int bData) { /*dmx6fire_iic_write(devc, EWX_DIG_ADDR, bRegister, bData); */ IIC_WriteWord (devc, EWX_DIG_ADDR, bRegister, bData); } #else static int read_dmx6fire_spdif_reg (envy24_devc * devc, int bRegister) { return 0; } static void write_dmx6fire_spdif_reg (envy24_devc * devc, int bRegister, int bData) { } #endif static void dmx6fire_card_init (envy24_devc * devc) { envy24_write_cci (devc, 0x20, 0xff); envy24_write_cci (devc, 0x21, 0x00); envy24_write_cci (devc, 0x22, 0xff); envy24_write_cci (devc, 0x20, 0xff); HW_AK4525_SetChipSelect (devc, 0x00); #if 1 IIC_WriteWord (devc, 0x40, 0x02, 0x00); /* Polarity register */ IIC_WriteWord (devc, 0x40, 0x03, 0x00); /* Direction register */ FrontboxControl (devc, DEFAULT_FRONTBOX_SETUP); /* Set default values */ if (!HW_AK4525_Reset (devc, -1)) cmn_err (CE_CONT, "Envy24: DMX6fire reset failed\n");; #endif } static int dmx6fire_mixer_set (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: if (devc->skipdevs == 2) return devc->rec_portc[2].riaa_filter; else return devc->rec_portc[4].riaa_filter; break; } if (cmd == SNDCTL_MIX_WRITE) switch (ctrl) { case 1: if (devc->skipdevs == 2) return devc->rec_portc[2].riaa_filter = !!value; else return devc->rec_portc[4].riaa_filter = !!value; break; } return OSS_EINVAL; } static int dmx6fire_mix_init (envy24_devc * devc, int dev, int group) { int err; #if 0 int i, mask = devc->outportmask, skip, codec, ports; int range = 3; int typ = MIXT_ENUM; 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) { char tmp[32]; 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_tdif, 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_tdif, typ, tmp, range, MIXF_MAINVOL | MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; } } #endif if ((group = mixer_ext_create_group (dev, 0, "ENVY24_6FIRE")) < 0) return group; if ((err = mixer_ext_create_control (dev, group, 1, dmx6fire_mixer_set, MIXT_ONOFF, "6FIRE_RIAA", 2, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; return 0; } envy24_auxdrv_t dmx6fire_auxdrv = { dmx6fire_card_init, dmx6fire_mix_init, init_cs8427_spdif, write_cs8427_spdif, NULL, read_dmx6fire_spdif_reg, write_dmx6fire_spdif_reg, set_dmx6fire_speed, NULL, cs8427_spdif_mixer_init };