📄 xsac97ctrl.c
字号:
rangeCheckResult = XsAc97CtrlRangeCheckStatusId (statusId);
if (rangeCheckResult)
{
LOGERROR ( XsAc97CtrlContext.loggedError,
ERR_L_XSAC97CTRL,
ERR_S_XSAC97CTRL_GET_STATUS,
rangeCheckResult, 0, 0, 0)
}
else
{
statusTableEntryP = XsAc97CtrlStatusTable + statusId;
// Get the indication, normalize it, then isolate it.
statusIndication = XsAc97CtrlRegsP->GSR;
statusIndication >>= statusTableEntryP->reportBitShiftGsr;
statusIndication &= statusTableEntryP->reportBitMaskGsr;
// Clear the indication if it exists and can be cleared.
// Don't use the clearing subroutine, there's so much extra overhead.
// Don't clear without finding it, to avoid race condition.
// NULL clear register means it can't be cleared.
if (statusIndication && statusTableEntryP->clearRegisterP)
{
// Write a shifted "1" to clear the status. Don't "OR" it in.
*statusTableEntryP->clearRegisterP =
1u << statusTableEntryP->clearBitShift;
}
} // else (rangeCheckResult)
return (statusIndication);
} // XsAc97CtrlGetStatus()
/*
*******************************************************************************
*
* FUNCTION: XsAc97CtrlWriteCodecReg
*
* DESCRIPTION: Write a value to a specific mixer register in a specific
* AC'97 codec or modem, using the AC Link.
*
* INPUT PARAMETERS:
*
* RETURNS:
* Success: 0 (ERR_NONE)
* Failure: ERR_T_NOT_AVAIL: AC Link was not available within the
* timeout interval.
* ERR_T_ILLPARAM: One of the ID parameters was illegal.
* Which one is recorded in the first parameter of
* the logged error. Software error.
*
* GLOBAL EFFECTS: Disables interrupts briefly, possibly sub-microsecond.
* Theoretical maximum is about 130 - 200 uSec.
* On termination, AC Link remains locked until the codec I/0
* cycle initiated by the local write completes. That
* should take from 21 to 42 uSec. Next codec I/O will not
* be able to start during that time.
*
* ASSUMPTIONS:
*
* CALLS:
*
* CALLED BY:
*
* PROTOTYPE: UINT32 XsAc97CtrlWriteCodecReg (XsAc97CtrlCodecModemIdT,
* AC97MixerRegisterIdT,
* UINT32);
*
*******************************************************************************
*
* DESIGN:
*
* 1. Disable interrupts, saving pre-existing state.
* 2. Read the CAR until it is verified that no Codec I/O accesses are
* currently in progress. This also claims the next codec I/O
* transaction for this operation.
* 3. Clear any status and error indications that can result from a Codec
* write operation (CDONE).
* 4. Write the specified data to the processor memory location that is mapped
* to the target Codec's target register.
* 5. Wait with a timeout until the CDONE indicator is set. This means that
* the command has been sent.
* 6. Restore previous interrupt state.
*
* Note: It should theoretically be possible to write a driver so that this
* operation does not wait for the transmission to complete. Any remaining
* wait would be done in the beginning of the next access. That
* optimization has not yet been successfully implemented.
*
*******************************************************************************
*/
ErrorT XsAc97CtrlWriteCodecReg (XsAc97CtrlCodecModemIdT ac97DeviceId,
AC97MixerRegisterIdT targetRegister,
UINT32 newValue)
{
ErrorT retVal;
INT badParamNum = 0 ; // Which param had error. Assume no errors.
BOOL gotLink;
UINT32 irqIntState;
INT timeRemaining;
VUINT32* mixerRegsAccessP;
VUINT finishedStatus;
retVal = Ac97CheckRegId (targetRegister);
if (retVal)
{
badParamNum = 2;
}
else // register ID OK, set register access pointer to codec access base
{
switch (ac97DeviceId)
{
case XS_AC97CTRL_CM_ID_PRI_CODEC:
mixerRegsAccessP = &XsAc97CtrlRegsP->
XsAC97CtrlMixerRegsPrimaryAud[0];
break;
default:
retVal = ERR_T_ILLPARAM;
badParamNum = 1;
break;
}
} // else (register ID OK)
if (!retVal)
{
// Point to specified register within area mapped to target codec regs
mixerRegsAccessP += (targetRegister / XS_AC97CTRL_MIXER_REGS_PER_WORD);
irqIntState = XsIcDisableInterruptsIrq();
// Lock the ACLINK
timeRemaining = XS_AC97CTRL_CAIP_TIMEOUT_1USECS;
do
{
gotLink = XsAc97CtrlLockAcLink();
if (!gotLink) // 1 usec is a long time. Skip delay if possible.
{
DM_WaitUs(1);
}
}
// Wait while time remaining and ACLINK not available
while ( timeRemaining-- && !gotLink );
if (!gotLink) // Didn't get the ACLINK
{
retVal = ERR_T_NOT_AVAIL;
}
else // We got the link. Perform the write operation and don't wait.
{
// First, clear old write status indications.
// Ignore return code; if the ID is invalid, it won't compile.
(void) XsAc97CtrlClearStatus (XS_AC97CTRL_STAT_CDONE);
*mixerRegsAccessP = newValue; // Now the write!
// Wait until write cycle is complete. There should be a way
// to do this speculatively at the beginning of the procedure.
// Need to discover it. Too inefficient to always wait.
// Use more forgiving read timeout
timeRemaining = XS_AC97CTRL_READ_TIMEOUT_1USECS;
do
{
DM_WaitUs(1);
finishedStatus =
XsAc97CtrlGetStatus (XS_AC97CTRL_STAT_CDONE);
}
// Wait while time remaining and command I/O still incomplete
// CDONE bit is high when done.
while ( (timeRemaining--) && !finishedStatus);
} // Got AC link
XsIcRestoreInterruptsIrq (irqIntState);
} // if (!retVal): No problem with params. Sent command if link available.
if (retVal)
{
LOGERROR (XsAc97CtrlContext.loggedError, ERR_L_XSAC97CTRL,
ERR_S_XSAC97CTRL_CODEC_WRITE, retVal, badParamNum, 0, targetRegister)
}
return(retVal);
} // XsAc97CtrlCodecWrite()
/*
*******************************************************************************
*
* FUNCTION: XsAc97CtrlReadCodecReg
*
* DESCRIPTION: Read the value of a specific mixer register in a specified
* AC'97 codec or modem, using the AC Link.
*
* INPUT PARAMETERS:
* XsAc97CtrlCodecModemIdT ac97DeviceId: ID of modem or codec to read.
* AC97MixerRegisterIdT targetRegister: ID of register on codec.
* UINT32* mixerRegValueP: Address of variable in which to
* put the value from the codec reg.
*
* OUTPUT: UINT32* mixerRegValueP: Receives the value reported for the
* specified mixer register in the specified
* codec. Valid only if return code is zero.
*
* RETURNS:
* Success: 0 (ERR_NONE); value in *mixerRegValueP is valid.
* Failure: ERR_T_NOT_AVAIL: AC Link was not available within the
* initial timeout interval.
* ERR_T_ILLPARAM: One of the ID parameters was illegal.
* Which one is recorded in the first parameter of
* the logged error. Software error.
* ERR_T_TIMEOUT: A timeout occurred after initial
* acquisition of the AC Link. The point in the
* routine where the timeout occurred is recorded
* in the second parameter of the logged error.
*
* GLOBAL EFFECTS: Disables interrupts for at least 42 uSec. Theoretical
* maximum is about 250 uSec.
* On termination, AC Link remains locked until the codec I/0
* cycle initiated by the second local read completes. That
* could take from 42 to 84+ uSec. Next codec I/O will not
* be able to start during that time.
* Errors recorded in context structure and system error log.
*
* ASSUMPTIONS:
*
* CALLS:
*
* CALLED BY:
*
* PROTOTYPE: UINT32 XsAc97CtrlReadCodecReg (XsAc97CtrlCodecModemIdT,
* AC97MixerRegisterIdT,
* UINT32*);
*
*******************************************************************************
*
* DESIGN:
*
* 1. Disable interrupts, saving pre-existing state.
* 2. Read the CAR until it is verified that no codec I/O accesses are
* currently in progress. This also claims the next codec I/O transaction
* for this operation.
* 3. Clear any status and error indications that can result from a codec read
* operation (SDONE, Read Completion Status).
* 4. Perform the first (dummy) read from the processor memory location that is
* mapped to the target Codec's target register. Discard the resulting data
* 5. Continuously check the AC '97 Controller's Global Status Register until
* the SDONE bit is set. Use software timeout logic.
* 6. If the Read Completion Status bit is set or the software timeout has
* occurred, there is a timeout.
* a) Declare an error status
* b) Skip to step 8 in this procedure.
* 7. If there was no timeout, the read operation was successful.
* a) Read the CAR until it is verified that no codec I/O accesses are
* currently in progress. This also claims the next codec I/O
* transaction for this operation. It leaves an indication for the
* next SW read or write I/O operation. This will prevent ambiguity.
* (We shouldn't actually have to loop here because the SDONE bit
* indicator ought to be set at the same time as the CAR indicator
* clears. But we loop anyway, just in case.)
* b) Perform the second read to obtain the register contents reported by
* the codec.
* c) Report the data obtained from the codec.
* d) Wait with a timeout until the SDONE indicator is set.
* This means that the I/O cycle triggered by the second read has
* completed.
* 8. Restore previous interrupt state.
* 9. Report success or error status.
*
* Note: It should theoretically be possible to write a driver so that that
* this operation does not wait for the second read operation to complete.
* Any remaining wait would be done in the beginning of the next access.
* That optimization has not yet been successfully implemented.
*
* Note also that there is a possible optimization in this processor for reads
* of a modem's GPIO bits that are reported to the controller in time slot 12.
* It would be to skip the timeout logic and the second read cycle because the
* local values are constantly updated from the values reported in TS 12.
*
* Once the link is acquired, perform the double read under interrupt
* protection. This is necessary to make it an atomic operation. Even
* if link acquisition continued for @@@
*
*******************************************************************************
*/
ErrorT XsAc97CtrlReadCodecReg (XsAc97CtrlCodecModemIdT ac97DeviceId,
AC97MixerRegisterIdT targetRegister,
UINT32* mixerRegValueP)
{
ErrorT retVal ;
INT badParamNum = 0; // Which param had error. Assume no errors.
INT timeoutLoc = 0; // What point had a timeout. Assume no errors.
BOOL gotLink;
VUINT32 readFinishedStatus;
UINT32 irqIntState;
UINT32 rcsErrTmp;
INT minorIterations;
INT retries = 0;
XsAc97CtrlStatsT* codeStatsP;
VUINT32* mixerRegsAccessP;
retVal = Ac97CheckRegId (targetRegister);
if (retVal)
{
badParamNum = 2;
}
else // register ID OK, set register access pointer to codec access base
{
switch (ac97DeviceId)
{
case XS_AC97CTRL_CM_ID_PRI_CODEC:
mixerRegsAccessP = &XsAc97CtrlRegsP->
XsAC97CtrlMixerRegsPrimaryAud[0];
codeStatsP = &XsAc97CtrlContext.statistics[ac97DeviceId];
break;
default:
retVal = ERR_T_ILLPARAM;
badParamNum = 1;
break;
}
// Point to specified register within area mapped to target codec regs
mixerRegsAccessP += (targetRegister / XS_AC97CTRL_MIXER_REGS_PER_WORD);
} // else (register ID OK)
if (!retVal)
{
// Some statistics. Only valid invocations are interesting.
irqIntState = XsIcDisableInterruptsIrq();
codeStatsP->readCodecNumInvocations++;
XsIcRestoreInterruptsIrq (irqIntState);
do
{
retVal = ERR_NONE; // Clear for possible retry
// Protect entire operation for simplicity. Very small extra
// execution time penalty.
irqIntState = XsIcDisableInterruptsIrq();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -