AudioI2SControl.cpp [plain text]
#include "AudioI2SControl.h"
#include "daca_hw.h"
#include "AudioI2SHardwareConstants.h"
#define super OSObject
OSDefineMetaClassAndStructors(AudioI2SControl, OSObject)
AudioI2SControl *AudioI2SControl::create(AudioI2SInfo *theInfo)
{
debugIOLog (3, "+ AudioI2SControl::create");
AudioI2SControl *myAudioI2SControl;
myAudioI2SControl = new AudioI2SControl;
if(myAudioI2SControl) {
if(!(myAudioI2SControl->init(theInfo))){
myAudioI2SControl->release();
myAudioI2SControl = 0;
}
}
debugIOLog (3, "- AudioI2SControl::create");
return myAudioI2SControl;
}
bool AudioI2SControl::init(AudioI2SInfo *theInfo)
{
UInt32 tempFcr1;
debugIOLog (3, "+ AudioI2SControl::init");
if(!super::init())
return(false);
if ( NULL == theInfo ) { return ( false ); }
IOMemoryMap *map = theInfo->map ;
switch ( theInfo->i2sSerialFormat ) { case kSerialFormatSony: case kSerialFormat64x: case kSerialFormat32x: case kSerialFormatDAV: case kSerialFormatSiliLabs:
serialFormat = theInfo->i2sSerialFormat; break;
default:
debugIOLog (3, "### WRONG I2S Serial Format" );
serialFormat = kSerialFormatSony; break;
}
soundConfigSpace = (UInt8 *)map->getPhysicalAddress();
if ((((UInt32)soundConfigSpace ^ kI2S0BaseOffset) & 0x0001FFFF) == 0)
{
ioBaseAddress = (void *)((UInt32)soundConfigSpace - kI2S0BaseOffset);
ioBaseAddressMemory = IODeviceMemory::withRange ((IOPhysicalAddress)((UInt8 *)soundConfigSpace - kI2S0BaseOffset), 256);
i2SInterfaceNumber = kUseI2SCell0;
}
else if ((((UInt32)soundConfigSpace ^ kI2S1BaseOffset) & 0x0001FFFF) == 0)
{
ioBaseAddress = (void *)((UInt32)soundConfigSpace - kI2S1BaseOffset);
ioBaseAddressMemory = IODeviceMemory::withRange ((IOPhysicalAddress)((UInt8 *)soundConfigSpace - kI2S1BaseOffset), 256);
i2SInterfaceNumber = kUseI2SCell1;
}
else
{
debugIOLog (3, "AudioI2SControl::init ERROR: unable to setup ioBaseAddress and i2SInterfaceNumber");
}
if (kUseI2SCell0 == i2SInterfaceNumber) {
ioI2SBaseAddressMemory = IODeviceMemory::withRange ((IOPhysicalAddress)((UInt8 *)soundConfigSpace), kI2S_IO_CONFIGURATION_SIZE);
} else {
ioI2SBaseAddressMemory = IODeviceMemory::withRange ((IOPhysicalAddress)((UInt8 *)soundConfigSpace), kI2S_IO_CONFIGURATION_SIZE);
}
if (NULL != ioI2SBaseAddressMemory) {
i2sBaseAddress = (void *)ioI2SBaseAddressMemory->map()->getVirtualAddress();
} else {
return false;
}
tempFcr1 = KLGetRegister ( kFCR1Offset );
if ( kUseI2SCell0 == i2SInterfaceNumber ) {
tempFcr1 &= ~( 1 << kI2S0SwReset );
KLSetRegister ( kFCR1Offset, tempFcr1 );
KLSetRegister ( kFCR1Offset, tempFcr1 | kI2S0InterfaceEnable );
} else {
tempFcr1 &= ~( 1 << kI2S1SwReset );
KLSetRegister ( kFCR1Offset, tempFcr1 );
KLSetRegister ( kFCR1Offset, tempFcr1 | kI2S1InterfaceEnable );
}
debugIOLog (3, "- AudioI2SControl::init");
return(true);
}
void AudioI2SControl::free()
{
if (NULL != ioBaseAddressMemory) {
ioBaseAddressMemory->release();
}
if (NULL != ioI2SBaseAddressMemory) {
ioI2SBaseAddressMemory->release();
}
super::free();
}
bool AudioI2SControl::setSampleParameters(UInt32 sampleRate, UInt32 mclkToFsRatio, ClockSource *pClockSource, UInt32 *pMclkDivisor, UInt32 *pSclkDivisor, UInt32 newSerialFormat)
{
UInt32 mclkRatio;
UInt32 reqMClkRate;
mclkRatio = mclkToFsRatio;
if ( mclkRatio == 0 ) mclkRatio = 64;
reqMClkRate = sampleRate * mclkRatio;
if ((kClock18MHz % reqMClkRate) == 0) { clockSource = kClock18MHz;
} else if ((kClock45MHz % reqMClkRate) == 0) { clockSource = kClock45MHz;
} else if ((kClock49MHz % reqMClkRate) == 0) { clockSource = kClock49MHz;
} else {
debugIOLog (3, "AppleDACAAudio::setSampleParameters Unable to find a suitable source clock (no globals changes take effect)");
return false;
}
*pClockSource = clockSource;
mclkDivisor = clockSource / reqMClkRate;
*pMclkDivisor = mclkDivisor;
switch (newSerialFormat) { case kSndIOFormatI2SSony:
case kSndIOFormatI2S64x:
sclkDivisor = mclkRatio / k64TicksPerFrame; serialFormat = newSerialFormat;
break;
case kSndIOFormatI2S32x:
sclkDivisor = mclkRatio / k32TicksPerFrame; serialFormat = newSerialFormat;
break;
default:
debugIOLog (3, "AppleDACAAudio::setSampleParameters Invalid serial format");
return false;
break;
}
*pSclkDivisor = sclkDivisor;
return true;
}
void AudioI2SControl::setSerialFormatRegister(ClockSource clockSource, UInt32 mclkDivisor, UInt32 sclkDivisor, SoundFormat serialFormat, UInt32 newDataFormat)
{
UInt32 regValue = 0;
IOService * keyLargo;
IOReturn err;
const OSSymbol* funcSymbolName = NULL;
err = kIOReturnError;
switch ((int)clockSource) {
case kClock18MHz: regValue = kClockSource18MHz; break;
case kClock45MHz: regValue = kClockSource45MHz; break;
case kClock49MHz: regValue = kClockSource49MHz; break;
default: break;
}
switch (mclkDivisor) {
case 1: regValue |= kMClkDivisor1; break;
case 3: regValue |= kMClkDivisor3; break;
case 5: regValue |= kMClkDivisor5; break;
default: regValue |= (((mclkDivisor / 2) - 1) << kMClkDivisorShift) & kMClkDivisorMask; break;
}
switch ((int)sclkDivisor) { case 1: regValue |= kSClkDivisor1; break;
case 3: regValue |= kSClkDivisor3; break;
default: regValue |= (((sclkDivisor / 2) - 1) << kSClkDivisorShift) & kSClkDivisorMask; break;
}
regValue |= kSClkMaster;
switch (serialFormat) {
case kSndIOFormatI2SSony: regValue |= kSerialFormatSony; break;
case kSndIOFormatI2S64x: regValue |= kSerialFormat64x; break;
case kSndIOFormatI2S32x: regValue |= kSerialFormat32x; break;
default: break;
}
clockRun(false);
keyLargo = NULL;
keyLargo = IOService::waitForService (IOService::serviceMatching ("KeyLargo"));
if (NULL != keyLargo) {
funcSymbolName = OSSymbol::withCString ( "keyLargo_powerI2S" ); FailIf ( NULL == funcSymbolName, Exit ); switch ( i2SInterfaceNumber ) {
case kUseI2SCell0: err = keyLargo->callPlatformFunction (funcSymbolName, false, (void *)true, (void *)0, 0, 0); break; case kUseI2SCell1: err = keyLargo->callPlatformFunction (funcSymbolName, false, (void *)true, (void *)1, 0, 0); break; }
funcSymbolName->release (); if ( kIOReturnSuccess != err ) {
debugIOLog (1, "keyLargo->callPlatformFunction FAIL" );
}
}
SetSerialFormatReg ( regValue );
dataFormat = newDataFormat; SetDataWordSizesReg ( dataFormat ); clockRun(true);
Exit:
return;
}
#pragma mark + GENERIC REGISTER ACCESS ROUTINES
UInt32 AudioI2SControl::ReadWordLittleEndian(void *address, UInt32 offset )
{
return OSReadLittleInt32(address, offset);
}
void AudioI2SControl::WriteWordLittleEndian(void *address, UInt32 offset, UInt32 value)
{
OSWriteLittleInt32(address, offset, value);
}
UInt32 AudioI2SControl::GetIntCtlReg(void)
{
return ReadWordLittleEndian(i2sBaseAddress, kI2SIntCtlOffset);
}
void AudioI2SControl::SetIntCtlReg(UInt32 value)
{
WriteWordLittleEndian(i2sBaseAddress, kI2SIntCtlOffset, value);
}
UInt32 AudioI2SControl::GetSerialFormatReg(void)
{
return ReadWordLittleEndian(i2sBaseAddress, kI2SSerialFormatOffset);
}
void AudioI2SControl::SetSerialFormatReg(UInt32 value)
{
debugIOLog ( 5, "± AudioI2SControl::SetSerialFormatReg ( 0x%lX )", value );
WriteWordLittleEndian(i2sBaseAddress, kI2SSerialFormatOffset, value);
}
UInt32 AudioI2SControl::GetCodecMsgOutReg(void)
{
return ReadWordLittleEndian(i2sBaseAddress, kI2SCodecMsgOutOffset);
}
void AudioI2SControl::SetCodecMsgOutReg(UInt32 value)
{
WriteWordLittleEndian(i2sBaseAddress, kI2SCodecMsgOutOffset, value);
}
UInt32 AudioI2SControl::GetCodecMsgInReg(void)
{
return ReadWordLittleEndian(i2sBaseAddress, kI2SCodecMsgInOffset);
}
void AudioI2SControl::SetCodecMsgInReg(UInt32 value)
{
WriteWordLittleEndian(i2sBaseAddress, kI2SCodecMsgInOffset, value);
}
UInt32 AudioI2SControl::GetFrameCountReg(void)
{
return ReadWordLittleEndian(i2sBaseAddress, kI2SFrameCountOffset);
}
void AudioI2SControl::SetFrameCountReg(UInt32 value)
{
WriteWordLittleEndian(i2sBaseAddress, kI2SFrameCountOffset, value);
}
UInt32 AudioI2SControl::GetFrameMatchReg(void)
{
return ReadWordLittleEndian(i2sBaseAddress, kI2SFrameMatchOffset);
}
void AudioI2SControl::SetFrameMatchReg(UInt32 value)
{
WriteWordLittleEndian(i2sBaseAddress, kI2SFrameMatchOffset, value);
}
UInt32 AudioI2SControl::GetDataWordSizesReg(void)
{
return ReadWordLittleEndian(i2sBaseAddress, kI2SDataWordSizesOffset);
}
void AudioI2SControl::SetDataWordSizesReg(UInt32 value)
{
WriteWordLittleEndian(i2sBaseAddress, kI2SDataWordSizesOffset, value);
}
UInt32 AudioI2SControl::GetPeakLevelSelReg(void)
{
return ReadWordLittleEndian(i2sBaseAddress, kI2SPeakLevelSelOffset);
}
void AudioI2SControl::SetPeakLevelSelReg(UInt32 value)
{
WriteWordLittleEndian(i2sBaseAddress, kI2SPeakLevelSelOffset, value);
}
UInt32 AudioI2SControl::GetPeakLevelIn0Reg(void)
{
return ReadWordLittleEndian(i2sBaseAddress, kI2SPeakLevelIn0Offset);
}
void AudioI2SControl::SetPeakLevelIn0Reg(UInt32 value)
{
WriteWordLittleEndian(i2sBaseAddress, kI2SPeakLevelIn0Offset, value);
}
UInt32 AudioI2SControl::GetPeakLevelIn1Reg(void)
{
return ReadWordLittleEndian(i2sBaseAddress, kI2SPeakLevelIn1Offset);
}
void AudioI2SControl::SetPeakLevelIn1Reg(UInt32 value)
{
WriteWordLittleEndian(i2sBaseAddress, kI2SPeakLevelIn1Offset, value);
}
UInt32 AudioI2SControl::GetCounterReg(void )
{
return ((UInt32)(i2sBaseAddress) + kI2SFrameCountOffset);
}
UInt32 AudioI2SControl::FCR1GetReg( void )
{
return KLGetRegister (kFCR1Offset);
}
void AudioI2SControl::Fcr1SetReg(UInt32 value)
{
KLSetRegister (kFCR1Offset, value);
}
UInt32 AudioI2SControl::FCR3GetReg( void )
{
return KLGetRegister (kFCR3Offset);
}
void AudioI2SControl::Fcr3SetReg(UInt32 value)
{
KLSetRegister (kFCR3Offset, value);
}
void AudioI2SControl::KLSetRegister(UInt32 klRegisterOffset, UInt32 value)
{
IOService * keyLargoService = NULL;
UInt32 mask = kAudioFCR1Mask;
const OSSymbol * theSymbol;
theSymbol = OSSymbol::withCString ("keyLargo_safeWriteRegUInt32");
keyLargoService = IOService::waitForService (IOService::serviceMatching ("KeyLargo"));
if (keyLargoService && theSymbol) {
keyLargoService->callPlatformFunction (theSymbol, false, (void *)klRegisterOffset, (void *)mask, (void *) value, 0);
theSymbol->release ();
}
}
UInt32 AudioI2SControl::KLGetRegister(UInt32 klRegisterOffset)
{
UInt32 value = 0;
IOService * keyLargoService = NULL;
const OSSymbol * theSymbol;
theSymbol = OSSymbol::withCString ("keyLargo_safeReadRegUInt32");
keyLargoService = IOService::waitForService (IOService::serviceMatching ("KeyLargo"));
if (keyLargoService && theSymbol) {
keyLargoService->callPlatformFunction (theSymbol, false, (void *)klRegisterOffset, &value, 0, 0);
theSymbol->release ();
}
return value;
}
bool AudioI2SControl::clockRun(bool start)
{
bool success = true;
if (start) {
switch ( i2SInterfaceNumber ) {
case kUseI2SCell0:
KLSetRegister (kFCR1Offset, KLGetRegister (kFCR1Offset) | kI2S0InterfaceEnable);
KLSetRegister (kFCR1Offset, KLGetRegister (kFCR1Offset) | kI2S0CellEnable);
KLSetRegister (kFCR1Offset, KLGetRegister (kFCR1Offset) | kI2S0ClockEnable);
break;
case kUseI2SCell1:
KLSetRegister (kFCR1Offset, KLGetRegister (kFCR1Offset) | kI2S1InterfaceEnable);
KLSetRegister (kFCR1Offset, KLGetRegister (kFCR1Offset) | kI2S1CellEnable);
KLSetRegister (kFCR1Offset, KLGetRegister (kFCR1Offset) | kI2S1ClockEnable);
break;
default:
debugIOLog (1, "\n\n\n!!!!Wrong I2S interface number!!!!\n\n");
}
} else {
UInt16 loop = 50;
switch ( i2SInterfaceNumber ) {
case kUseI2SCell0:
KLSetRegister (kFCR1Offset, KLGetRegister (kFCR1Offset) | kI2S0InterfaceEnable);
KLSetRegister (kFCR1Offset, KLGetRegister (kFCR1Offset) | kI2S0CellEnable);
KLSetRegister (kFCR1Offset, KLGetRegister (kFCR1Offset) & ~kI2S0ClockEnable);
break;
case kUseI2SCell1:
KLSetRegister (kFCR1Offset, KLGetRegister (kFCR1Offset) | kI2S1InterfaceEnable);
KLSetRegister (kFCR1Offset, KLGetRegister (kFCR1Offset) | kI2S1CellEnable);
KLSetRegister (kFCR1Offset, KLGetRegister (kFCR1Offset) & ~kI2S1ClockEnable);
break;
default:
debugIOLog (1, "\n\n\n!!!!Wrong I2S interface number!!!!\n\n");
}
while (((GetIntCtlReg() & kClocksStoppedPending) == 0) && (loop--)) {
IOSleep(10);
}
success = ((GetIntCtlReg() & kClocksStoppedPending) != 0);
}
if (!success)
debugIOLog (3, "PPCDACA::clockRun(%s) failed", (start ? "true" : "false"));
return success;
}
#define kCommonFrameRate 44100
UInt32 AudioI2SControl::frameRate(UInt32 index)
{
return (UInt32)kCommonFrameRate;
}