AudioI2SControl.cpp   [plain text]


/*
 *  AudioI2SControl.cpp
 *  AppleOnboardAudio
 *
 *  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)
{
    DEBUG_IOLOG("+ AudioI2SControl::create\n");
    AudioI2SControl *myAudioI2SControl;
    myAudioI2SControl = new AudioI2SControl;
    
    if(myAudioI2SControl) {
        if(!(myAudioI2SControl->init(theInfo))){
            myAudioI2SControl->release();
            myAudioI2SControl = 0;
        }            
    }
    DEBUG_IOLOG("- AudioI2SControl::create\n");
    return myAudioI2SControl;
}

bool AudioI2SControl::init(AudioI2SInfo *theInfo) 
{    
    DEBUG_IOLOG("+ AudioI2SControl::init\n");
	
    IOMemoryMap 		*map = theInfo->map ;

    if(!super::init())
        return(false);

    // 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) 
    {
        ioBaseAddress = (void *)((UInt32)soundConfigSpace - kI2S0BaseOffset);
        i2SInterfaceNumber = kUseI2SCell0;
    }
    else if ((((UInt32)soundConfigSpace ^ kI2S1BaseOffset) & 0x0001FFFF) == 0) 
    {
        ioBaseAddress = (void *)((UInt32)soundConfigSpace - kI2S1BaseOffset);
        i2SInterfaceNumber = kUseI2SCell1;
    }
    else 
    {
        DEBUG_IOLOG("AudioI2SControl::init ERROR: unable to setup ioBaseAddress and i2SInterfaceNumber\n");
    }

    // This is the keylargo FC1 (Feature configuration 1)
    ioClockBaseAddress = (void *)((UInt32)ioBaseAddress + kI2SClockOffset);

    // Finds the position of the status register:
    ioStatusRegister_GPIO12 = (void *)((UInt32)ioBaseAddress + kGPio12);

    // Enables the I2S Interface:
    KLSetRegister(ioClockBaseAddress, KLGetRegister(ioClockBaseAddress) | kI2S0InterfaceEnable);

    // Sets ups the DAC-3550 as required by this machine wiring
    // configuration:
    if (!dependentSetup())
    {
        DEBUG_IOLOG("AudioI2SControl::init ERROR: DAC-3550 setup failed\n");
    }

    DEBUG_IOLOG("- AudioI2SControl::init\n");
    return(true);
}


// --------------------------------------------------------------------------
void AudioI2SControl::free()
{
    super::free();
}

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

bool AudioI2SControl::setSampleParameters(UInt32 sampleRate, UInt32 mclkToFsRatio) 
{
    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
        dacaClockSource = kClock18MHz;
    } else if ((kClock45MHz % reqMClkRate) == 0) {	// next check 45 MHz clock (11.025, 22.050 & 44.100 KHz sample rates)
        dacaClockSource = kClock45MHz;
    } else if ((kClock49MHz % reqMClkRate) == 0) {	// last, try 49 Mhz clock (48.000 & 96.000 KHz sample rates)
        dacaClockSource = kClock49MHz;
    } else {
        CLOG("AppleDACAAudio::setSampleParameters Unable to find a suitable source clock (no globals changes take effect)\n");
        return false;
    }

    // get the MClk divisor
    dacaMclkDivisor = dacaClockSource / reqMClkRate;
    
    switch (dacaSerialFormat) {					// SClk depends on format
        case kSndIOFormatI2SSony:
        case kSndIOFormatI2S64x:
            dacaSclkDivisor = mclkRatio / k64TicksPerFrame;	// SClk divisor is MClk ratio/64
            break;
        case kSndIOFormatI2S32x:
            dacaSclkDivisor = mclkRatio / k32TicksPerFrame;	// SClk divisor is MClk ratio/32
            break;
        default:
            DEBUG_IOLOG("AppleDACAAudio::setSampleParameters Invalid serial format\n");
            return false;
            break;
    }

    return true;
 }

// --------------------------------------------------------------------------
// Method: setSerialFormatRegister ::Set global values to the serial format register
void AudioI2SControl::setSerialFormatRegister(ClockSource clockSource, UInt32 mclkDivisor, UInt32 sclkDivisor, SoundFormat serialFormat)
{
    UInt32	regValue = 0;

    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);
	// 2] Setup the serial format register
	switch ( i2SInterfaceNumber ) {
			case kUseI2SCell0:    I2S0_SetSerialFormatReg(regValue);		break;
			case kUseI2SCell1:    I2S1_SetSerialFormatReg(regValue);		break;
	}
	// 3 restarts the clock:
    clockRun(true);    
}

// --------------------------------------------------------------------------
// Method: dependentSetup
//
// Purpose:
//        this handles the setup of the DAC-3550 chip for each kind of
//        hosting hardware.
bool AudioI2SControl::dependentSetup(void)
{
    DEBUG_IOLOG( "+ AppleDACAAudio::dependentSetup\n");

    // Sets the frame rate:
    UInt32 myFrameRate = frameRate(0);

    // dacaSerialFormat = kSndIOFormatI2SSony;			// start out in Sony format
    dacaSerialFormat = kSndIOFormatI2S32x;
    
    // Reads the initial status of the DAC3550A registers:
    // The following functions return "false" on the first generation of iBooks. However I wish to
    // keep them, since:
    // 1] it is bad to make assumptions on what the hardware can do and can not do here (eventually these
    //    assumptions should be made in the i2c driver).
    // 2] the next generation of iBook may supprot reading the DACA registers, and we will have more precise
    //    values in the "mirror" registers.
//    if (openI2C()) 
//    {
//        // And reads the data we need:
//        interface->readI2CBus((UInt8)i2cBusAddrDAC3550A, i2cBusSubAddrSR_REG, & sampleRateReg, sizeof(sampleRateReg));
//        interface->readI2CBus((UInt8)i2cBusAddrDAC3550A, i2cBusSubAddrAVOL, (UInt8*)& analogVolumeReg, sizeof(analogVolumeReg));
//        interface->readI2CBus((UInt8)i2cBusAddrDAC3550A, i2cBusSubaddrGCFG, & configurationReg, sizeof(configurationReg));
//
//        closeI2C();
//    }

    // If nobody set a format before choose one:
    if (dacaSerialFormat ==  kSndIOFormatUnknown)
        dacaSerialFormat = kSndIOFormatI2SSony;

    // This call will set the next of the drame parametes
    // (dacaClockSource, dacaMclkDivisor,  dacaSclkDivisor)
    if (!setSampleParameters(myFrameRate, 0)) 
    {
        DEBUG_IOLOG("AppleDACAAudio::dependentSetup can not set i2s sample rate\n");
        return false;
//    } 
// this is moved into the AppleDACAAudio class
//    else if (!setDACASampleRate(myFrameRate)) 
//    {
//        DEBUG_IOLOG("AppleDACAAudio::dependentSetup can not set DACA sample rate\n");
//        return false;
    } 
	else 
    {
        setSerialFormatRegister(dacaClockSource, dacaMclkDivisor, dacaSclkDivisor, dacaSerialFormat);
    }
    
    DEBUG_IOLOG( "+ AppleDACAAudio::dependentSetup\n");
    return true;
}

#pragma mark + GENERIC REGISTER ACCESS ROUTINES

// Generic INLINEd methods to access to registers:
// ===============================================
INLINE UInt32 AudioI2SControl::ReadWordLittleEndian(void *address, UInt32 offset )
{
#if 0
    UInt32 *realAddress = (UInt32*)(address) + offset;
    UInt32 value = *realAddress;
    UInt32 newValue =
        ((value & 0x000000FF) << 16) |
        ((value & 0x0000FF00) << 8) |
        ((value & 0x00FF0000) >> 8) |
        ((value & 0xFF000000) >> 16);

    return (newValue);
#else
    return OSReadLittleInt32(address, offset);
#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::WriteWordLittleEndian(void *address, UInt32 offset, UInt32 value)
{
#if 0
    UInt32 *realAddress = (UInt32*)(address) + offset;
    UInt32 newValue =
        ((value & 0x000000FF) << 16) |
        ((value & 0x0000FF00) << 8) |
        ((value & 0x00FF0000) >> 8) |
        ((value & 0xFF000000) >> 16);

    *realAddress = newValue;
#else
    OSWriteLittleInt32(address, offset, value);
#endif    
}

// INLINEd methods to access to all the I2S registers:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S0_GetIntCtlReg(void)
{
	return ReadWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SIntCtlOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S1_GetIntCtlReg(void)
{
	return ReadWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SIntCtlOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S0_SetIntCtlReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SIntCtlOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S1_SetIntCtlReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SIntCtlOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S0_GetSerialFormatReg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SSerialFormatOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S1_GetSerialFormatReg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SSerialFormatOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S0_SetSerialFormatReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SSerialFormatOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S1_SetSerialFormatReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SSerialFormatOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S0_GetCodecMsgOutReg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SCodecMsgOutOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S1_GetCodecMsgOutReg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SCodecMsgOutOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S0_SetCodecMsgOutReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SCodecMsgOutOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S1_SetCodecMsgOutReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SCodecMsgOutOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S0_GetCodecMsgInReg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SCodecMsgInOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S1_GetCodecMsgInReg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SCodecMsgInOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S0_SetCodecMsgInReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SCodecMsgInOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S1_SetCodecMsgInReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SCodecMsgInOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S0_GetFrameCountReg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SFrameCountOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S1_GetFrameCountReg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SFrameCountOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S0_SetFrameCountReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SFrameCountOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S1_SetFrameCountReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SFrameCountOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S0_GetFrameMatchReg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SFrameMatchOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S1_GetFrameMatchReg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SFrameMatchOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S0_SetFrameMatchReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SFrameMatchOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S1_SetFrameMatchReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SFrameMatchOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S0_GetDataWordSizesReg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SDataWordSizesOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S1_GetDataWordSizesReg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SDataWordSizesOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S0_SetDataWordSizesReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SDataWordSizesOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S1_SetDataWordSizesReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SDataWordSizesOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S0_GetPeakLevelSelReg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SPeakLevelSelOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S1_GetPeakLevelSelReg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SPeakLevelSelOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S0_SetPeakLevelSelReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SPeakLevelSelOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S1_SetPeakLevelSelReg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SPeakLevelSelOffset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S0_GetPeakLevelIn0Reg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SPeakLevelIn0Offset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S1_GetPeakLevelIn0Reg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SPeakLevelIn0Offset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S0_SetPeakLevelIn0Reg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SPeakLevelIn0Offset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S1_SetPeakLevelIn0Reg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SPeakLevelIn0Offset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S0_GetPeakLevelIn1Reg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SPeakLevelIn1Offset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S1_GetPeakLevelIn1Reg(void)
{
    return ReadWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SPeakLevelIn1Offset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S0_SetPeakLevelIn1Reg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S0BaseOffset + kI2SPeakLevelIn1Offset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::I2S1_SetPeakLevelIn1Reg(UInt32 value)
{
    WriteWordLittleEndian(ioBaseAddress, kI2S1BaseOffset + kI2SPeakLevelIn1Offset, value);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S0_GetCounterReg(void )
{
    return ((UInt32)(ioBaseAddress) + kI2S0BaseOffset + kI2SFrameCountOffset);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::I2S1_GetCounterReg(void )
{
    return ((UInt32)(ioBaseAddress) + kI2S1BaseOffset + kI2SFrameCountOffset);
}

// Access to Keylargo registers:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE void AudioI2SControl::KLSetRegister(void *klRegister, UInt32 value)
{
    UInt32 *reg = (UInt32*)klRegister;
    *reg = value;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
INLINE UInt32 AudioI2SControl::KLGetRegister(void *klRegister)
{
    UInt32 *reg = (UInt32*)klRegister;
    return (*reg);
}

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

    if (start) {
		switch ( i2SInterfaceNumber ) {
			case kUseI2SCell0:
				KLSetRegister(ioClockBaseAddress, KLGetRegister(ioClockBaseAddress) | kI2S0ClockEnable);
				break;
			case kUseI2SCell1:
				KLSetRegister(ioClockBaseAddress, KLGetRegister(ioClockBaseAddress) | kI2S1ClockEnable);
				break;
		}
    } else {
        UInt16 loop = 50;
		switch ( i2SInterfaceNumber ) {
			case kUseI2SCell0:
				KLSetRegister(ioClockBaseAddress, KLGetRegister(ioClockBaseAddress) & (~kI2S0ClockEnable));
				break;
			case kUseI2SCell1:
				KLSetRegister(ioClockBaseAddress, KLGetRegister(ioClockBaseAddress) & (~kI2S1ClockEnable));
				break;
		}
        
        switch ( i2SInterfaceNumber ) {
			case kUseI2SCell0:
				while (((I2S0_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 =  ((I2S0_GetIntCtlReg() & kClocksStoppedPending) != 0);
				break;
			case kUseI2SCell1:
				while (((I2S1_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 =  ((I2S1_GetIntCtlReg() & kClocksStoppedPending) != 0);
				break;
			default:
				success = false;
				break;
        }
    }

    if (!success)
        debug2IOLog("PPCDACA::clockRun(%s) failed\n", (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;  
}