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 result;
IOReturn error = kIOReturnError;
debug2IOLog ( "+ I2STransportInterface::init ( %d )\n", (unsigned int)inPlatformInterface );
result = super::init ( inPlatformInterface );
FailIf ( !result, Exit );
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 ); error = mPlatformObject->setI2SClockEnable ( false );
FailIf ( kIOReturnSuccess != error, Exit );
waitForClocksStopped ();
mPlatformObject->setI2SSWReset ( true );
IOSleep ( 10 );
mPlatformObject->setI2SSWReset ( false );
error = mPlatformObject->setDataWordSizes ( mDataWordSize );
FailIf ( kIOReturnSuccess != error, Exit );
error = mPlatformObject->setI2SCellEnable ( true );
FailIf ( kIOReturnSuccess != error, Exit );
error = mPlatformObject->setI2SEnable ( true );
FailIf ( kIOReturnSuccess != error, Exit );
error = transportSetSampleRate ( 44100 );
FailIf ( kIOReturnSuccess != error, Exit );
Exit:
if ( result ) { result = kIOReturnSuccess == error; }
debug3IOLog ( "- I2STransportInterface::init ( %d ) = %d\n", (unsigned int)inPlatformInterface, (unsigned int)result );
return result;
}
void I2STransportInterface::free () {
releaseClockSources ();
super::free();
return;
}
IOReturn I2STransportInterface::transportSetSampleRate ( UInt32 sampleRate ) {
IOReturn result = kIOReturnError;
debug2IOLog ( "+ I2STransportInterface::transportSetSampleRate ( %d )\n", (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 );
debug3IOLog ( "mPlatformObject->setSerialFormatRegiste ( %X ) returns %X\n", (unsigned int)mSerialFormat, (unsigned int)result );
result = mPlatformObject->setDataWordSizes ( mDataWordSize );
FailIf ( kIOReturnSuccess != result, Exit );
debug3IOLog ( "mPlatformObject->setDataWordSizes ( %X ) returns %X\n", (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 );
#ifdef kVERBOSE_LOG
mPlatformObject->LogFCR ();
mPlatformObject->LogI2S ();
#endif
Exit:
debug2IOLog ( "- transportSetSampleRate ( %d )\n", (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 ( "+ I2STransportInterface::performTransportSleep ()\n" );
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:
debug2IOLog ( "- I2STransportInterface::performTransportSleep () = %d\n", (unsigned int)result );
return result;
}
IOReturn I2STransportInterface::performTransportWake ( void ) {
IOReturn result = kIOReturnError;
debugIOLog ( "+ I2STransportInterface::performTransportWake ()\n" );
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);
debug2IOLog ( "- I2STransportInterface::performTransportWake () = %d\n", (unsigned int)result );
Exit:
return result;
}
IOReturn I2STransportInterface::transportBreakClockSelect ( UInt32 clockSource ) {
IOReturn result = kIOReturnError;
debug2IrqIOLog ( "+ I2STransportInterface::transportBreakClockSelect ( %d )\n", (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 );
debug3IrqIOLog ( "mPlatformObject->setSerialFormatRegiste ( %X ) returns %X\n", (unsigned int)mSerialFormat, (unsigned int)result );
result = mPlatformObject->setDataWordSizes ( mDataWordSize );
FailIf ( kIOReturnSuccess != result, Exit );
debug3IrqIOLog ( "mPlatformObject->setDataWordSizes ( %X ) returns %X\n", (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:
debugIrqIOLog ( "... kTRANSPORT_MASTER_CLOCK requires no action\n" );
break;
case kTRANSPORT_SLAVE_CLOCK:
debugIrqIOLog ( "... setting clock mux to ALTERNATE source\n" );
result = mPlatformObject->setClockMux ( kGPIO_MuxSelectAlternate );
FailIf ( kIOReturnSuccess != result, Exit );
break;
default:
FailIf ( true, Exit );
break;
}
Exit:
#ifdef kVERBOSE_LOG
mPlatformObject->LogFCR ();
mPlatformObject->LogI2S ();
#endif
debug3IrqIOLog ( "- I2STransportInterface::transportBreakClockSelect ( %d ) = %X\n", (unsigned int)clockSource, (unsigned int)result );
return result;
}
IOReturn I2STransportInterface::transportMakeClockSelect ( UInt32 clockSource ) {
IOReturn result = kIOReturnError;
debug2IrqIOLog ( "+ I2STransportInterface::transportMakeClockSelect ( %d )\n", (unsigned int)clockSource );
FailIf ( NULL == mPlatformObject, Exit );
switch ( clockSource ) {
case kTRANSPORT_MASTER_CLOCK:
debugIrqIOLog ( "... setting clock mux to DEFAULT source\n" );
result = mPlatformObject->setClockMux ( kGPIO_MuxSelectDefault );
FailIf ( kIOReturnSuccess != result, Exit );
debugIrqIOLog ( "... setting kBClkMasterShift to MASTER\n" );
break;
case kTRANSPORT_SLAVE_CLOCK:
debugIrqIOLog ( "... kTRANSPORT_SLAVE_CLOCK requires no action\n" );
result = kIOReturnSuccess;
break;
default:
FailIf ( true, Exit );
break;
}
Exit:
result = mPlatformObject->setI2SClockEnable ( true );
#ifdef kVERBOSE_LOG
mPlatformObject->LogFCR ();
mPlatformObject->LogI2S ();
#endif
debug3IrqIOLog ( "- I2STransportInterface::transportMakeClockSelect ( %d ) = %X\n", (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_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_88Khz_LowerLimt <= curSerialFormat ) && ( curSerialFormat <= kSampleRate_88Khz_UpperLimt ) ) {
result = 88200;
} else if ( ( kSampleRate_96Khz_LowerLimt <= curSerialFormat ) && ( curSerialFormat <= kSampleRate_96Khz_UpperLimt ) ) {
result = 96000;
} 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;
}
#pragma mark #--------------------
#pragma mark # PRIVATE METHODS
#pragma mark #--------------------
IOReturn I2STransportInterface::requestClockSources () {
IOReturn result = kIOReturnError;
FailMessage (mHave18MHzClock || mHave45MHzClock || mHave49MHzClock);
if (FALSE == mHave45MHzClock) {
result = mPlatformObject->requestI2SClockSource (kI2S_45MHz);
if (kIOReturnSuccess != result) {
IOLog ("I2STransportInterface failed to acquire 45 MHz clock.\n");
} else {
mHave45MHzClock = TRUE;
}
}
if (FALSE == mHave49MHzClock) {
result = mPlatformObject->requestI2SClockSource (kI2S_49MHz);
if (kIOReturnSuccess != result) {
IOLog ("I2STransportInterface failed to acquire 49 MHz clock.\n");
} else {
mHave49MHzClock = TRUE;
}
}
if (FALSE == mHave18MHzClock) {
result = mPlatformObject->requestI2SClockSource (kI2S_18MHz);
if (kIOReturnSuccess != result) {
IOLog ("I2STransportInterface failed to acquire 18 MHz clock.\n");
} 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) {
IOLog ("Failed to release 45 MHz clock.\n");
} else {
mHave45MHzClock = FALSE;
}
}
if (TRUE == mHave49MHzClock) {
result = mPlatformObject->releaseI2SClockSource (kI2S_49MHz);
if (kIOReturnSuccess != result) {
IOLog ("Failed to release 49 MHz clock.\n");
} else {
mHave49MHzClock = FALSE;
}
}
if (TRUE == mHave18MHzClock) {
result = mPlatformObject->releaseI2SClockSource (kI2S_18MHz);
if (kIOReturnSuccess != result) {
IOLog ("Failed to release 18 MHz clock.\n");
} 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;
if ( 0 == ( kClock18MHz % mMClkFrequency ) ) {
mClockSourceFrequency = kClock18MHz;
mClockSelector = kI2S_18MHz;
mSerialFormat = ( kClockSource18MHz << kClockSourceShift );
} else if ( 0 == ( kClock45MHz % mMClkFrequency ) ) {
mClockSourceFrequency = kClock45MHz;
mClockSelector = kI2S_45MHz;
mSerialFormat = ( kClockSource45MHz << kClockSourceShift );
} else if ( 0 == ( kClock49MHz % mMClkFrequency ) ) {
mClockSourceFrequency = kClock49MHz;
mClockSelector = kI2S_49MHz;
mSerialFormat = ( kClockSource49MHz << kClockSourceShift );
} else {
FailIf ( true, Exit );
}
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;
switch ( mMClkDivisor ) {
case 1: mMClkDivider = 0x00000014; break;
case 3: mMClkDivider = 0x00000013; break;
case 5: mMClkDivider = 0x00000012; break;
default: mMClkDivider = ( ( mMClkDivisor / 2 ) - 1 ); break;
}
mSerialFormat |= ( mMClkDivider << kMClkDivisorShift );
mSClkDivisor = ( mClockSourceFrequency / mMClkDivisor ) / mSClkFrequency;
switch ( mSClkDivisor ) {
case 1: mSClkDivider = 0x00000008; break;
case 3: mSClkDivider = 0x00000009; 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;
}
return result;
}
IOReturn I2STransportInterface::setTransportInterfaceState ( TransportStateStructPtr inState ) {
return super::setTransportInterfaceState ( inState );
}