📄 codeccontrol.cpp
字号:
#include "wavemain.h"
#include "uda1380.h"
#include "bsp.h"
// IMPORTANT NOTE
// When programming the CODEC, the WS clock should be running to generate
// the base clock fo the CODEC WSPLL. Failure to run the WS clock will
// cause some CODEC operations to fail!
// Static driver data
int CODECControl::m_codecinstances = 0;
DWORD CODECControl::m_codecgoodinit = 0;
HANDLE CODECControl::m_codecdrvI2CCtl = INVALID_HANDLE_VALUE;
UINT32 CODECControl::m_codecsavedVol = 0xFFFFFFFF;
DWORD CODECControl::m_codecboostEnable = 0;
UINT32 CODECControl::m_codecSavedBoostLevel = 0;
DWORD CODECControl::m_codecmuteEnabled = 0;
DWORD CODECControl::m_codecHeadPhoneEnabledFlag = 1;
DWORD CODECControl::m_codecLineInUsedFlag = 1;
UINT32 CODECControl::m_codecGainADCPGA = 0;
UINT16 CODECControl::m_codecGainADCVGA = 0;
UINT32 CODECControl::m_codecVolDecimator = 0;
DWORD CODECControl::m_codecDigitalMixerEnabled = 1;
//********************************************************************
// Initial setup data for the I2S device (via I2C)
//********************************************************************
typedef struct
{
UNS_8 reg;
UNS_16 val;
} I2S_SETUP_T;
static I2S_SETUP_T i2sinitdata[] =
{
// L3 reset
{UDA1380_REG_L3, 0x0000},
// CODEC ADC and DAC clock from WSPLL, all clocks enabled
{UDA1380_REG_EVALCLK, 0x0F30},
// I2S bus data I/O formats, use digital mixer for output
// BCKO is slave
{UDA1380_REG_I2S, 0x0000},
// Enable all power for now
{UDA1380_REG_PWRCTRL, 0xA5D0},
// Full mixer analog input gain
{UDA1380_REG_ANAMIX, 0x0000},
// Enable headphone short circuit protection
{UDA1380_REG_HEADAMP, 0x0202},
// Full master volume
{UDA1380_REG_MSTRVOL, 0x0000},
// Enable full mixer volume on both channels
{UDA1380_REG_MIXVOL, 0x0000},
// Bass and treble boost set to flat
{UDA1380_REG_MODEBBT, 0x0000},
// Disable mute and de-emphasis
{UDA1380_REG_MSTRMUTE, 0x0000},
// Mixer off, other settings off
{UDA1380_REG_MIXSDO, 0x0000},
// ADC decimator volume to max
{UDA1380_REG_DECVOL, 0x0000},
// No PGA mute, full gain
{UDA1380_REG_PGA, 0x0F0F},
// Select line in and MIC, max MIC gain
{UDA1380_REG_ADC, 0x0F02},
// AGC
{UDA1380_REG_AGC, 0x0000},
// End of list
{0xFF, 0xFFFF}
};
//********************************************************************
// Initialization and de-init functions
//********************************************************************
// Constructor
CODECControl::CODECControl(void)
{
// Assume output channel until changed
m_codecchanDir = I2S_OUT_CH;
m_codecinstances++;
if (m_codecinstances == 1)
{
m_codecgoodinit = 1;
// Open I2C driver to communicate with I2S
m_codecdrvI2CCtl = CreateFile(I2SI2SDEVNAME, (GENERIC_READ | GENERIC_WRITE),
(FILE_SHARE_WRITE | FILE_SHARE_READ), NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_SYSTEM, 0);
if (m_codecdrvI2CCtl == INVALID_HANDLE_VALUE)
{
RETAILMSG(1,
(TEXT("CODECContext: Error initializing I2C CODEC interface\r\n")));
m_codecgoodinit = 0;
}
// Set static default levels for all instances
m_codecsavedVol = 0xFFFFFFFF;
m_codecboostEnable = 1;
m_codecSavedBoostLevel = 0;
m_codecmuteEnabled = 1;
m_codecHeadPhoneEnabledFlag = 1;
m_codecLineInUsedFlag = 1;
m_codecGainADCPGA = 0;
m_codecGainADCVGA = 0;
m_codecVolDecimator = 0;
m_codecDigitalMixerEnabled = 1;
}
}
// Destructor
CODECControl::~CODECControl(void)
{
m_codecinstances--;
if (m_codecinstances == 0)
{
// Delete I2C driver instance
if (m_codecdrvI2CCtl != INVALID_HANDLE_VALUE)
{
CloseHandle(m_codecdrvI2CCtl);
m_codecdrvI2CCtl = INVALID_HANDLE_VALUE;
}
m_codecgoodinit = 0;
}
}
// Returns initialized 'good' status after construction
DWORD CODECControl::codecIsInitGood(void)
{
return m_codecgoodinit;
}
// Setup CODEC default registers, this should be done once the I2S
// clock is running
DWORD CODECControl::codecSetup(void)
{
int idx;
DWORD goodSetup = 1;
// Initialize CODEC via I2C interface
idx = 0;
while (i2sinitdata[idx].reg != 0xFF)
{
if (codecWriteReg(i2sinitdata[idx].val, i2sinitdata[idx].reg) == 0)
{
goodSetup = 0;
}
idx++;
}
return goodSetup;
}
// Set direction for this instances
void CODECControl::codecSetDirection(I2S_CH_T ch)
{
m_codecchanDir = ch;
}
//********************************************************************
// CODEC raw register read/write functions
//********************************************************************
// Write a value to a CODEC register
DWORD CODECControl::codecWriteReg(UINT16 val,
UINT8 reg)
{
DWORD good;
I2C_OUT_XFER_T xferout;
I2C_IN_XFER_T xferin;
// Setup transfer
xferout.flags_buff [0] = (I2S_DEV_ADDR | I2C_WRITE | I2C_START_FLAG);
xferout.flags_buff [1] = reg;
// Start read operation with repeated start
xferout.flags_buff [2] = ((val >> 8) & 0xFF);
xferout.flags_buff [3] = (((val >> 0) & 0xFF) | I2C_STOP_FLAG);
// Send 4 bytes, read 0 bytes
xferout.tosend = 4;
xferout.torecv = 0;
good = codecI2CTransaction(&xferout, &xferin);
if (good != 0)
{
if (xferin.ist != I2CST_COMPLETE)
{
good = 0;
}
}
return good;
}
// Read a value from a register
DWORD CODECControl::codecReadReg(UINT16 *val,
UINT8 reg)
{
DWORD good;
I2C_OUT_XFER_T xferout;
I2C_IN_XFER_T xferin;
// Setup transfer
xferout.flags_buff [0] = (I2S_DEV_ADDR | I2C_WRITE | I2C_START_FLAG);
xferout.flags_buff [1] = reg;
// Start read operation with repeated start
xferout.flags_buff [2] = (I2S_DEV_ADDR | I2C_READ | I2C_START_FLAG);
// Read 2 bytes, send 3 bytes
xferout.tosend = 3;
xferout.torecv = 2;
good = codecI2CTransaction(&xferout, &xferin);
if (good != 0)
{
if (xferin.ist != I2CST_COMPLETE)
{
good = 0;
}
else
{
*val = ((UINT16) xferin.buff[0] << 8) |
((UINT16) xferin.buff[1]);
}
}
return good;
}
//********************************************************************
// CODEC clock and power control functions
//********************************************************************
// Adjust the CODEC PLL to the pass sample rate
DWORD CODECControl::codecSetPLLRate(DWORD wsrate)
{
UINT16 rmask, regval;
DWORD setGood = 0;
// Based on the WS rate, the PLL also needs to be adjusted
if (wsrate < 12500)
{
rmask = EVALCLK_WSPLL_SEL6_12K;
}
else if (wsrate < 25000)
{
rmask = EVALCLK_WSPLL_SEL12_25K;
}
else if (wsrate < 50000)
{
rmask = EVALCLK_WSPLL_SEL25_50K;
}
else
{
rmask = EVALCLK_WSPLL_SEL50_100K;
}
// Read current value first
if (codecReadReg(®val, UDA1380_REG_EVALCLK) != 0)
{
regval = regval & ~0x3;
regval = regval | rmask;
if (codecWriteReg(regval, UDA1380_REG_EVALCLK) != 0)
{
setGood = 1;
}
}
return setGood;
}
// Power down/up the output or input side of the CODEC. This will enable
// power in the CODEC for output or input functionality. This should only
// be used when bringing up or down the CODEC
DWORD CODECControl::codecPowerUp(DWORD powerup)
{
// TBD fix me
// Not support yet, CODEC is always powered on
return 1;
}
// Returns power up status for input or output channels
DWORD CODECControl::codecIsPoweredUp(void)
{
// TBD fix me
// Not support yet, CODEC is always powered on
return 1;
}
// Enable clocking for the output or input side of the CODEC. This can
// be used to gate clocking when no audio is output or input to save
// power.
DWORD CODECControl::codecClockUp(DWORD clockup)
{
// TBD fix me
// Not support yet, CODEC is always clocked
return 1;
}
// Returns clocking status for input or output channels
DWORD CODECControl::codecIsClockUp(void)
{
// TBD fix me
// Not support yet, CODEC is always clocked
return 1;
}
//********************************************************************
// Sound control functions
//********************************************************************
// Return or set volume, accepts and returns a value beween 0 and 0xFFFF,
// 0 will mute the volume, High 16 bits=right, low 16 bits=left
DWORD CODECControl::codecSetMasterVolume(UINT32 newvol)
{
UINT16 trunVolL, trunVolR, vol;
DWORD status = 0;
// Skip if no changes are needed
if (m_codecsavedVol == newvol)
{
return 1;
}
trunVolL = 0xFFFF - (UINT16) ((newvol >> 0) & 0xFFFF);
trunVolR = 0xFFFF - (UINT16) ((newvol >> 16) & 0xFFFF);
// The I2S CODEC uses 8 bits for volume level for each channel (left
// and right), so use just the upper 8 bits for the left and right
// channels
vol = ((trunVolL >> 8) & 0x00FF) | ((trunVolR >> 0) & 0xFF00);
// Write to the volume register
if (codecWriteReg(vol, UDA1380_REG_MSTRVOL) != 0)
{
status = 1;
m_codecsavedVol = newvol;
}
return status;
}
// Return current master volume, High 16 bits=right, low 16 bits=left
UINT32 CODECControl::codecGetMasterVolume(void)
{
return m_codecsavedVol;
}
// Bass and treble boost enable/disable
DWORD CODECControl::codecEnableBoost(DWORD enable)
{
UINT16 regval;
DWORD good = 0;
// Skip if no changes are needed
if (m_codecboostEnable == enable)
{
return 1;
}
// Read current value first
if (codecReadReg(®val, UDA1380_REG_MODEBBT) != 0)
{
if (enable != 0)
{
regval |= MODEBBT_BOOST_MASK;
}
else
{
regval &= ~MODEBBT_BOOST_MASK;
}
if (codecWriteReg(regval, UDA1380_REG_MODEBBT) != 0)
{
good = 1;
m_codecboostEnable = enable;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -