/*
 *  AudioI2SControl.cpp
 *  Apple02Audio
 *
 *  Created by nthompso on Fri Jul 13 2001.
 *  Copyright (c) 2001 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 *
 * This file contains a class for dealing with i2s for audio drivers.
 */

#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 ); }		//	[3060321]	rbm	1 Oct 2002
	
    IOMemoryMap 		*map = theInfo->map ;

	switch ( theInfo->i2sSerialFormat ) {				//	[3060321]	rbm	2 Oct 2002	begin {
		case kSerialFormatSony:							//	fall through to kSerialFormatSiliLabs
		case kSerialFormat64x:							//	fall through to kSerialFormatSiliLabs
		case kSerialFormat32x:							//	fall through to kSerialFormatSiliLabs
		case kSerialFormatDAV:							//	fall through to kSerialFormatSiliLabs
		case kSerialFormatSiliLabs:
			serialFormat = theInfo->i2sSerialFormat;	//	set the format as requested
			break;
		default:
			debugIOLog (3,  "### WRONG I2S Serial Format" );
			serialFormat = kSerialFormatSony;			//	force a legal value here...
			break;
	}													//	[3060321]	rbm	1 Oct 2002	} end
	
    // cache the config space
	soundConfigSpace = (UInt8 *)map->getPhysicalAddress();

    // sets the clock base address figuring out which I2S cell we're on
    if ((((UInt32)soundConfigSpace ^ kI2S0BaseOffset) & 0x0001FFFF) == 0) 
    {
		//	[3060321]	ioBaseAddress is required by this object in order to enable the target
		//				I2S I/O Module for which this object is to service.  The I2S I/O Module
		//				enable occurs through the configuration registers which reside in the
		//				first block of ioBase.		rbm		2 Oct 2002
		ioBaseAddress = (void *)((UInt32)soundConfigSpace - kI2S0BaseOffset);
		ioBaseAddressMemory = IODeviceMemory::withRange ((IOPhysicalAddress)((UInt8 *)soundConfigSpace - kI2S0BaseOffset), 256);
        i2SInterfaceNumber = kUseI2SCell0;
    }
    else if ((((UInt32)soundConfigSpace ^ kI2S1BaseOffset) & 0x0001FFFF) == 0) 
    {
		//	[3060321]	ioBaseAddress is required by this object in order to enable the target
		//				I2S I/O Module for which this object is to service.  The I2S I/O Module
		//				enable occurs through the configuration registers which reside in the
		//				first block of ioBase.		rbm		2 Oct 2002
		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");
    }

	//
	//	There are three sections of memory mapped I/O that are directly accessed by the Apple02Audio.  These
	//	include the GPIOs, I2S DMA Channel Registers and I2S control registers.  They fall within the memory map 
	//	as follows:
	//	~                              ~
	//	|______________________________|
	//	|                              |
	//	|         I2S Control          |
	//	|______________________________|	<-	soundConfigSpace = ioBase + i2s0BaseOffset ...OR... ioBase + i2s1BaseOffset
	//	|                              |
	//	~                              ~
	//	~                              ~
	//	|______________________________|
	//	|                              |
	//	|       I2S DMA Channel        |
	//	|______________________________|	<-	i2sDMA = ioBase + i2s0_DMA ...OR... ioBase + i2s1_DMA
	//	|                              |
	//	~                              ~
	//	~                              ~
	//	|______________________________|
	//	|            FCRs              |
	//	|            GPIO              |	<-	gpio = ioBase + gpioOffsetAddress
	//	|         ExtIntGPIO           |	<-	fcr = ioBase + fcrOffsetAddress
	//	|______________________________|	<-	ioConfigurationBaseAddress
	//	|                              |
	//	~                              ~
	//
	//	The I2S DMA Channel is mapped in by the Apple02DBDMAAudioDMAEngine.  Only the I2S control registers are 
	//	mapped in by the AudioI2SControl.  The Apple I/O Configuration Space (i.e. FCRs, GPIOs and ExtIntGPIOs)
	//	are mapped in by the subclass of Apple02Audio.  The FCRs must also be mapped in by the AudioI2SControl
	//	object as the init method must enable the I2S I/O Module for which the AudioI2SControl object is
	//	being instantiated for.
	//
	
	//	Map the I2S configuration registers
	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;
	}

	//	Enable the I2S interface by setting the enable bit in the feature 
	//	control register.  This one action requires knowledge of the address 
	//	of I/O configuration address space.		[3060321]	rbm		2 Oct 2002
	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();
}

// --------------------------------------------------------------------------
// Method: setSampleRate :: Sets the sample rate on the I2S bus

bool AudioI2SControl::setSampleParameters(UInt32 sampleRate, UInt32 mclkToFsRatio, ClockSource *pClockSource, UInt32 *pMclkDivisor, UInt32 *pSclkDivisor, UInt32 newSerialFormat) 
{
    UInt32	mclkRatio;
    UInt32	reqMClkRate;
	
    mclkRatio = mclkToFsRatio;		// remember the MClk ratio required

    if ( mclkRatio == 0 )		// or make one up if MClk not required
        mclkRatio = 64;			// use 64 x ratio since that will give us the best characteristics
    
    reqMClkRate = sampleRate * mclkRatio;	// this is the required MClk rate

	// look for a source clock that divides down evenly into the MClk
    if ((kClock18MHz % reqMClkRate) == 0) {  		// preferential source is 18 MHz
        clockSource = kClock18MHz;
    } else if ((kClock45MHz % reqMClkRate) == 0) {	// next check 45 MHz clock (11.025, 22.050 & 44.100 KHz sample rates)
        clockSource = kClock45MHz;
    } else if ((kClock49MHz % reqMClkRate) == 0) {	// last, try 49 Mhz clock (48.000 & 96.000 KHz sample rates)
        clockSource = kClock49MHz;
    } else {
        debugIOLog (3, "AppleDACAAudio::setSampleParameters Unable to find a suitable source clock (no globals changes take effect)");
        return false;
    }
	*pClockSource = clockSource;

    // get the MClk divisor
    mclkDivisor = clockSource / reqMClkRate;
	*pMclkDivisor = mclkDivisor;
    
    switch (newSerialFormat) {					// SClk depends on format	[3060320]	rbm		2 Oct 2002
        case kSndIOFormatI2SSony:
        case kSndIOFormatI2S64x:
            sclkDivisor = mclkRatio / k64TicksPerFrame;	// SClk divisor is MClk ratio/64
			serialFormat = newSerialFormat;
            break;
        case kSndIOFormatI2S32x:
            sclkDivisor = mclkRatio / k32TicksPerFrame;	// SClk divisor is MClk ratio/32
			serialFormat = newSerialFormat;
            break;
        default:
            debugIOLog (3, "AppleDACAAudio::setSampleParameters Invalid serial format");
            return false;
            break;
    }
	*pSclkDivisor = sclkDivisor;

    return true;
 }

// --------------------------------------------------------------------------
// Method: setSerialFormatRegister ::Set global values to the serial format register
void AudioI2SControl::setSerialFormatRegister(ClockSource clockSource, UInt32 mclkDivisor, UInt32 sclkDivisor, SoundFormat serialFormat, UInt32 newDataFormat)
{
    UInt32					regValue = 0;
    IOService *				keyLargo;
	IOReturn				err;
	const OSSymbol*			funcSymbolName = NULL;											//	[3323977]

	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) {		//	sclk is equivalent to Bclk
        case 1:						regValue |= kSClkDivisor1;															break;
        case 3:						regValue |= kSClkDivisor3;															break;
        default:					regValue |= (((sclkDivisor / 2) - 1) << kSClkDivisorShift) & kSClkDivisorMask;		break;
    }
    regValue |= kSClkMaster;		// force master mode

    switch (serialFormat) {
        case kSndIOFormatI2SSony:	regValue |= kSerialFormatSony;														break;
        case kSndIOFormatI2S64x:	regValue |= kSerialFormat64x;														break;
        case kSndIOFormatI2S32x:	regValue |= kSerialFormat32x;														break;
        default:																										break;
    }

	// This is a 3 step process:

	// 1] Stop the clock:
    clockRun(false);

	keyLargo = NULL;
    keyLargo = IOService::waitForService (IOService::serviceMatching ("KeyLargo"));
    
    if (NULL != keyLargo) {
		funcSymbolName = OSSymbol::withCString ( "keyLargo_powerI2S" );						//	[3323977]
		FailIf ( NULL == funcSymbolName, Exit );											//	[3323977]
		// ...turn on the i2s clocks...
		switch ( i2SInterfaceNumber ) {
			case kUseI2SCell0:	err = keyLargo->callPlatformFunction (funcSymbolName, false, (void *)true, (void *)0, 0, 0);	break;	//	[3323977]
			case kUseI2SCell1:	err = keyLargo->callPlatformFunction (funcSymbolName, false, (void *)true, (void *)1, 0, 0);	break;	//	[3323977]
		}
		funcSymbolName->release ();		//	[3323977]
		if ( kIOReturnSuccess != err ) {
			debugIOLog (1,  "keyLargo->callPlatformFunction FAIL" );
		}
	}

	// 2] Setup the serial format register & data format register
	SetSerialFormatReg ( regValue );
	dataFormat = newDataFormat;				//	[3060321]	save this to verify value that was used to init!
	SetDataWordSizesReg ( dataFormat );		//	[3060321]	rbm	2 Oct 2002	MUST OCCUR WHILE CLOCK IS STOPPED
	// 3 restarts the clock:
    clockRun(true);
Exit:
	return;
}


#pragma mark + GENERIC REGISTER ACCESS ROUTINES

// Generic INLINEd methods to access to registers:
// ===============================================
UInt32 AudioI2SControl::ReadWordLittleEndian(void *address, UInt32 offset )
{
    return OSReadLittleInt32(address, offset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void AudioI2SControl::WriteWordLittleEndian(void *address, UInt32 offset, UInt32 value)
{
    OSWriteLittleInt32(address, offset, value);
}

// INLINEd methods to access to all the I2S registers:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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)
{
	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);
}

// Access to Keylargo registers:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	[3110829] register accesses need to translate endian format.
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 ();
    } 
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	[3110829] register accesses need to translate endian format.
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;
}

// --------------------------------------------------------------------------
// Method: clockRun  ::starts and stops the clock count:
bool AudioI2SControl::clockRun(bool start) 
{
    bool success = true;

    if (start) {
		switch ( i2SInterfaceNumber ) {
			case kUseI2SCell0:
				KLSetRegister (kFCR1Offset, KLGetRegister (kFCR1Offset) | kI2S0ClockEnable);
				break;
			case kUseI2SCell1:
				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--)) {
			// it does not do anything, jut waites for the clock
			// to stop
			IOSleep(10);
		}
		// we are successful if the clock actually stopped.
		success = ((GetIntCtlReg() & kClocksStoppedPending) != 0);
    }

    if (!success)
        debugIOLog (3, "PPCDACA::clockRun(%s) failed", (start ? "true" : "false"));

    return success;
}


// --------------------------------------------------------------------------
// Method: frameRate
//
// Purpose:
//        Should look in the registry : for now return a default value

#define kCommonFrameRate 44100

UInt32 AudioI2SControl::frameRate(UInt32 index) 
{
    return (UInt32)kCommonFrameRate;  
}

