AppleOWScreamerAudio.cpp   [plain text]


/*
 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Hardware independent (relatively) code for the Awacs Controller
 *
 * HISTORY
 *
 * 14-Jan-1999
 *	Created.
 *
 */

#include "AudioHardwareCommon.h"
#include "AudioHardwareUtilities.h"
#include "AppleOWScreamerAudio.h"
#include "awacs_OWhw.h"
#include "AppleDBDMAAudioDMAEngine.h"
#include "AudioHardwareConstants.h"

/*
 * From Excelsior:Toolbox:SoundMgr:Hardware:Ports:OutPorts.cp & GossamerOut.h
 */
#define kSGS7433Addr 0x8A
#define kSGSNumRegs 7
unsigned char SGSShadowRegs[kSGSNumRegs] =
                                        { 0x09,		/* Adr:1, AutoIncr 				*/
                                          0x20, 	/* Reg 1: Vol  = 0dB				*/
                                          0xFF, 	/* Reg 2: Bass = 0dB, Treble = 0dB		*/
                                          0x00, 	/* Reg 3: Internal Spkr - Left  Atten = 0dB	*/
                                          0x0A,     /* Reg 4: Line Out	- Left  Atten = -10dB	*/
                                          0x00,     /* Reg 5: Internal Spkr - Right Atten = 0dB	*/
                                          0x0A };	/* Reg 6: Line Out	- Right Atten = -10dB   */
#define	sgsBalMuteBit		0x020	// When this bit is set in the output fader/ balance regs, the channel is muted
#define	sgsBalVolBits		0x1F	// mask for volume steps
#define kVolReg				0x01	// volume register
#define	kLFAttnReg			0x03	// Left Front (ie everything except the rear spkr jack)
#define	kRFAttnReg			0x05	// Right Front
#define	kLRAttnReg			0x04	// Left Rear (the rear speaker jack only)
#define	kRRAttnReg			0x06	// Right Rear

#define NUM_POWER_STATES	2

static void 	writeCodecControlReg( volatile awacsOW_regmap_t *ioBaseAwacs, int value );
static void 	writeSoundControlReg( volatile awacsOW_regmap_t *ioBaseAwacs, int value );
static UInt32 readCodecStatusReg( volatile awacsOW_regmap_t *ioBaseAwacs );


static void writeCodecControlReg( volatile awacsOW_regmap_t *ioBaseAwacs, int value )
{
  int          CodecControlReg;

#ifdef DEBUGMODE
      DEBUG_IOLOG( "PPCSound(awacs): CodecControlReg @ %08x = %08x\n", (int)&ioBaseAwacs->CodecControlRegister, value);
#endif

  OSWriteLittleInt32(&ioBaseAwacs->CodecControlRegister, 0, value );
  eieio();

  do{
      CodecControlReg =  OSReadLittleInt32( &ioBaseAwacs->CodecControlRegister, 0 );
      eieio();
    }
  while ( CodecControlReg & kCodecCtlBusy );
}


static void writeSoundControlReg( volatile awacsOW_regmap_t *ioBaseAwacs, int value )
{

  DEBUG2_IOLOG( "PPCSound(awacs): SoundControlReg = %08x\n", value);
  OSWriteLittleInt32( &ioBaseAwacs->SoundControlRegister, 0, value );
  eieio();
}

static UInt32 readCodecStatusReg( volatile awacsOW_regmap_t *ioBaseAwacs )
{
  return OSReadLittleInt32( &ioBaseAwacs->CodecStatusRegister, 0 );
}

// static int readClippingCountReg( volatile awacsOW_regmap_t *ioBaseAwacs )
// {
//	return OSReadLittleInt32( &ioBaseAwacs->ClippingCountRegister, 0 );
// }

static void writeSGSRegs()
{
    int i;
    for (i = 0; i < kSGSNumRegs; i++) {
        (*PE_write_IIC)(kSGS7433Addr, i, SGSShadowRegs[i]);
    }
}

#define super IOAudioDevice
OSDefineMetaClassAndStructors(AppleOWScreamerAudio, IOAudioDevice)

bool AppleOWScreamerAudio::init(OSDictionary *properties)
{
    CLOG("+AppleOWScreamerAudio::init\n");
    if (!super::init(properties)) {
        return false;
    }

    awacsVersion = kAWACSMaxVersion;

    duringInitialization = true;
    curActiveSpkr = kCpuSpkr;
    gVolLeft = 255;
    gVolRight = 255;
    CLOG("-AppleOWScreamerAudio::init\n");
    return true;
}

void AppleOWScreamerAudio::free()
{
    CLOG("+AppleOWScreamerAudio::free\n");
    super::free();
    CLOG("-AppleOWScreamerAudio::free\n");
}

void AppleOWScreamerAudio::retain() const
{
    CLOG("+AppleOWScreamerAudio::retain\n");
    super::retain();
    CLOG("-AppleOWScreamerAudio::retain\n");
}

void AppleOWScreamerAudio::release() const
{
    CLOG("+AppleOWScreamerAudio::release\n");
    super::release();
    CLOG("-AppleOWScreamerAudio::release\n");
}

bool AppleOWScreamerAudio::start(IOService *provider)
{
    IOMemoryMap *		map;
    AppleDBDMAAudioDMAEngine 	*driverDMAEngine;
    IORegistryEntry *		perch = 0;
    IORegistryEntry *		sound = 0;
    AbsoluteTime		timerInterval;

    CLOG("+ AppleOWScreamerAudio::start\n");
    setManufacturerName("Apple");
    setDeviceName("Built-in audio controller");
    
    map = provider->mapDeviceMemoryWithIndex(AppleDBDMAAudioDMAEngine::kDBDMADeviceIndex);
    if (!map) {
        return false;
    }
    ioBase = (awacsOW_regmap_t *)map->getVirtualAddress();

    driverDMAEngine = new AppleDBDMAAudioDMAEngine;
    if (!driverDMAEngine->init(NULL, provider, true)) {
        driverDMAEngine->release();
        return false;
    }
    
	// Have to create the controls before calling activateAudioEngine
    if (!createPorts(driverDMAEngine)) {
        return false;
    }
    
    if( kIOReturnSuccess != activateAudioEngine(driverDMAEngine)){
        driverDMAEngine->release();
        return false;
    }
    
    driverDMAEngine->release();
    
    numDetects = 2;
    numOutputs = 2;

    sound = provider->childFromPath("sound", gIODTPlane);
    if (sound) {
        OSData *t;
        t = OSDynamicCast(OSData, sound->getProperty("#-detects"));
        if(t) {
            numDetects = *(UInt32*)(t->getBytesNoCopy());
        }
        t = OSDynamicCast(OSData, sound->getProperty("#-outputs"));
        if(t) {
            numOutputs = *(UInt32*)(t->getBytesNoCopy());
        }        
    }


    perch = IORegistryEntry::fromPath("/perch", gIODTPlane );

    iicAudioDevicePresent = (perch != NULL);
    if(iicAudioDevicePresent) {
        waitForService( resourceMatching( "IOiic0" ));
    }

    initHardware();
    
	/*
	* Mask out the input bits in the cached status register so that the AudioComponents
	* get updated to the correct values the first time we update the device status
	* (Everything is initialized on the assumption that nothing is plugged in)
	*/
    codecStatus &= ~kAllSense;

    CLEAN_RELEASE(perch);
    CLEAN_RELEASE(sound);

    sndHWSetActiveInputExclusive(kSndHWInputNone);
    flushAudioControls();

    if (!super::start(provider)) {
        return false;
    }

    nanoseconds_to_absolutetime(NSEC_PER_SEC, &timerInterval);
    addTimerEvent(this, &AppleOWScreamerAudio::timerCallback, timerInterval);

	if (NULL != driverDMAEngine)
		driverDMAEngine->setSampleLatencies (kScreamerOWSampleLatency, kScreamerOWSampleLatency);

    duringInitialization = false;
    CLOG("- AppleOWScreamerAudio::start\n");
    return true;
}

void AppleOWScreamerAudio::stop(IOService *provider)
{
    CLOG("- AppleOWScreamerAudio::stop\n");
    super::stop(provider);
    CLOG("- AppleOWScreamerAudio::stop\n");
}

void AppleOWScreamerAudio::initHardware()
{
    CLOG("+ AppleOWScreamerAudio::initHardware\n");
    codecStatus = readCodecStatusReg( ioBase );
    awacsVersion = (codecStatus & kRevisionNumberMask) >> kRevisionNumberShft;

    if ( awacsVersion > kAWACSMaxVersion ) {
        codecControlRegister[5] = kCodecCtlAddress5 | kCodecCtlEMSelect0;
        writeCodecControlReg(  ioBase, codecControlRegister[5] );
        codecControlRegister[6] = kCodecCtlAddress6 | kCodecCtlEMSelect0;
        writeCodecControlReg(  ioBase, codecControlRegister[6] );
    }

    if ( iicAudioDevicePresent ) {
        writeSGSRegs();
    }

    soundControlRegister = ( kInSubFrame0      |
                              kOutSubFrame0     |
                              kHWRate44100        );
    writeSoundControlReg( ioBase, soundControlRegister);

    codecControlRegister[0] = kCodecCtlAddress0 | kCodecCtlEMSelect0;
    codecControlRegister[1] = kCodecCtlAddress1 | kCodecCtlEMSelect0;
    codecControlRegister[2] = kCodecCtlAddress2 | kCodecCtlEMSelect0;
    codecControlRegister[4] = kCodecCtlAddress4 | kCodecCtlEMSelect0;

    codecControlRegister[0] |= (kMicInput | kDefaultMicGain);

    // Beige G3 passes sound right through to be later controlled
    // by the SGS audio processor--turn on these pass-thru ports.
    if ( iicAudioDevicePresent ) {
        codecControlRegister[1] |= (kParallelOutputEnable);
    }

    writeCodecControlReg(  ioBase, codecControlRegister[0] );
    writeCodecControlReg(  ioBase, codecControlRegister[1] );
    writeCodecControlReg(  ioBase, codecControlRegister[2] );
    writeCodecControlReg(  ioBase, codecControlRegister[4] );
    
    CLOG("- AppleOWScreamerAudio::initHardware\n");
}

void AppleOWScreamerAudio::recalibrate()
{
    CLOG("+ AppleOWScreamerAudio::recalibrate\n");
    UInt32 tempCodecControlReg1;

    tempCodecControlReg1 = codecControlRegister[1];

    tempCodecControlReg1 |= (kMuteInternalSpeaker | kMuteHeadphone);
    // tempCodecControlReg1 |= kRecalibrate;

    IOSleep(10);
    writeCodecControlReg(ioBase, tempCodecControlReg1);
    IOSleep(10);
    writeCodecControlReg(ioBase, codecControlRegister[1]);
    CLOG("- AppleOWScreamerAudio::recalibrate\n");
}

IOService* AppleOWScreamerAudio::probe(IOService *provider, SInt32* score){
    IORegistryEntry *sound = 0;
    CLOG("+ AppleOWScreamerAudio::probe\n");
    
    super::probe(provider, score);
    *score = kIODefaultProbeScore;
    sound = provider->childFromPath("sound", gIODTPlane);
	// we are on a old world if there is no sound entry
    if(!sound) {
        *score = *score+1;
        return(this);
    } else {
        if (!(sound->getProperty(kModelPropName)) && !(sound->getProperty(kSoundObjectsPropName)))
            *score = *score+1;
            return(this);
    }

    return(0);
}

IOReturn AppleOWScreamerAudio::performPowerStateChange(IOAudioDevicePowerState oldPowerState,
                                                        IOAudioDevicePowerState newPowerState,
                                                        UInt32 *microsecondsUntilComplete)
{
    IOReturn result;
    
    result = super::performPowerStateChange(oldPowerState, newPowerState, microsecondsUntilComplete);
    
    if (result == kIOReturnSuccess) {
        if (oldPowerState == kIOAudioDeviceSleep) {
            result = performDeviceWake();
        } else if (newPowerState == kIOAudioDeviceSleep) {
            result = performDeviceSleep();
        }
    }
    
    return result;
}


IOReturn AppleOWScreamerAudio::performDeviceSleep(){
    IOReturn result = kIOReturnSuccess;
    
    return(result);
}

IOReturn AppleOWScreamerAudio::performDeviceWake()
{
    IOReturn result;

	writeCodecControlReg(  ioBase, codecControlRegister[0] );
	writeCodecControlReg(  ioBase, codecControlRegister[1] );
	writeCodecControlReg(  ioBase, codecControlRegister[2] );
	writeCodecControlReg(  ioBase, codecControlRegister[4] );
	if ( awacsVersion > kAWACSMaxVersion ) {
		writeCodecControlReg(  ioBase, codecControlRegister[5] );
		writeCodecControlReg(  ioBase, codecControlRegister[6] );
	}

	recalibrate();
    
	result = kIOReturnSuccess;

    return result;
}

bool AppleOWScreamerAudio::createPorts(IOAudioEngine *driverDMAEngine)
{
    IOAudioPort *outputPort = 0;
    IOAudioPort *inputPort = 0;
    IOAudioPort *passThru = 0;

    CLOG("+ AppleOWScreamerAudio::createPorts\n");
    if (!driverDMAEngine) {
        return false;
    }

    /*
     * Create out part port : 2 level (obne for each side and one mute)
     */

    outputPort = IOAudioPort::withAttributes(kIOAudioPortTypeOutput, "Output port");
    if (!outputPort) {
        return false;
    }

	// [2731278] Create output selector that is used to tell the HAL what the current output is (speaker, headphone, etc.)
	outputSelector = IOAudioSelectorControl::createOutputSelector ('ispk', kIOAudioControlChannelIDAll);
	if (NULL != outputSelector) {
		driverDMAEngine->addDefaultAudioControl(outputSelector);
		// No value change handler because this is a read only control
		outputSelector->addAvailableSelection(kIOAudioOutputPortSubTypeInternalSpeaker, "Internal speaker");
		outputSelector->addAvailableSelection(kIOAudioOutputPortSubTypeHeadphones, "Headphones");
		// Don't release it because we might use it later.
	}
	// end [2731278]

    outVolLeft = IOAudioLevelControl::createVolumeControl(32,0,32,
                                          (-22 << 16) + (32768), /* -22.5 in fixed point 16.16 */
                                          0,
                                          kIOAudioControlChannelIDDefaultLeft,
                                          kIOAudioControlChannelNameLeft,
                                          kOutVolLeft, 
                                          kIOAudioControlUsageOutput);
    if (!outVolLeft) {
        return false;
    }

    driverDMAEngine->addDefaultAudioControl(outVolLeft);
    outVolLeft->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)volumeLeftChangeHandler, this);

    outVolRight = IOAudioLevelControl::createVolumeControl(32,0,32,
                                          (-22 << 16) + (32768),
                                          0,
                                          kIOAudioControlChannelIDDefaultRight,
                                          kIOAudioControlChannelNameRight,
                                          kOutVolRight, 
                                          kIOAudioControlUsageOutput);
    if (!outVolRight) {
        return false;
    }


    driverDMAEngine->addDefaultAudioControl(outVolRight);
    outVolRight->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)volumeRightChangeHandler, this);

	// Create a toggle control for reporting the status of the headphone jack
	headphoneConnection = IOAudioToggleControl::create (FALSE,
										kIOAudioControlChannelIDAll,
										kIOAudioControlChannelNameAll,
										kHeadphoneInsert,
										kIOAudioControlTypeJack,
										kIOAudioControlUsageOutput);

	if (NULL != headphoneConnection) {
		driverDMAEngine->addDefaultAudioControl (headphoneConnection);
		// no value change handler because this isn't a settable control
		// Don't release it because we might reference it later
	}

    outMute = IOAudioToggleControl::createMuteControl(false,
                                      kIOAudioControlChannelIDAll,
                                      kIOAudioControlChannelNameAll,
                                      kOutMute, 
                                      kIOAudioControlUsageOutput);
    if (!outMute) {
        return false;
    }

    driverDMAEngine->addDefaultAudioControl(outMute);
    outMute->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)outputMuteChangeHandler, this);

    /*
     * Create  input port : 2 level controls associated to it
     */

    inputPort = IOAudioPort::withAttributes(kIOAudioPortTypeInput, "Input Port");
    if (!inputPort) {
        return false;
    }

    inGainLeft = IOAudioLevelControl::createVolumeControl(65535,
                                          0,
                                          65535,
                                          0,
                                          (22 << 16) + (32768),	/* 22.5 in fixed point - 16.16 */
                                          kIOAudioControlChannelIDDefaultLeft,
                                          kIOAudioControlChannelNameLeft,
                                          kInGainLeft, 
                                          kIOAudioControlUsageInput);
    if (!inGainLeft) {
        return false;
    }

    driverDMAEngine->addDefaultAudioControl(inGainLeft);
    inGainLeft->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)gainLeftChangeHandler, this);
    
    inGainRight = IOAudioLevelControl::createVolumeControl(65535,
                                          0,
                                          65535,
                                          0,
                                          (22 << 16) + (32768),	/* 22.5 in fixed point - 16.16 */
                                          kIOAudioControlChannelIDDefaultRight,
                                          kIOAudioControlChannelNameRight,
                                          kInGainRight, 
                                          kIOAudioControlUsageInput);
    if (!inGainRight) {
        return false;
    }
    driverDMAEngine->addDefaultAudioControl(inGainRight);
    inGainRight->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)gainRightChangeHandler, this);
     
	// Create a jack control to let the HAL/Sound Manager know that some input source is plugged in
	inputConnection = IOAudioToggleControl::create (FALSE,
										kIOAudioControlChannelIDAll,
										kIOAudioControlChannelNameAll,
										kInputInsert,
										kIOAudioControlTypeJack,
										kIOAudioControlUsageInput);

	if (NULL != inputConnection) {
		driverDMAEngine->addDefaultAudioControl (inputConnection);
		// no value change handler because this isn't a settable control
		// Don't release it because we might reference it later
	}

    /*
     * Build passthru port
     */

    passThru = IOAudioPort::withAttributes(kIOAudioPortTypePassThru, "PassThru");
    if (!passThru) {
        return false;
    }

        
    playthruToggle = IOAudioToggleControl::createMuteControl(true,
                                         kIOAudioControlChannelIDAll,
                                         kIOAudioControlChannelNameAll,
                                         kPassThruToggle, 
                                         kIOAudioControlUsagePassThru);
    if (!playthruToggle) {
        return false;
    }
	// passThru->addAudioControl(playthruToggle);
      
    attachAudioPort(outputPort, driverDMAEngine, 0);
    attachAudioPort(inputPort, 0, driverDMAEngine);
    attachAudioPort(passThru, inputPort, outputPort);


    checkStatus(true);

    inputPort->release();
    outputPort->release();
    passThru->release();
    
    CLOG("- AppleOWScreamerAudio::createPorts\n");
    return true;
}

void AppleOWScreamerAudio::checkStatus(bool force)
{
	UInt32				newCodecStatus;
	UInt32				InsenseStatus;
	bool				OmicPlugged;
	bool				OheadPlugged;
	bool				micPlugged;
	bool				headPlugged;
	OSNumber *					activeOutput;

    newCodecStatus = readCodecStatusReg (ioBase);

	// get the old status In fact this stuff is pretty rydimentary
    InsenseStatus = codecStatus & 0x0000000F;
    if (duringInitialization) {
        OmicPlugged = false;
        OheadPlugged = false;
    } else {
        OmicPlugged = ((InsenseStatus & kSndHWInSense1) == kSndHWInSense1);
        OheadPlugged = ((InsenseStatus & kSndHWInSense2) == kSndHWInSense2);
    }

    if ((codecStatus != newCodecStatus) || force) {
        InsenseStatus = newCodecStatus & 0x0000000F;
        micPlugged = ((InsenseStatus & kSndHWInSense1) == kSndHWInSense1);
        headPlugged = ((InsenseStatus & kSndHWInSense2) == kSndHWInSense2);

		// change in the headphone
        if (headPlugged != OheadPlugged) {
            if (headPlugged) {
                curActiveSpkr = kHeadPhoneSpkr;  
                setToneHardwareMuteRear (false);		// Enable ext/rear speakers
                setToneHardwareMuteFront (true);		// Mute front
                setToneHardwareMuteBoomer (true);
            } else {
                curActiveSpkr = kCpuSpkr;
                setToneHardwareMuteBoomer (false);
                setToneHardwareMuteFront (false);	// Enable tone front
                setToneHardwareMuteRear (true);
            }

			// For [2926907]
			if (NULL != headphoneConnection) {
				OSNumber *			headphoneState;
	
				headphoneState = OSNumber::withNumber ((long long unsigned int)headPlugged, 32);
				(void)headphoneConnection->hardwareValueChanged (headphoneState);
			}
			// end [2926907]

			if (NULL != outputSelector) {
				if(headPlugged) {
					activeOutput = OSNumber::withNumber (kIOAudioOutputPortSubTypeHeadphones, 32);
					outputSelector->hardwareValueChanged (activeOutput);
				} else {
					activeOutput = OSNumber::withNumber (kIOAudioOutputPortSubTypeInternalSpeaker, 32);
					outputSelector->hardwareValueChanged (activeOutput);
				}
			}
        }
        
        if (micPlugged != OmicPlugged) {
            if (micPlugged) {
                sndHWSetActiveInputExclusive (kSndHWInput2);
            } else {
                sndHWSetActiveInputExclusive (kSndHWInputNone);
			}

			// For [2829546]
			if (NULL != inputConnection) {
				OSNumber *			inputState;
	
				inputState = OSNumber::withNumber ((long long unsigned int)micPlugged, 32);
				(void)inputConnection->hardwareValueChanged (inputState);
			}
			// end [2829546]
        }

        codecStatus = newCodecStatus;
    }
}

void AppleOWScreamerAudio::timerCallback(OSObject *target, IOAudioDevice *device)
{
    AppleOWScreamerAudio *screamer;

    screamer = OSDynamicCast(AppleOWScreamerAudio, target);
    if (screamer) {
        screamer->checkStatus(false);
    }
}


IOReturn AppleOWScreamerAudio::setModemSound(bool state){
    return(kIOReturnError);
}


IOReturn AppleOWScreamerAudio::callPlatformFunction( const OSSymbol * functionName,bool
            waitForFunction,void *param1, void *param2, void *param3, void *param4 ){
            
    if(functionName->isEqualTo("setModemSound")) {
        return(setModemSound((bool)param1));
    }        
    
    return(super::callPlatformFunction(functionName,
            waitForFunction,param1, param2, param3, param4));
       
}


IOReturn AppleOWScreamerAudio::setToneHardwareMuteRear(bool mute){
    IOReturn result = kIOReturnSuccess;
      
    unsigned char chipLeftReg, chipRightReg;

	// READ
    readsgs7433 ( kLRAttnReg, &chipLeftReg);
    readsgs7433 ( kRRAttnReg, &chipRightReg);
	
	// MODIFY
    if( mute ) {
        chipLeftReg |= sgsBalMuteBit;		// set bit 5
        chipRightReg |= sgsBalMuteBit;		// set bit 5
    } else {
        chipLeftReg &= ~sgsBalMuteBit;		// clear bit 5 only
        chipRightReg &= ~sgsBalMuteBit;
    }	
	// WRITE
    writesgs7433(  kLRAttnReg, chipLeftReg);
    writesgs7433(  kRRAttnReg, chipRightReg);
	
    return(result);
}

IOReturn AppleOWScreamerAudio::setToneHardwareMuteFront(bool mute){
    IOReturn result = kIOReturnSuccess;
        
    return(result);
}
    
IOReturn AppleOWScreamerAudio::setToneHardwareMuteBoomer(bool mute){
    IOReturn result = kIOReturnSuccess;
     
    UInt32	progOutputBits;

    progOutputBits = sndHWGetProgOutput();
	
    if ( mute )
        progOutputBits &= ~kSndHWProgOutput1;	// clear bit to mute, like power up
    else
        progOutputBits |=  kSndHWProgOutput1;	// set bit to enable, ie clear mute bit

    result=  sndHWSetProgOutput(progOutputBits);	// And write it out	
    return(result);
}
    
IOReturn AppleOWScreamerAudio::writesgs7433(UInt8 RegIndex, unsigned char RegValue )
{
    IOReturn result = kIOReturnSuccess;
    
    SGSShadowRegs[RegIndex]= RegValue;
    writeSGSRegs();
    
    return(result);
} 


IOReturn AppleOWScreamerAudio::readsgs7433( UInt8 RegIndex, unsigned char *RegValue)
{
     IOReturn result = kIOReturnSuccess;
     
    *RegValue= SGSShadowRegs[RegIndex];	// return the shadow reg
    return(result);
}


// A few sndHWLib functions
UInt32  AppleOWScreamerAudio::sndHWGetProgOutput(void ){
    return (sndHWGetRegister(kAWACsProgOutputReg) & kAWACsProgOutputField) >> kAWACsProgOutputShift;
}

