I2STransportInterface.cpp [plain text]
#include "I2STransportInterface.h"
#include <IOKit/IOMessage.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#include <IOKit/IOTimerEventSource.h>
#include "AudioHardwareUtilities.h"
#include "PlatformInterface.h"
#define super TransportInterface
OSDefineMetaClassAndStructors ( I2STransportInterface, TransportInterface );
#pragma mark #--------------------
#pragma mark # PUBLIC METHODS
#pragma mark #--------------------
bool I2STransportInterface::init ( PlatformInterface * inPlatformInterface ) {
bool success;
IOReturn result = kIOReturnError;
debugIOLog (3, "+ I2STransportInterface[%p]::init ( %d )", this, (unsigned int)inPlatformInterface );
success = super::init ( inPlatformInterface );
FailIf ( !success, Exit );
success = false;
FailIf ( NULL == mPlatformObject, Exit );
super::transportSetTransportInterfaceType ( kTransportInterfaceType_I2S );
requestClockSources ();
mPlatformObject->setClockMux ( kGPIO_MuxSelectDefault );
super::transportMakeClockSelect ( kTRANSPORT_MASTER_CLOCK );
mMclkMultiplier = 256; mSclkMultiplier = 64;
mDataWordSize = ( kDataIn16 << kDataInSizeShift ) | ( kDataOut16 << kDataOutSizeShift );
mDataWordSize |=( kI2sStereoChannels << kNumChannelsOutShift ) | ( kI2sStereoChannels << kNumChannelsInShift );
mPlatformObject->setI2SIOMIntControl ( 1 << kClocksStoppedPendingShift ); result = mPlatformObject->setI2SClockEnable ( false );
FailIf ( kIOReturnSuccess != result, Exit );
waitForClocksStopped ();
mPlatformObject->setI2SSWReset ( true );
IOSleep ( 10 );
mPlatformObject->setI2SSWReset ( false );
result = mPlatformObject->setDataWordSizes ( mDataWordSize );
FailIf ( kIOReturnSuccess != result, Exit );
result = mPlatformObject->setI2SCellEnable ( true );
FailIf ( kIOReturnSuccess != result, Exit );
result = mPlatformObject->setI2SEnable ( true );
FailIf ( kIOReturnSuccess != result, Exit );
result = transportSetSampleRate ( 44100 );
FailIf ( kIOReturnSuccess != result, Exit );
success = true;
Exit:
debugIOLog (3, "- I2STransportInterface[%p (%ld)]::init ( %d ) = %d", this, mInstanceIndex, (unsigned int)inPlatformInterface, (unsigned int)success );
return success;
}
void I2STransportInterface::free () {
releaseClockSources ();
super::free();
return;
}
IOReturn I2STransportInterface::transportSetSampleRate ( UInt32 sampleRate ) {
IOReturn result = kIOReturnError;
debugIOLog (3, "+ I2STransportInterface[%ld]::transportSetSampleRate ( %d )", mInstanceIndex, (unsigned int)sampleRate );
result = calculateSerialFormatRegisterValue ( sampleRate );
FailIf ( kIOReturnSuccess != result, Exit );
FailIf ( NULL == mPlatformObject, Exit );
mPlatformObject->setI2SIOMIntControl ( 1 << kClocksStoppedPendingShift ); result = mPlatformObject->setI2SClockEnable ( false );
FailIf ( kIOReturnSuccess != result, Exit );
waitForClocksStopped ();
result = mPlatformObject->setSerialFormatRegister ( mSerialFormat );
FailIf ( kIOReturnSuccess != result, Exit );
debugIOLog (3, "mPlatformObject->setSerialFormatRegister ( %X ) returns %X", (unsigned int)mSerialFormat, (unsigned int)result );
result = mPlatformObject->setDataWordSizes ( mDataWordSize );
FailIf ( kIOReturnSuccess != result, Exit );
debugIOLog (3, "mPlatformObject->setDataWordSizes ( %X ) returns %X", (unsigned int)mDataWordSize, (unsigned int)result );
result = mPlatformObject->setI2SEnable ( true );
FailIf ( kIOReturnSuccess != result, Exit );
result = mPlatformObject->setI2SClockEnable ( true );
FailIf ( kIOReturnSuccess != result, Exit );
result = super::transportSetSampleRate ( sampleRate );
Exit:
debugIOLog (3, "- transportSetSampleRate ( %d )", (unsigned int)sampleRate );
return result;
}
IOReturn I2STransportInterface::transportSetSampleWidth ( UInt32 sampleDepth, UInt32 dmaWidth ) {
IOReturn result = kIOReturnError;
FailIf ( NULL == mPlatformObject, Exit );
if ( 16 == sampleDepth && 16 == dmaWidth ) {
mDataWordSize = ( kDataIn16 << kDataInSizeShift ) | ( kDataOut16 << kDataOutSizeShift );
} else if ( 24 == sampleDepth && 32 == dmaWidth ) {
mDataWordSize = ( kDataIn24 << kDataInSizeShift ) | ( kDataOut24 << kDataOutSizeShift );
} else {
FailIf ( true, Exit );
}
mDataWordSize |= ( ( kI2sStereoChannels << kNumChannelsOutShift ) | ( kI2sStereoChannels << kNumChannelsInShift ) );
result = mPlatformObject->setI2SClockEnable ( false );
FailIf ( kIOReturnSuccess != result, Exit );
waitForClocksStopped ();
result = mPlatformObject->setDataWordSizes ( mDataWordSize );
FailIf ( kIOReturnSuccess != result, Exit );
result = mPlatformObject->setI2SEnable ( true );
FailIf ( kIOReturnSuccess != result, Exit );
result = mPlatformObject->setI2SClockEnable ( true );
FailIf ( kIOReturnSuccess != result, Exit );
result = super::transportSetSampleWidth ( sampleDepth, dmaWidth );
Exit:
return result;
}
IOReturn I2STransportInterface::performTransportSleep ( void ) {
IOReturn result = kIOReturnError;
debugIOLog (3, "+ I2STransportInterface::performTransportSleep ()" );
FailIf ( NULL == mPlatformObject, Exit );
result = mPlatformObject->setI2SClockEnable ( false );
FailIf ( kIOReturnSuccess != result, Exit );
waitForClocksStopped ();
result = mPlatformObject->setI2SCellEnable ( false );
FailIf ( kIOReturnSuccess != result, Exit );
result = mPlatformObject->releaseI2SClockSource ( mClockSelector );
releaseClockSources ();
Exit:
debugIOLog (3, "- I2STransportInterface::performTransportSleep () = %d", (unsigned int)result );
return result;
}
IOReturn I2STransportInterface::performTransportWake ( void ) {
IOReturn result = kIOReturnError;
debugIOLog (3, "+ I2STransportInterface::performTransportWake ()" );
requestClockSources ();
result = mPlatformObject->setDataWordSizes ( mDataWordSize );
FailIf (kIOReturnSuccess != result, Exit);
result = mPlatformObject->setI2SCellEnable ( true );
FailIf ( kIOReturnSuccess != result, Exit );
result = transportSetSampleRate ( mTransportState.transportSampleRate );
FailIf (kIOReturnSuccess != result, Exit);
debugIOLog (3, "- I2STransportInterface::performTransportWake () = %d", (unsigned int)result );
Exit:
return result;
}
IOReturn I2STransportInterface::transportBreakClockSelect ( UInt32 clockSource ) {
IOReturn result = kIOReturnError;
debugIOLog (7, "+ I2STransportInterface::transportBreakClockSelect ( %d )", (unsigned int)clockSource );
FailIf ( NULL == mPlatformObject, Exit );
super::transportBreakClockSelect ( clockSource );
result = calculateSerialFormatRegisterValue ( mTransportState.transportSampleRate );
FailIf ( kIOReturnSuccess != result, Exit );
IOSleep ( 10 );
result = mPlatformObject->setSerialFormatRegister ( mSerialFormat );
FailIf ( kIOReturnSuccess != result, Exit );
debugIOLog (7, "mPlatformObject->setSerialFormatRegiste ( %X ) returns %X", (unsigned int)mSerialFormat, (unsigned int)result );
result = mPlatformObject->setDataWordSizes ( mDataWordSize );
FailIf ( kIOReturnSuccess != result, Exit );
debugIOLog (7, "mPlatformObject->setDataWordSizes ( %X ) returns %X", (unsigned int)mDataWordSize, (unsigned int)result );
IOSleep ( 10 );
mPlatformObject->setI2SIOMIntControl ( 1 << kClocksStoppedPendingShift ); result = mPlatformObject->setI2SClockEnable ( false );
FailIf ( kIOReturnSuccess != result, Exit );
waitForClocksStopped ();
IOSleep ( 10 );
switch ( clockSource ) {
case kTRANSPORT_MASTER_CLOCK:
debugIOLog (7, "... kTRANSPORT_MASTER_CLOCK requires no action" );
break;
case kTRANSPORT_SLAVE_CLOCK:
debugIOLog (7, "... setting clock mux to ALTERNATE source" );
if ( kTransportInterfaceType_I2S == mTransportState.transportInterfaceType ) {
result = mPlatformObject->setClockMux ( kGPIO_MuxSelectAlternate );
FailIf ( kIOReturnSuccess != result, Exit );
} else {
result = kIOReturnSuccess;
}
break;
default:
FailIf ( true, Exit );
break;
}
Exit:
debugIOLog (7, "- I2STransportInterface::transportBreakClockSelect ( %d ) = %X", (unsigned int)clockSource, (unsigned int)result );
return result;
}
IOReturn I2STransportInterface::transportMakeClockSelect ( UInt32 clockSource ) {
IOReturn result = kIOReturnError;
debugIOLog (7, "+ I2STransportInterface::transportMakeClockSelect ( %d )", (unsigned int)clockSource );
FailIf ( NULL == mPlatformObject, Exit );
switch ( clockSource ) {
case kTRANSPORT_MASTER_CLOCK:
debugIOLog (7, "... setting clock mux to DEFAULT source" );
if ( kTransportInterfaceType_I2S == mTransportState.transportInterfaceType ) {
result = mPlatformObject->setClockMux ( kGPIO_MuxSelectDefault );
FailIf ( kIOReturnSuccess != result, Exit );
} else {
result = kIOReturnSuccess;
}
debugIOLog (7, "... setting kBClkMasterShift to MASTER" );
break;
case kTRANSPORT_SLAVE_CLOCK:
debugIOLog (7, "... kTRANSPORT_SLAVE_CLOCK requires no action" );
result = kIOReturnSuccess;
break;
default:
FailIf ( true, Exit );
break;
}
Exit:
result = mPlatformObject->setI2SClockEnable ( true );
debugIOLog (7, "- I2STransportInterface::transportMakeClockSelect ( %d ) = %X", (unsigned int)clockSource, (unsigned int)result );
return result;
}
UInt32 I2STransportInterface::transportGetSampleRate ( void ) {
UInt32 curSerialFormat;
UInt32 result = 0;
if ( kTRANSPORT_SLAVE_CLOCK == mTransportState.clockSource ) {
curSerialFormat = mPlatformObject->getSerialFormatRegister ();
curSerialFormat &= 0x00000FFF;
if ( ( kSampleRate_11Khz_LowerLimt <= curSerialFormat ) && ( curSerialFormat <= kSampleRate_11Khz_UpperLimt ) ) {
result = 11025;
} else if ( ( kSampleRate_16Khz_LowerLimt <= curSerialFormat ) && ( curSerialFormat <= kSampleRate_16Khz_UpperLimt ) ) {
result = 16000;
} else if ( ( kSampleRate_22Khz_LowerLimt <= curSerialFormat ) && ( curSerialFormat <= kSampleRate_22Khz_UpperLimt ) ) {
result = 22050;
} else if ( ( kSampleRate_24Khz_LowerLimt <= curSerialFormat ) && ( curSerialFormat <= kSampleRate_24Khz_UpperLimt ) ) {
result = 24000;
} else if ( ( kSampleRate_32Khz_LowerLimt <= curSerialFormat ) && ( curSerialFormat <= kSampleRate_32Khz_UpperLimt ) ) {
result = 32000;
} else if ( ( kSampleRate_44Khz_LowerLimt <= curSerialFormat ) && ( curSerialFormat <= kSampleRate_44Khz_UpperLimt ) ) {
result = 44100;
} else if ( ( kSampleRate_48Khz_LowerLimt <= curSerialFormat ) && ( curSerialFormat <= kSampleRate_48Khz_UpperLimt ) ) {
result = 48000;
} else if ( ( kSampleRate_64Khz_LowerLimt <= curSerialFormat ) && ( curSerialFormat <= kSampleRate_64Khz_UpperLimt ) ) {
result = 64000;
} else if ( ( kSampleRate_88Khz_LowerLimt <= curSerialFormat ) && ( curSerialFormat <= kSampleRate_88Khz_UpperLimt ) ) {
result = 88200;
} else if ( ( kSampleRate_96Khz_LowerLimt <= curSerialFormat ) && ( curSerialFormat <= kSampleRate_96Khz_UpperLimt ) ) {
result = 96000;
} else if ( ( kSampleRate_176Khz_LowerLimt <= curSerialFormat ) && ( curSerialFormat <= kSampleRate_176Khz_UpperLimt ) ) {
result = 176400;
} else if ( ( kSampleRate_192Khz_LowerLimt <= curSerialFormat ) && ( curSerialFormat <= kSampleRate_192Khz_UpperLimt ) ) {
result = 19200;
} else {
curSerialFormat *= 5425;
curSerialFormat = 100000000 / curSerialFormat;
result = curSerialFormat * 1000;
}
} else {
result = super::transportGetSampleRate ();
}
return result;
}
IOReturn I2STransportInterface::transportSetPeakLevel ( UInt32 channelTarget, UInt32 levelMeterValue ) {
IOReturn result;
debugIOLog (3, "+ I2STransportInterface::transportSetPeakLevel ( '%4s', %lX )", (char*)&channelTarget, levelMeterValue );
switch ( channelTarget ) {
case kStreamFrontLeft:
case kStreamFrontRight: result = mPlatformObject->setPeakLevel ( channelTarget, levelMeterValue ); break;
default: result = kIOReturnBadArgument; break;
}
debugIOLog (3, "- I2STransportInterface::transportSetPeakLevel ( '%4s', %lX ) returns %X", (char*)&channelTarget, levelMeterValue, result );
return result;
}
UInt32 I2STransportInterface::transportGetPeakLevel ( UInt32 channelTarget ) {
UInt32 result = 0;
debugIOLog (3, "+ I2STransportInterface::transportGetPeakLevel ( '%4s' )", (char*)&channelTarget );
switch ( channelTarget ) {
case kStreamFrontLeft:
case kStreamFrontRight:
result = mPlatformObject->getPeakLevel ( channelTarget );
if ( ( 1 << kNewPeakInShift ) & result ) {
mPlatformObject->setPeakLevel ( channelTarget, ( 1 << kNewPeakInShift ) | ( 0 << kHoldPeakInShift ) );
mPlatformObject->setPeakLevel ( channelTarget, ( 0 << kNewPeakInShift ) | ( 0 << kHoldPeakInShift ) );
mPlatformObject->setPeakLevel ( channelTarget, ( 0 << kNewPeakInShift ) | ( 1 << kHoldPeakInShift ) );
}
result &= kPeakValueMask;
break;
}
debugIOLog (3, "- I2STransportInterface::transportGetPeakLevel ( '%4s' ) returns %lX", (char*)&channelTarget, result );
return result;
}
#pragma mark #--------------------
#pragma mark # PRIVATE METHODS
#pragma mark #--------------------
IOReturn I2STransportInterface::requestClockSources () {
IOReturn result = kIOReturnError;
debugIOLog (3, "+ I2STransportInterface::requestClockSources ()");
if (FALSE == mHave45MHzClock) {
result = mPlatformObject->requestI2SClockSource (kI2S_45MHz);
if (kIOReturnSuccess != result) {
debugIOLog (3, "I2STransportInterface failed to acquire 45 MHz clock.");
} else {
mHave45MHzClock = TRUE;
}
}
if (FALSE == mHave49MHzClock) {
result = mPlatformObject->requestI2SClockSource (kI2S_49MHz);
if (kIOReturnSuccess != result) {
debugIOLog (3, "I2STransportInterface failed to acquire 49 MHz clock.");
} else {
mHave49MHzClock = TRUE;
}
}
if (FALSE == mHave18MHzClock) {
result = mPlatformObject->requestI2SClockSource (kI2S_18MHz);
if (kIOReturnSuccess != result) {
debugIOLog (3, "I2STransportInterface failed to acquire 18 MHz clock.");
} else {
mHave18MHzClock = TRUE;
}
}
if (mHave18MHzClock && mHave45MHzClock && mHave49MHzClock) {
result = kIOReturnSuccess;
}
return result;
}
IOReturn I2STransportInterface::releaseClockSources () {
IOReturn result = kIOReturnError;
if (TRUE == mHave45MHzClock) {
result = mPlatformObject->releaseI2SClockSource (kI2S_45MHz);
if (kIOReturnSuccess != result) {
debugIOLog (3, "Failed to release 45 MHz clock.");
} else {
mHave45MHzClock = FALSE;
}
}
if (TRUE == mHave49MHzClock) {
result = mPlatformObject->releaseI2SClockSource (kI2S_49MHz);
if (kIOReturnSuccess != result) {
debugIOLog (3, "Failed to release 49 MHz clock.");
} else {
mHave49MHzClock = FALSE;
}
}
if (TRUE == mHave18MHzClock) {
result = mPlatformObject->releaseI2SClockSource (kI2S_18MHz);
if (kIOReturnSuccess != result) {
debugIOLog (3, "Failed to release 18 MHz clock.");
} else {
mHave18MHzClock = FALSE;
}
}
result = kIOReturnSuccess;
return result;
}
IOReturn I2STransportInterface::calculateSerialFormatRegisterValue ( UInt32 sampleRate ) {
IOReturn result = kIOReturnError;
FailIf ( NULL == mPlatformObject, Exit );
mMClkFrequency = sampleRate * mMclkMultiplier;
mSClkFrequency = sampleRate * mSclkMultiplier;
debugIOLog(6, "I2STransportInterface::calculateSerialFormatRegisterValue: sampleRate = %ld, mMClkFrequency = %ld, mSClkFrequency = %ld", sampleRate, mMClkFrequency, mSClkFrequency);
if ( 0 == ( kClock49MHz % mMClkFrequency ) ) {
mClockSourceFrequency = kClock49MHz;
mClockSelector = kI2S_49MHz;
mSerialFormat = ( kClockSource49MHz << kClockSourceShift );
} else if ( 0 == ( kClock45MHz % mMClkFrequency ) ) {
mClockSourceFrequency = kClock45MHz;
mClockSelector = kI2S_45MHz;
mSerialFormat = ( kClockSource45MHz << kClockSourceShift );
} else if ( 0 == ( kClock18MHz % mMClkFrequency ) ) {
mClockSourceFrequency = kClock18MHz;
mClockSelector = kI2S_18MHz;
mSerialFormat = ( kClockSource18MHz << kClockSourceShift );
} else {
debugIOLog(1, "I2STransportInterface UNABLE TO DERIVE SAMPLE RATE (%ld)", sampleRate);
FailIf ( true, Exit );
}
debugIOLog(6, "I2STransportInterface::calculateSerialFormatRegisterValue: mClockSourceFrequency = %ld", mClockSourceFrequency);
if ( kTRANSPORT_MASTER_CLOCK == mTransportState.clockSource ) {
mSerialFormat |= ( kSClkMaster << kBClkMasterShift );
} else if ( kTRANSPORT_SLAVE_CLOCK == mTransportState.clockSource ) {
mSerialFormat |= ( kSClkSlave << kBClkMasterShift );
} else {
FailIf ( true, Exit );
}
mMClkDivisor = mClockSourceFrequency / mMClkFrequency;
debugIOLog(6, "I2STransportInterface::calculateSerialFormatRegisterValue: mMClkDivisor = %ld", mMClkDivisor);
switch ( mMClkDivisor ) {
case 1: mMClkDivider = 14; break;
case 3: mMClkDivider = 13; break;
case 5: mMClkDivider = 12; break;
default: mMClkDivider = ( ( mMClkDivisor / 2 ) - 1 ); break;
}
mSerialFormat |= ( mMClkDivider << kMClkDivisorShift );
mSClkDivisor = ( mClockSourceFrequency / mMClkDivisor ) / mSClkFrequency;
debugIOLog(6, "I2STransportInterface::calculateSerialFormatRegisterValue: mSClkDivisor = %ld", mSClkDivisor);
switch ( mSClkDivisor ) {
case 1: mSClkDivider = 8; break;
case 3: mSClkDivider = 9; break;
default: mSClkDivider = ( ( mSClkDivisor / 2 ) - 1 ); break;
}
mSerialFormat |= ( mSClkDivider << kSClkDivisorShift );
switch ( mSclkMultiplier ) {
case 32: mSerialFormat |= ( kSerialFormat32x << kSerialFormatShift ); break;
case 64: mSerialFormat |= ( kSerialFormat64x << kSerialFormatShift ); break;
default: FailIf ( true, Exit ); break;
}
result = kIOReturnSuccess;
Exit:
return result;
}
void I2STransportInterface::waitForClocksStopped ( void ) {
UInt32 timeOut = 100;
while ( ( ( mPlatformObject->getI2SIOMIntControl () & ( 1 << kClocksStoppedPendingShift ) ) == 0 ) && timeOut ) {
IODelay ( 10 );
timeOut--;
}
mPlatformObject->setI2SIOMIntControl ( 1 << kClocksStoppedPendingShift ); }
#pragma mark #--------------------
#pragma mark # USER CLIENT
#pragma mark #--------------------
IOReturn I2STransportInterface::getTransportInterfaceState ( TransportStateStructPtr outState ) {
IOReturn result;
result = super::getTransportInterfaceState ( outState );
if ( NULL != outState && kIOReturnSuccess == result ) {
((TransportStateStructPtr)outState)->instanceState[0] = mMclkMultiplier;
((TransportStateStructPtr)outState)->instanceState[1] = mSclkMultiplier;
((TransportStateStructPtr)outState)->instanceState[2] = mMClkFrequency;
((TransportStateStructPtr)outState)->instanceState[3] = mSClkFrequency;
((TransportStateStructPtr)outState)->instanceState[4] = mClockSourceFrequency;
((TransportStateStructPtr)outState)->instanceState[5] = mMClkDivisor;
((TransportStateStructPtr)outState)->instanceState[6] = mSClkDivisor;
((TransportStateStructPtr)outState)->instanceState[7] = mMClkDivider;
((TransportStateStructPtr)outState)->instanceState[8] = mSClkDivider;
((TransportStateStructPtr)outState)->instanceState[9] = mSerialFormat;
((TransportStateStructPtr)outState)->instanceState[10] = mDataWordSize;
((TransportStateStructPtr)outState)->instanceState[11] = mClockSelector;
((TransportStateStructPtr)outState)->instanceState[12] = (UInt32)mHave45MHzClock;
((TransportStateStructPtr)outState)->instanceState[13] = (UInt32)mHave49MHzClock;
((TransportStateStructPtr)outState)->instanceState[14] = (UInt32)mHave18MHzClock;
((TransportStateStructPtr)outState)->instanceState[15] = transportGetPeakLevel ( kStreamFrontLeft );
((TransportStateStructPtr)outState)->instanceState[16] = transportGetPeakLevel ( kStreamFrontRight );
}
return result;
}
IOReturn I2STransportInterface::setTransportInterfaceState ( TransportStateStructPtr inState ) {
return super::setTransportInterfaceState ( inState );
}