IOReturn   AppleOWScreamerAudio::sndHWSetProgOutput(UInt32 outputBits){
    UInt32	progOutputReg;
    IOReturn result; 
	
    result = kIOReturnError;
    FAIL_IF((outputBits & (kSndHWProgOutput0 | kSndHWProgOutput1)) != outputBits, EXIT);
    
    result = kIOReturnSuccess;
    progOutputReg = sndHWGetRegister(kAWACsProgOutputReg) & ~kAWACsProgOutputField;
    progOutputReg |= ((outputBits << kAWACsProgOutputShift) & kAWACsProgOutputField);
    sndHWSetRegister(kAWACsProgOutputReg, progOutputReg);
	
EXIT:
    return result;
}


UInt32 AppleOWScreamerAudio::sndHWGetRegister(UInt32 regNum){
    return(codecControlRegister[regNum]);
}

IOReturn AppleOWScreamerAudio::sndHWSetRegister(UInt32 regNum, UInt32 value){
    IOReturn myReturn = kIOReturnSuccess;
    
    codecControlRegister[regNum] = value;
    writeCodecControlReg(ioBase, codecControlRegister[regNum]);
    return(myReturn);
}

IOReturn AppleOWScreamerAudio::sndHWSetActiveInputExclusive(UInt32 input ){
    
    IOReturn result = kIOReturnSuccess; 
    
    UInt32		inputReg, gainReg;
    UInt32		pcmciaReg;
    Boolean		needsRecalibrate;
	
    needsRecalibrate = (input != sndHWGetActiveInputExclusive());
	
	// start with all inputs off
    inputReg = sndHWGetRegister(kAWACsInputReg) & ~kAWACsInputField;
    pcmciaReg = sndHWGetRegister(kAWACsPCMCIAAttenReg) & ~kAWACsPCMCIAAttenField;
    	
    switch (input){
        case kSndHWInputNone:
            break;
        case kSndHWInput1:
            inputReg |= kAWACsInputA;	
            break;
        case kSndHWInput2:
            inputReg |= kAWACsInputB;
            break;
        case kSndHWInput3:
            inputReg |= kAWACsInputC;
            break;
        case kSndHWInput4:
            pcmciaReg |= kAWACsPCMCIAOn;
            needsRecalibrate = false;
            break;
        default:
            result = kIOReturnError;
            goto EXIT;
            break;
    }
	
	// this should disappear. We put the gain input to the max value
    
    gainReg = sndHWGetRegister(kAWACsGainReg) & ~kAWACsGainField;		// get and clear current gain setting

    gainReg |= ((kAWACsMaxHardwareGain << kAWACsGainLeftShift) & kAWACsGainLeft);
    gainReg |= (kAWACsMaxHardwareGain & kAWACsGainRight);
    sndHWSetRegister(kAWACsGainReg, gainReg);

    sndHWSetRegister(kAWACsInputReg, inputReg);
	
	// if (needsRecalibrate) 
		// GC_Recalibrate();
      
EXIT:
    return(result);
}

UInt32 	 AppleOWScreamerAudio::sndHWGetActiveInputExclusive(void){
    UInt32		input;
    UInt32		inputReg;
    UInt32		pcmciaReg;
		
    input = kSndHWInputNone;
	
    inputReg = sndHWGetRegister(kAWACsInputReg) & kAWACsInputField;
    pcmciaReg = sndHWGetRegister(kAWACsPCMCIAAttenReg) & kAWACsPCMCIAAttenField;

    switch (inputReg){
        case kAWACsInputA:
            input = kSndHWInput1;
            break;
        case kAWACsInputB:
            input = kSndHWInput2;
            break;
        case kAWACsInputC:
            input = kSndHWInput3;
            break;
        default:
            if (pcmciaReg == kAWACsPCMCIAOn)
                input = kSndHWInput4;
            else if (inputReg != 0)
                CLOG("Invalid input setting\n");
            break;
    }
	
    return (input);
}


    
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Calculates the PRAM volume value for stereo volume.
 UInt8 AppleOWScreamerAudio::VolumeToPRAMValue( UInt32 leftVol, UInt32 rightVol )
{ 
	UInt32			pramVolume;						// Volume level to store in PRAM
	UInt32 			averageVolume;					// summed volume
    const UInt32 	volumeRange = (kMaximumVolume - kMinimumVolume+1); // for now. Change to the uber class later
    UInt32 			volumeSteps;
    
	averageVolume = (leftVol + rightVol) >> 1;		// sum the channel volumes and get an average
    volumeSteps = volumeRange / kMaximumPRAMVolume;	// divide the range by the range of the pramVolume
    pramVolume = averageVolume / volumeSteps;
    
	// Since the volume level in PRAM is only worth three bits,
	// we round small values up to 1. This avoids SysBeep from
	// flashing the menu bar when it thinks sound is off and
	// should really just be very quiet.

	if ((pramVolume == 0) && (leftVol != 0 || rightVol !=0 ))
		pramVolume = 1;
		
	return (pramVolume & 0x07);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Writes the given unsignedfixed volume out to PRAM.
void AppleOWScreamerAudio::WritePRAMVol(  UInt32 leftVol, UInt32 rightVol  )
{
	UInt8				pramVolume;
	UInt8 				curPRAMVol;
	IODTPlatformExpert * 		platform = NULL;
		
	platform = OSDynamicCast(IODTPlatformExpert,getPlatform());

	if (platform)
	{
		pramVolume = VolumeToPRAMValue(leftVol,rightVol);
		
		// get the old value to compare it with
		platform->readXPRAM((IOByteCount)kPRamVolumeAddr,&curPRAMVol, (IOByteCount)1);
		
		
		// Update only if there is a change
		if (pramVolume != (curPRAMVol & 0x07))
		{
			// clear bottom 3 bits of volume control byte from PRAM low memory image
			curPRAMVol = (curPRAMVol & 0xF8) | pramVolume;
			
			// write out the volume control byte to PRAM
			platform->writeXPRAM((IOByteCount)kPRamVolumeAddr, &curPRAMVol,(IOByteCount) 1);
		}
	}
}

/****************************** Control Handler***********************************/
/*   These functions are needed to deal with the change of DMA attached control  */
/*   									         								 */
/*********************************************************************************/

IOReturn AppleOWScreamerAudio::volumeLeftChangeHandler(IOService *target, IOAudioControl *volumeControl, SInt32 oldValue, SInt32 newValue){
    IOReturn result = kIOReturnSuccess;
    AppleOWScreamerAudio *audioDevice;
    
    
    audioDevice = (AppleOWScreamerAudio *)target;
    if (audioDevice) { result = audioDevice->volumeLeftChanged(volumeControl, oldValue, newValue);}
    return result;
}

IOReturn AppleOWScreamerAudio::volumeLeftChanged(IOAudioControl *volumeControl, SInt32 oldValue, SInt32 newValue){
    IOReturn result = kIOReturnSuccess;
    SInt32 value;
        
    value = newValue;
    
    if ( iicAudioDevicePresent ) {
        setToneHardwareVolume(newValue, gVolRight);
    } else {
        value = 15 - ((value * 16 / 65536) & 15);
        codecControlRegister[4] = (codecControlRegister[4] & ~kLeftSpeakerAttenMask) |
                            (value << kLeftSpeakerAttenShift);
        writeCodecControlReg( ioBase, codecControlRegister[4] );
    }
    WritePRAMVol(gVolLeft,gVolRight);
    return(result);
}

IOReturn AppleOWScreamerAudio::volumeRightChangeHandler(IOService *target, IOAudioControl *volumeControl, SInt32 oldValue, SInt32 newValue){
    IOReturn result = kIOReturnSuccess;
    AppleOWScreamerAudio *audioDevice;
    
    audioDevice = (AppleOWScreamerAudio *)target;
    if (audioDevice) { result = audioDevice->volumeRightChanged(volumeControl, oldValue, newValue);}
    return result;
}

IOReturn AppleOWScreamerAudio::volumeRightChanged(IOAudioControl *volumeControl, SInt32 oldValue, SInt32 newValue){
    IOReturn result = kIOReturnSuccess;
    SInt32 value;
    value = newValue;
    
    if ( iicAudioDevicePresent ) {              
        setToneHardwareVolume( gVolLeft, newValue);
    } else {
        value = 15 - ((value * 16 / 65536) & 15);
        codecControlRegister[4] = (codecControlRegister[4] & ~kRightSpeakerAttenMask) |
                            (value << kRightSpeakerAttenShift);
        writeCodecControlReg( ioBase, codecControlRegister[4] );
    }
    WritePRAMVol(gVolLeft,gVolRight);
  
    return result;

}

IOReturn AppleOWScreamerAudio::outputMuteChangeHandler(IOService *target, IOAudioControl *muteControl, SInt32 oldValue, SInt32 newValue){
    IOReturn result = kIOReturnSuccess;
    AppleOWScreamerAudio *audioDevice;
    
    audioDevice = (AppleOWScreamerAudio *)target;
    if (audioDevice) { result = audioDevice->outputMuteChanged(muteControl, oldValue, newValue);}
    return result;
}

IOReturn AppleOWScreamerAudio::outputMuteChanged(IOAudioControl *muteControl, SInt32 oldValue, SInt32 newValue){
    IOReturn result = kIOReturnSuccess;
    SInt32 value;
    value = newValue;
	// pass it to the AudioHardwareOutputObjects
    if (iicAudioDevicePresent ) {
        unsigned char chipInReg;
		// READ
        readsgs7433 ( kInFuncReg, &chipInReg);
	
		// MODIFY
        if( value )					// accept any non 0 as audioMuted
            chipInReg |= SGSChipMuteBit;		// Mute sound - set mute bit		
        else
            chipInReg &= ~SGSChipMuteBit;		// Unmute it - clear mute bit

		// WRITE
        result= writesgs7433 ( kInFuncReg, chipInReg);
           
    } else {
        if (value) {
            codecControlRegister[1] |= kMuteInternalSpeaker;
        } else {
            codecControlRegister[1] &= ~kMuteInternalSpeaker;
        }
    }
    writeCodecControlReg( ioBase, codecControlRegister[1] );
                
	// write the PRAM stuff
    if (value)
        WritePRAMVol(0,0);
    else
        WritePRAMVol(gVolLeft,gVolRight);
        
    return result;
}

IOReturn AppleOWScreamerAudio::gainLeftChangeHandler(IOService *target, IOAudioControl *gainControl, SInt32 oldValue, SInt32 newValue){
    IOReturn result = kIOReturnSuccess;
    AppleOWScreamerAudio *audioDevice;
    
    audioDevice = (AppleOWScreamerAudio *)target;
    if (audioDevice) { result = audioDevice->gainLeftChanged(gainControl, oldValue, newValue);}
    return result;

}

IOReturn AppleOWScreamerAudio::gainLeftChanged(IOAudioControl *gainControl, SInt32 oldValue, SInt32 newValue){
    IOReturn result = kIOReturnSuccess;
    SInt32 value;
    
    value = (newValue * 16 / 65536) & 15;
    codecControlRegister[0] = (codecControlRegister[0] & ~kLeftInputGainMask) |
                        (value << kLeftInputGainShift);
    writeCodecControlReg( ioBase, codecControlRegister[0] );
            
    return result;
}

IOReturn AppleOWScreamerAudio::gainRightChangeHandler(IOService *target, IOAudioControl *gainControl, SInt32 oldValue, SInt32 newValue){
    IOReturn result = kIOReturnSuccess;
    AppleOWScreamerAudio *audioDevice;
    
    audioDevice = (AppleOWScreamerAudio *)target;
    if (audioDevice) { result = audioDevice->gainRightChanged(gainControl, oldValue, newValue);}
    return result;

}

IOReturn AppleOWScreamerAudio::gainRightChanged(IOAudioControl *gainControl, SInt32 oldValue, SInt32 newValue){
   IOReturn result = kIOReturnSuccess;
   SInt32 value;
   
   value = (newValue * 16 / 65536) & 15;
    codecControlRegister[0] = (codecControlRegister[0] & ~kRightInputGainMask) |
                        (value << kRightInputGainShift);
    writeCodecControlReg( ioBase, codecControlRegister[0] );
    return result;

}

IOReturn AppleOWScreamerAudio::passThruChangeHandler(IOService *target, IOAudioControl *passThruControl, SInt32 oldValue, SInt32 newValue){
    IOReturn result = kIOReturnSuccess;
    AppleOWScreamerAudio *audioDevice;
    
    audioDevice = (AppleOWScreamerAudio *)target;
    if (audioDevice) { result = audioDevice->passThruChanged(passThruControl, oldValue, newValue);}
    return result;

}

IOReturn AppleOWScreamerAudio::passThruChanged(IOAudioControl *passThruControl, SInt32 oldValue, SInt32 newValue){
    
    IOReturn result = kIOReturnSuccess;
    
    if(newValue)
        codecControlRegister[1] &= ~kLoopThruEnable;
    else
        codecControlRegister[1] |= kLoopThruEnable;
    writeCodecControlReg( ioBase, codecControlRegister[1] );
    return result;
}


IOReturn AppleOWScreamerAudio::setToneHardwareVolume(UInt32 volLeft, UInt32 volRight){
	// for now we are doing manipulation on each bit
    setToneHardwareBalance(volLeft, volRight);
    gVolLeft = volLeft;
    gVolRight = volRight;
    return(kIOReturnSuccess);
}

IOReturn AppleOWScreamerAudio::setToneHardwareBalance(UInt32 volLeft, UInt32 volRight){
    unsigned char attnl, attnr;
    unsigned char LFvalue, RFValue;

	// first deal with the left side
    if(volLeft != gVolLeft) {
        readsgs7433 ( kLFAttnReg, &LFvalue);
        if( 0 == volLeft) {
            LFvalue |= sgsBalMuteBit;		// 0 we mute
        } else {
            if(0 == gVolLeft) {				// we are leaving an unmute state
                LFvalue &= ~sgsBalMuteBit;
            }
            attnl = (unsigned char) (31-volLeft+1);
            LFvalue &=sgsBalMuteBit;
            LFvalue |= attnl;
        }
        writesgs7433( kLFAttnReg, LFvalue);
        writesgs7433( kLRAttnReg, LFvalue);
    }
	// now we deal with the right side
    if(volRight != gVolRight) {
        readsgs7433 ( kRFAttnReg, &RFValue);
        if( 0 == volRight) {
            RFValue|= sgsBalMuteBit;		// 0 we mute
        } else {
            if(0 == gVolRight)  {			// we are laeving an unmute state
                RFValue&= ~sgsBalMuteBit;	// 0 we mute
            }
            attnr = (unsigned char) (31-volRight+1);
            RFValue &=sgsBalMuteBit;
            RFValue |= attnr;
        }
        writesgs7433( kRFAttnReg, RFValue);
        writesgs7433( kRRAttnReg, RFValue);
    }
    return(kIOReturnSuccess);
}