AppleScreamerAudio.cpp   [plain text]


/*
 *  AudioScreamerAudio.cpp (definition)
 *  Project : AppleOnboardAudio
 *
 *  Copyright (c) 1998-2000 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, AN 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@
 *
 *  Hardware independent (relatively) code for the Awacs Controller
 *  NEW-WORLD MACHINE ONLY!!!
 *
 *  As of the release of OS X version 1, the AppleScreamerAudio driver has gone
 *  under major changes. These changes adress only NewWorld machines (the translucent
 *  ones).
 *  
 *  This work was undertaken in order to use the information present in the Open Firmware
 *  "sound" node, and that represent the hardware wiring. We can then avoid the multiple
 *  cases for different machines, and have a driver that autoconfigures itself. Another
 *  goal is to isolate the Codec related function, from the rest of the driver. A superclass
 *  for all Apple Hardware driver can then be extracted and reused when new hardware
 *  is coming. 
 *    
 *  For commodity, all functions have been defined inside the driver class. This can 
 *  obviously done in a better way, by grouping the functionnality and creating 
 *  appropriate objects.
 *  
 *  The list of hardware access functions is not restrictive. It is only sufficient
 *  enough to answer to the behavior asked by the UI guidelines of OS X version 1.
 *  As long as the hardware support it, it is our intention to different 
 *  UI policies, and have a wider flexibility. 
 *
 *  PCMCIA card support is not supported for now 
 */
 
#include "AudioHardwareCommon.h"
#include "AudioHardwareConstants.h"
#include "AudioHardwareUtilities.h"
#include "AudioHardwareDetect.h"
#include "AudioHardwareOutput.h"
#include "AudioHardwareInput.h"
#include "AudioHardwareMux.h"
#include "AudioHardwarePower.h"

#include "awacs_hw.h"
#include "AppleScreamerAudio.h"

#include "AppleDBDMAAudioDMAEngine.h"

static void		ScreamerWaitUntilReady (volatile awacs_regmap_t *ioBaseAwacs);
static void 	Screamer_writeCodecControlReg( volatile awacs_regmap_t *ioBaseAwacs, int value );
static void 	Screamer_writeSoundControlReg( volatile awacs_regmap_t *ioBaseAwacs, int value );
static UInt32   Screamer_ReadStatusRegisters( volatile awacs_regmap_t *ioBaseAwacs );
static int		Screamer_readCodecControlReg(volatile awacs_regmap_t *ioBaseAwacs, int regNum);

#define	LEFTATTEN(x)				((x & kAWACsOutputLeftAtten) >> kAWACsOutputLeftShift)
#define RIGHTATTEN(x)				(x & kAWACsOutputRightAtten)

// Not used at the moment, turned off just to kill warning

static void ScreamerWaitUntilReady (volatile awacs_regmap_t *ioBaseAwacs) {
	UInt32					codecStatus;
	UInt32					partReady;

	// Wait for the part to become ready before we talk to it
	codecStatus = Screamer_ReadStatusRegisters (ioBaseAwacs);
	partReady = codecStatus & 1 << 22;
	while (!partReady) {
		IOSleep (1);
		codecStatus = Screamer_ReadStatusRegisters (ioBaseAwacs);
		partReady = codecStatus & 1 << 22;
	}
}

static int Screamer_readCodecControlReg(volatile awacs_regmap_t *ioBaseAwacs, int regNum)
{
    UInt32 reg, result;
    
    reg = (((regNum << kReadBackRegisterShift) & kReadBackRegisterMask) | kReadBackEnable) & kCodecCtlDataMask;
    reg |= kCodecCtlAddress7;
    reg |= kCodecCtlEMSelect0;
    
    OSWriteLittleInt32(&ioBaseAwacs->CodecControlRegister, 0, reg);
    eieio();
    
    do {
        result = OSReadLittleInt32(&ioBaseAwacs->CodecControlRegister, 0);
        eieio();
    } while (result & kCodecCtlBusy);
    
    // We're going to do this twice to make sure the results are back before reading the status register
    // What a pain - there must be a better way
    OSWriteLittleInt32(&ioBaseAwacs->CodecControlRegister, 0, reg);
    eieio();
    
    do {
        result = OSReadLittleInt32(&ioBaseAwacs->CodecControlRegister, 0);
        eieio();
    } while (result & kCodecCtlBusy);
    
    result = (OSReadLittleInt32(&ioBaseAwacs->CodecStatusRegister, 0) >> 4) & kCodecCtlDataMask;
    
    return result;
}


static void Screamer_writeCodecControlReg( volatile awacs_regmap_t *ioBaseAwacs, int value )
{
	int          CodecControlReg;

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

	ScreamerWaitUntilReady (ioBaseAwacs);

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

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


static void Screamer_writeSoundControlReg( volatile awacs_regmap_t *ioBaseAwacs, int value )
{
	// DEBUG_IOLOG( "PPCSound(awacs): SoundControlReg = %08x\n", value);

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

static UInt32 Screamer_ReadStatusRegisters( volatile awacs_regmap_t *ioBaseAwacs )
{	
    // we need to have something that check if the Screamer is busy or in readback mode

	return OSReadLittleInt32( &ioBaseAwacs->CodecStatusRegister, 0 );
}

#define super AppleOnboardAudio

OSDefineMetaClassAndStructors(AppleScreamerAudio, AppleOnboardAudio)

// Unix like prototypes
bool AppleScreamerAudio::init(OSDictionary *properties)
{
    DEBUG_IOLOG("+ AppleScreamerAudio::init\n");
    if (!super::init(properties))
        return false;        
    chipInformation.awacsVersion = kAWACsAwacsRevision;
    
//    mVolLeft = 0;
//    mVolRight = 0;
//    mIsMute = false;
    mVolMuteActive = false;

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


void AppleScreamerAudio::free()
{
    DEBUG_IOLOG("+ AppleScreamerAudio::free\n");
	super::free();
    DEBUG_IOLOG("- AppleScreamerAudio::free\n");
}


IOService* AppleScreamerAudio::probe(IOService* provider, SInt32* score)
{
	// Finds the possible candidate for sound, to be used in
	// reading the caracteristics of this hardware:
    IORegistryEntry *sound = 0;
    DEBUG_IOLOG("+ AppleScreamerAudio::probe\n");
    
    super::probe(provider, score);
    *score = kIODefaultProbeScore;
    sound = provider->childFromPath("sound", gIODTPlane);
	// we are on a new world : the registry is assumed to be fixed
    if(sound) {
        OSData *tmpData;
        
        tmpData = OSDynamicCast(OSData, sound->getProperty(kModelPropName));
        if(tmpData) {
            if(tmpData->isEqualTo(kAWACsModelName, sizeof(kAWACsModelName) -1) ||
               tmpData->isEqualTo(kScreamerModelName, sizeof(kScreamerModelName) -1) ) {
                *score = *score+1;
                DEBUG_IOLOG("- AppleScreamerAudio::probe\n");
                return(this);
            } 
        } 
    } 
    DEBUG_IOLOG("- AppleScreamerAudio::probe\n");
    return (0);
}

bool AppleScreamerAudio::initHardware(IOService *provider)
{
    AbsoluteTime		timerInterval;
    bool myreturn = true;

    DEBUG_IOLOG("+ AppleScreamerAudio::initHardware\n");

    super::initHardware(provider);

	// Common information
    codecStatus &= ~kAllSense;
    gCanPollStatus = true;    
    checkStatus(true);
   
	// create and flush ports, controls
    
    // Use the current hardware settings as our defaults
   /* if (chipInformation.outputAActive) {
        int regValue;
        
        regValue = Screamer_readCodecControlReg(ioBase, kAWACsOutputAAttenReg);
        
        mVolLeft = (15 - ((regValue & kAWACsOutputLeftAtten) << kAWACsOutputLeftShift)) * 4096;
        mVolRight = (15 - (regValue & kAWACsOutputRightAtten)) * 4096;
        
        regValue = Screamer_readCodecControlReg(ioBase, kAWACsMuteReg);
        
        deviceMuted = (regValue & kAWACsMuteOutputA) == kAWACsMuteOutputA;
    } else if (chipInformation.outputCActive) {
        int regValue;
        
        regValue = Screamer_readCodecControlReg(ioBase, kAWACsOutputCAttenReg);
        
        mVolLeft = (15 - ((regValue & kAWACsOutputLeftAtten) << kAWACsOutputLeftShift)) * 4096;
        mVolRight = (15 - (regValue & kAWACsOutputRightAtten)) * 4096;
        
        regValue = Screamer_readCodecControlReg(ioBase, kAWACsMuteReg);
        
        deviceMuted = (regValue & kAWACsMuteOutputC) == kAWACsMuteOutputC;
    } else {
        deviceMuted = true;
    }
    
    if (outVolLeft) {
        outVolLeft->setValue(mVolLeft);
    }
    
    if (outVolRight) {
        outVolRight->setValue(mVolRight);
    }
    
    if (outMute) {
        outMute->setValue(deviceMuted ? 1 : 0);
    } */
    
    
	// flushAudioControls();
    
	// Prepare the timer loop --> should go on the workloop
    nanoseconds_to_absolutetime(NSEC_PER_SEC, &timerInterval);
    addTimerEvent(this, &AppleScreamerAudio::timerCallback, timerInterval);
    powerState = kIOAudioDeviceActive;
    DEBUG_IOLOG("- AppleScreamerAudio::initHardware\n");
    return myreturn;
}

void AppleScreamerAudio::sndHWPostDMAEngineInit (IOService *provider) {
	if (NULL != driverDMAEngine)
		driverDMAEngine->setSampleLatencies (kScreamerSampleLatency, kScreamerSampleLatency);
}

void AppleScreamerAudio::setDeviceDetectionActive(){
    gCanPollStatus = true;
}
    
void AppleScreamerAudio::setDeviceDetectionInActive(){
    gCanPollStatus = false;
}

// IOAudio subclasses
void AppleScreamerAudio::sndHWInitialize(IOService *provider)
{
    IOMemoryMap *map;
    
    DEBUG_IOLOG("+ AppleScreamerAudio::sndHWInitialize\n");
    map = provider->mapDeviceMemoryWithIndex(AppleDBDMAAudioDMAEngine::kDBDMADeviceIndex);
    
    ioBase = (awacs_regmap_t *)map->getVirtualAddress();
    
    codecStatus = Screamer_ReadStatusRegisters( ioBase );
            
	// fill the chip info
    chipInformation.partType = sndHWGetType();
    chipInformation.awacsVersion = (codecStatus & kAWACsRevisionNumberMask) >> kAWACsRevisionShift;
    chipInformation.preRecalDelay = kPreRecalDelayCrystal;      // assume Crystal for recalibrate (safest)
    chipInformation.rampDelay = kRampDelayNational;		// assume National for ramping (safest)
    
    soundControlRegister = ( kInSubFrame0 | kOutSubFrame0 | kHWRate44100);
    Screamer_writeSoundControlReg( ioBase, soundControlRegister);

    codecControlRegister[0] = kCodecCtlAddress0 | kCodecCtlEMSelect0;
    codecControlRegister[1] = kCodecCtlAddress1 | kCodecCtlEMSelect0;
    codecControlRegister[2] = kCodecCtlAddress2 | kCodecCtlEMSelect0;
    codecControlRegister[4] = kCodecCtlAddress4 | kCodecCtlEMSelect0;
    
    Screamer_writeCodecControlReg( ioBase, codecControlRegister[0] );
    Screamer_writeCodecControlReg( ioBase, codecControlRegister[1] );
    Screamer_writeCodecControlReg( ioBase, codecControlRegister[2] );
    Screamer_writeCodecControlReg( ioBase, codecControlRegister[4] );
    
    if ( chipInformation.awacsVersion > kAWACsAwacsRevision ) {
        codecControlRegister[5] = kCodecCtlAddress5 | kCodecCtlEMSelect0;
        Screamer_writeCodecControlReg(  ioBase, codecControlRegister[5] );
        codecControlRegister[6] = kCodecCtlAddress6 | kCodecCtlEMSelect0;
        Screamer_writeCodecControlReg(  ioBase, codecControlRegister[6] );
    }

    switch (sndHWGetManufacturer()){
        case kSndHWManfCrystal :
            chipInformation.preRecalDelay = kPreRecalDelayCrystal;
            chipInformation.rampDelay = kRampDelayCrystal;
            break;
        case kSndHWManfNational :
            chipInformation.preRecalDelay = kPreRecalDelayNational;
            chipInformation.rampDelay = kRampDelayNational;
            break;
        default :
            break;
    }
    
	// do the IO init

	// These line should go into the IO part

	// this means we assume an mic input. We should assume through the 
	// input objects
    codecControlRegister[0] |= (kUnusedInput | kDefaultMicGain);

	// we should add the Screamer info
    DEBUG_IOLOG("- AppleScreamerAudio::sndHWInitialize\n");
}

/****************************** Workloop functions ***************************/
/*   There is work to do there !!!!  					     				 */
/*   In fact we should put that on the work loop, and have a family method   */
/*   to add whatever we want on it (polling, interrupt based stuff...)       */
/*   There is also to improve into the communication with any input 	     */
/*   and output. Instead of calling the arrays, we should send notification  */
/*   trough the IOAudioJackControl inheritance of the AudioHardwareDetect,   */
/*   and have all ports, listen to these notification (maybe it would have   */
/*   easier in Objective-C... or the IOKit does it for us)		     		 */
/*****************************************************************************/

void AppleScreamerAudio::checkStatus(bool force) {

    UInt32 newCodecStatus, inSense, extdevices;
    AudioHardwareDetect *theDetect;
    OSArray *AudioDetects;

	// DEBUG_IOLOG("+ AppleScreamerAudio::checkStatus\n");        
    if(false == gCanPollStatus)
        return;
    
    newCodecStatus = Screamer_ReadStatusRegisters(ioBase);

    if (((codecStatus & kAWACsStatusInSenseMask) != (newCodecStatus &kAWACsStatusInSenseMask)) || force)   {
        UInt32 i;

        inSense = 0;
        codecStatus = newCodecStatus;
        extdevices = 0;
        inSense = sndHWGetInSenseBits();

        AudioDetects = getDetectArray();
        if(AudioDetects) {
            for(i = 0; i < AudioDetects->getCount(); i++) {
                theDetect = OSDynamicCast(AudioHardwareDetect, AudioDetects->getObject(i));
                if (theDetect) {
					extdevices |= theDetect->refreshDevices(inSense);
				}
            }
            setCurrentDevices(extdevices);
        } else {
            DEBUG_IOLOG("I didn't get the array\n");
        }
        
		}
	// DEBUG_IOLOG("- AppleScreamerAudio::checkStatus\n");
}


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

	// DEBUG_IOLOG("+ AppleScreamerAudio::timerCallback\n");
    screamer = OSDynamicCast(AppleScreamerAudio, target);
    if (screamer) 
        screamer->checkStatus(false);
	// DEBUG_IOLOG("- AppleScreamerAudio::timerCallback\n");
}




/*************************** sndHWXXXX functions******************************/
/*   These functions should be common to all Apple hardware drivers and be   */
/*   declared as virtual in the superclass. This is the only place where we  */
/*   should manipulate the hardware. Order is given by the UI policy system  */
/*   The set of function should be enought to implement the policy           */
/*****************************************************************************/

/************************** Hardware Register Manipulation ********************/

UInt32 AppleScreamerAudio::sndHWGetInSenseBits(){
    UInt32 newCodecStatus, status, inSense;
    
    inSense = 0;
    newCodecStatus = Screamer_ReadStatusRegisters(ioBase);
    newCodecStatus &= kAWACsStatusMask;
    status = newCodecStatus & kAWACsInSenseMask;
    
	// something is wacky with the order of the bytes
    if(status & kAWACsInSense0) 
        inSense |= kAWACsInSense3;
    if(status & kAWACsInSense1)
        inSense |= kAWACsInSense2;
    if(status & kAWACsInSense2)
        inSense |= kAWACsInSense1;
    if(status & kAWACsInSense3) 
        inSense |= kAWACsInSense0;

    return(inSense);
}

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

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

UInt32	AppleScreamerAudio::sndHWGetConnectedDevices(void) {
    return(currentDevices);
}

/************************** Manipulation of input and outputs ***********************/
/********(These functions are enough to implement the simple UI policy)**************/

UInt32 AppleScreamerAudio::sndHWGetActiveOutputExclusive(void){
    UInt32 result;
    
    if(!(chipInformation.outputAActive) && !(chipInformation.outputCActive))
        result = kSndHWOutputNone;
    else if(chipInformation.outputAActive)
        result = kSndHWOutput1;
    else
        result = kSndHWOutput2;

    return(result);
}
    
IOReturn   AppleScreamerAudio::sndHWSetActiveOutputExclusive(UInt32 outputPort ){
    IOReturn myReturn = kIOReturnSuccess;
    
    switch (outputPort){
        case kSndHWOutputNone:
            chipInformation.outputAActive = false;
            chipInformation.outputCActive = false;
            break;
        case kSndHWOutput1:
            DEBUG_IOLOG("+ output A is active \n");
            chipInformation.outputAActive = true;
            chipInformation.outputCActive = false;
            break;
        case kSndHWOutput2:
            DEBUG_IOLOG("+ output C is active \n");
            chipInformation.outputAActive = false;
            chipInformation.outputCActive = true;
            break;
        default:
            myReturn = false;
            break;
    }		
    sndHWSetSystemMute(sndHWGetSystemMute());
    return (myReturn);
}

UInt32 	 AppleScreamerAudio::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)
                DEBUG_IOLOG("Invalid input setting\n");
            break;
    }
	
    return (input);
}

IOReturn AppleScreamerAudio::sndHWSetActiveInputExclusive(UInt32 input ){
    IOReturn result = kIOReturnSuccess; 
    
    UInt32		inputReg;
    UInt32		pcmciaReg;
    Boolean		needsRecalibrate;
	
	// needsRecalibrate = (input != sndHWGetActiveInputExclusive());
	needsRecalibrate = FALSE;		// no need to recalibrate when switching to the modem input, this is the modem input on PowerBook G4
	// start with all inputs off
    inputReg = sndHWGetRegister(kAWACsInputReg) & ~kAWACsInputField;
    pcmciaReg = sndHWGetRegister(kAWACsPCMCIAAttenReg) & ~kAWACsPCMCIAAttenField;
    	
    switch (input){
        case kSndHWInputNone:
			needsRecalibrate = TRUE;		// Force a recalibration
            break;
        case kSndHWInput1:
            inputReg |= kAWACsInputA;	
            break;
        case kSndHWInput2:
            inputReg |= kAWACsInputB;
            break;
        case kSndHWInput3:
            inputReg |= kAWACsInputC;
            break;
        case kSndHWInput4:
            pcmciaReg |= kAWACsPCMCIAOn;
            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  AppleScreamerAudio::sndHWGetProgOutput(void ){
    return (sndHWGetRegister(kAWACsProgOutputReg) & kAWACsProgOutputField) >> kAWACsProgOutputShift;
}

IOReturn   AppleScreamerAudio::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;
}

/************************** Global (i.e all outputs) manipulation of mute and volume ***************/

bool   AppleScreamerAudio::sndHWGetSystemMute(void){
    return (gIsMute);
}
 
IOReturn   AppleScreamerAudio::sndHWSetSystemMute(bool mutestate){
    UInt32	muteReg;
    
//	IOLog ("mutestate = %d, gVolRight = %ld, gVolLeft = %ld\n", mutestate, gVolRight, gVolLeft);
    muteReg = sndHWGetRegister(kAWACsMuteReg) & ~kAWACsMuteField;
	if (mutestate || (gVolRight == gVolLeft && 0 == gVolRight)) {	
        muteReg |= (kAWACsMuteOutputA | kAWACsMuteOutputC);
		if (gVolRight == gVolLeft && 0 == gVolRight) {
			mVolMuteActive = TRUE;
		}
    } else {
		// Set the volume to the correct volume as it might have been changed while we were muted
		sndHWSetSystemVolume (gVolLeft, gVolRight);

        if (chipInformation.outputAActive) {
            muteReg &= ~kAWACsMuteOutputA;
        } else {
            muteReg |= kAWACsMuteOutputA;
        }
        if (chipInformation.outputCActive) {
            muteReg &= ~kAWACsMuteOutputC;
        } else {
            muteReg |= kAWACsMuteOutputC;
        }
    }
    sndHWSetRegister(kAWACsMuteReg, muteReg);
    return(kIOReturnSuccess);
}

IOReturn   AppleScreamerAudio::sndHWSetSystemVolume(UInt32 value){
    sndHWSetSystemVolume( value,  value);
    return kIOReturnSuccess;
}

bool AppleScreamerAudio::sndHWSetSystemVolume(UInt32 leftvalue, UInt32 rightvalue) {
	// This function is not very flexible. It sets the volume for 
	// each output port to the same level. This is obvioulsy not
	// very flexible, but we keep that for the UI Policy implementation

//    bool			hasChanged = false;
    UInt32			leftAttn;
	UInt32			rightAttn;

//	IOLog ("leftvalue = %ld, rightvalue = %ld, gVolRight = %ld, gVolLeft = %ld\n", leftvalue, rightvalue, gVolRight, gVolLeft);

//    if (((SInt32)leftvalue != gVolLeft)) {
        if (0 == leftvalue) {
			leftvalue = 1;
			leftMute = TRUE;
		} else {
			leftMute = FALSE;
		}
        leftvalue -= 1;
        leftAttn = 15 - leftvalue;
//		IOLog ("leftAttn = %ld, leftvalue = %ld \n", leftAttn, leftvalue);
		// we change the left value for each register
        codecControlRegister[kAWACsOutputAAttenReg] 
                = (codecControlRegister[kAWACsOutputAAttenReg] & ~kAWACsOutputLeftAtten) |
                            (leftAttn << kAWACsOutputLeftShift);
        codecControlRegister[kAWACsOutputCAttenReg] 
                = (codecControlRegister[kAWACsOutputCAttenReg] & ~kAWACsOutputLeftAtten) |
                            (leftAttn << kAWACsOutputLeftShift);
//        hasChanged = true;
//    }
    
//    if (((SInt32)rightvalue != gVolRight)) {
        if (0 == rightvalue) {
			rightvalue = 1;
			rightMute = TRUE;
		} else {
			rightMute = FALSE;
		}
        rightvalue -= 1;
        rightAttn = 15 - rightvalue;
//		IOLog ("rightAttn = %ld, rightvalue = %ld \n", rightAttn, rightvalue);
		// we change the right value for each register
        codecControlRegister[kAWACsOutputAAttenReg] 
                = (codecControlRegister[kAWACsOutputAAttenReg] & ~kAWACsOutputRightAtten) |
                            (rightAttn);
        codecControlRegister[kAWACsOutputCAttenReg] 
                = (codecControlRegister[kAWACsOutputCAttenReg] & ~kAWACsOutputRightAtten) |
                            (rightAttn);
//        hasChanged = true;
//    }
    
//    if (hasChanged) {
        Screamer_writeCodecControlReg ( ioBase, codecControlRegister[kAWACsOutputAAttenReg] );
        Screamer_writeCodecControlReg ( ioBase, codecControlRegister[kAWACsOutputCAttenReg] );
    
	//	if((rightvalue == leftvalue) && (0 == rightvalue)) {
		if (TRUE == leftMute && TRUE == rightMute) {
			mVolMuteActive = true;
//			IOLog ("setting system mute from volume handler\n");
			sndHWSetSystemMute(true);
		} else {
			if(mVolMuteActive) {
				mVolMuteActive = false;
//				IOLog ("turning off system mute from volume handler\n");
				sndHWSetSystemMute(false);
			}
		}
//    }

    return(true);
}


IOReturn AppleScreamerAudio::sndHWSetPlayThrough( bool playthroughState )
{
	UInt32	playthruReg;
	IOReturn result = kIOReturnSuccess; 

	playthruReg = sndHWGetRegister(kAWACsLoopThruReg) & ~kAWACsLoopThruEnable;
	if (playthroughState) {
		playthruReg |= kAWACsLoopThruEnable;
	}
	sndHWSetRegister(kAWACsLoopThruReg, playthruReg);

	return result;
}


/************************** Identification of the codec ************************/

UInt32 AppleScreamerAudio::sndHWGetType( void ) {
	UInt32		revision, info;
	UInt32		codecStatus;
	
        codecStatus = Screamer_ReadStatusRegisters( ioBase );
        revision = (codecStatus & kAWACsRevisionNumberMask) >> kAWACsRevisionShift;
        
	switch (revision) {
            case kAWACsAwacsRevision :
                info = kSndHWTypeAWACs;
                break;
            case kAWACsScreamerRevision :
                info = kSndHWTypeScreamer;
                break;
            default :
                info = kSndHWTypeUnknown;
                break;
	}		
        return info;
}

IOReturn   AppleScreamerAudio::sndHWSetPowerState(IOAudioDevicePowerState theState){

    if (chipInformation.partType == kSndHWTypeAWACs)
        setAWACsPowerState(theState);
    else
        setScreamerPowerState(theState);	
        
    return(kIOReturnSuccess);
}

UInt32 AppleScreamerAudio::sndHWGetManufacturer(void) {
    UInt32		codecStatus, info = 0;
    
    codecStatus = Screamer_ReadStatusRegisters( ioBase );
	
    switch (codecStatus & kAWACsManufacturerIDMask){
        case kAWACsManfCrystal:
            DEBUG_IOLOG("Crystal Manufacturer\n");
            info = kSndHWManfCrystal;
            break;
        case kAWACsManfNational:
            DEBUG_IOLOG("National Manufacturer\n");
            info = kSndHWManfNational;
            break;
        case kAWACsManfTI:
            DEBUG_IOLOG("TI Manufacturer\n");
            info = kSndHWManfTI;
            break;
        default:
            DEBUG_IOLOG("Unknown Manufacturer\n");
            info = kSndHWManfUnknown;
            break;
    }
    return info;
}

IOReturn AppleScreamerAudio::sndHWSetSystemInputGain(UInt32 leftGain, UInt32 rightGain){
    IOReturn myReturn = kIOReturnSuccess; 
    UInt32 gainReg;
    UInt8 galeft, garight;

    DEBUG3_IOLOG("+ AppleScreamerAudio::sndHWSetSystemInputGain (%ld, %ld)\n", leftGain, rightGain);
    galeft = (UInt8) leftGain;
    garight = (UInt8) rightGain;
	
    gainReg = sndHWGetRegister(kAWACsGainReg) & ~kAWACsGainField;		// get and clear current gain setting

    gainReg |= ((galeft << kAWACsGainLeftShift) & kAWACsGainLeft);
    gainReg |= (garight & kAWACsGainRight);
    sndHWSetRegister(kAWACsGainReg, gainReg);
    
    DEBUG_IOLOG("- AppleScreamerAudio::sndHWSetSystemInputGain\n");
    return(myReturn);
}

#pragma mark ++++++++ UTILITIES

/************************** Utilities for AWACS/Screamer only ************************/
/*
void AppleScreamerAudio::GC_Recalibrate()
{
    UInt32 awacsReg, saveReg;
    
    DEBUG_IOLOG("+ AppleScreamerAudio::GC_Recalibrate\n");

    IOSleep( kPreRecalDelayCrystal); //This recalibrate delay is a hack for some
                                     //broken Crystal parts
        
    awacsReg = sndHWGetRegister(kAWACsRecalibrateReg);
    saveReg = awacsReg;
    
    awacsReg |= (kMuteInternalSpeaker | kMuteHeadphone); //mute the outputs
    awacsReg |= kAWACsRecalibrate;			 //set the recalibrate bits
    
    sndHWSetRegister(kAWACsRecalibrateReg, awacsReg);
    IOSleep(1000);//kRecalibrateDelay*2);//There seems to be some confusion on the time we have to wait
                                         //No doc indicates it clearly. This value was indicated in 
                                         //the OS 9 code
    sndHWSetRegister(kAWACsRecalibrateReg, saveReg);
    DEBUG_IOLOG("- AppleScreamerAudio::GC_Recalibrate\n");
}
*/

void AppleScreamerAudio::GC_Recalibrate () {
	UInt32					awacsReg;
	UInt32					saveReg;

	debugIOLog ("+ AppleScreamerAudio::GC_Recalibrate\n");

	IOSleep (kPreRecalDelayCrystal); 		// This recalibrate delay is a hack for some broken Crystal parts

	awacsReg = sndHWGetRegister (kAWACsRecalibrateReg);
	saveReg = awacsReg;

	awacsReg |= (kMuteInternalSpeaker | kMuteHeadphone); // mute the outputs
	awacsReg |= kAWACsRecalibrate;			 // set the recalibrate bits

	sndHWSetRegister (kAWACsRecalibrateReg, awacsReg);

	ScreamerWaitUntilReady (ioBase);

	sndHWSetRegister (kAWACsRecalibrateReg, saveReg);
	debugIOLog ("- AppleScreamerAudio::GC_Recalibrate\n");
}

void AppleScreamerAudio::restoreSndHWRegisters( void )
{
        Screamer_writeCodecControlReg(  ioBase, codecControlRegister[0] );
        Screamer_writeCodecControlReg(  ioBase, codecControlRegister[1] );
        Screamer_writeCodecControlReg(  ioBase, codecControlRegister[2] );
        Screamer_writeCodecControlReg(  ioBase, codecControlRegister[4] );
        if ( chipInformation.awacsVersion > kAWACsAwacsRevision ) {
            Screamer_writeCodecControlReg(  ioBase, codecControlRegister[5] );
            Screamer_writeCodecControlReg(  ioBase, codecControlRegister[6] );
        }
}

void AppleScreamerAudio::InitializeShadowRegisters(void){
    UInt32 regNumber;
    
    switch(chipInformation.partType) {
        case kSndHWTypeScreamer:
            codecControlRegister[kMaxSndHWRegisters] = 0;
            for (regNumber = 0; regNumber < kMaxSndHWRegisters-1; regNumber++)
                codecControlRegister[regNumber] = Screamer_readCodecControlReg(ioBase,regNumber);
            break;
        case kSndHWTypeAWACs:
            for (regNumber = 0; regNumber < kMaxSndHWRegisters; regNumber++)
                    codecControlRegister[regNumber] = 0;
            break;
    }

}

void AppleScreamerAudio::setAWACsPowerState( IOAudioDevicePowerState state )
{
    switch (state){
        case kIOAudioDeviceActive : 
            restoreSndHWRegisters();
            GC_Recalibrate();
            break;
        case kIOAudioDeviceSleep :
            break;
        default:
            break;
    }
}

IOAudioDevicePowerState AppleScreamerAudio::SndHWGetPowerState( void )
{
	return powerState;							// return the cached copy, since part might not have power
}

void AppleScreamerAudio::setScreamerPowerState(IOAudioDevicePowerState state) {
	IOAudioDevicePowerState			curState;

	curState = SndHWGetPowerState ();

	debug3IOLog ("[AppleScreamerAudio] going to power state %d from %d\n", state, curState);
	switch (state) {
		case kIOAudioDeviceActive:
			GoRunState(curState);
			break;
		case kIOAudioDeviceIdle:
			break;
		case kIOAudioDeviceSleep:
			GoSleepState(curState);
			break;
		default:
			FailMessage("Invalid set power state");
			break;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Take the part from the curState into the run state.
// 	
// 	From SLEEP to RUN:
//		Indicate that recalibrate should actually happen next time
// 		Wait up to 583 ms for sndHW ready
// 		Restore complete contents of registers 0, 2, 4, and 5
// 		Restore register 1 except for mute bits
// 		Restore register 6 except for state bits
// 		Mute both outputs
// 		Set state bits to RUN
// 		Wait for 5 ms
// 		Wait up to 583 ms for sndHW ready
//		Recalibrate if necessary
// 		Restore DMA to previous state
// 		Enable port change interrupts
// 		Restore muting on both outputs
// 		Remember state is RUN
// 	
// 	From IDLE to RUN:
// 		Mute both outputs
// 		Set state bits to RUN
// 		Wait for 5 ms
// 		Wait up to 583 ms for sndHW ready
//		Recalibrate if necessary
// 		Enable port change interrupts
// 		Restore muting on both outputs
// 		Remember state is RUN
// 	
// 	From DOZE to RUN:
// 		Mute both outputs
// 		Set state bits to RUN
// 		Wait for 5 ms
// 		Wait up to 583 ms for sndHW ready
// 		Restore muting on both outputs
// 		Remember state is RUN
void AppleScreamerAudio::GoRunState( IOAudioDevicePowerState curState )
{
	Boolean						detectsActive;

	switch (curState) {
		case kIOAudioDeviceSleep :							// Sleep -> Run
			recalibrateNecessary = true;					// after powering-up, need to recalibrate
		case kIOAudioDeviceIdle : 							// Idle -> Run
			restoreSndHWRegisters ();
			SetStateBits (kScreamerRunState1, kIdleRunDelay);// Clear both idle and doze bits
			GC_Recalibrate ();								// recalibrate if necessary
			detectsActive = gCanPollStatus;
			gCanPollStatus = TRUE;
			checkStatus (TRUE);
			sndHWSetSystemMute (sndHWGetSystemMute());					// restore muting from run state
			gCanPollStatus = detectsActive;
			break;
		case kIOAudioDeviceActive :							// Run -> Run
			break;
		default :
			FailMessage("Invalid curState in GoRunState");
			break;
	}
	powerState = kIOAudioDeviceActive;						// Set state global to run
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Take the part from the curState into the idle state.
// 	
// 	From SLEEP to IDLE:
//		Indicate that recalibrate should actually happen next time
// 		Wait up to 583 ms for sndHW ready
// 		Restore complete contents of registers 0, 2, 4, and 5
// 		Restore register 1 except for mute bits
// 		Restore register 6 except for state bits
// 		Remember state is IDLE
// 	
// 	From DOZE to IDLE:
// 		Disable port change interrupts
// 		Mute both outputs
// 		Set state bits to IDLE
// 		Wait for 5 ms
// 		Wait up to 583 ms for sndHW ready
// 		Remember state is IDLE
// 	
// 	From RUN to IDLE:
// 		Disable port change interrupts
// 		Mute both outputs
// 		Set state bits to IDLE
// 		Wait for 5 ms
// 		Wait up to 583 ms for sndHW ready
// 		Remember state is IDLE
void AppleScreamerAudio::GoIdleState( IOAudioDevicePowerState curState )
{
	switch (curState) {
		case kIOAudioDeviceSleep :							// Sleep -> Idle
			recalibrateNecessary = true;					// after powering-up, need to recalibrate
		case kIOAudioDeviceIdle :							// Idle -> Idle
			break;
		case kIOAudioDeviceActive :							// Run -> Idle
			SetStateBits(kScreamerIdleState, kIdleRunDelay);// Set idle, clear doze
			break;
		default :
			FailMessage("Invalid curState in GoIdleState");
			break;
	}
	powerState = kIOAudioDeviceIdle;						// Set state global to idle
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Take the part from the curState into the sleep state.
//	I know this can be "optimized" by utilizing the case statements falling
//	through. It is more clear and readable the way it is, please leave it.
//
// 	From IDLE to SLEEP:
// 		Disable port change interrupts
//		Do necessary steps to power down part quietly (currently nothing)
// 		Remember state is SLEEP
// 	
// 	From DOZE to SLEEP:
// 		Mute both outputs
// 		Set state bits to IDLE
// 		Wait for 5 ms
// 		Wait up to 583 ms for sndHW ready
// 		Disable port change interrupts
//		Do necessary steps to power down part quietly (currently nothing)
// 		Remember state is SLEEP
// 	
// 	From RUN to SLEEP:
// 		Save current DMA state for restore on wake
// 		Mute both outputs
// 		Set state bits to IDLE
// 		Wait for 5 ms
// 		Wait up to 583 ms for sndHW ready
// 		Disable port change interrupts
//		Do necessary steps to power down part quietly (currently nothing)
// 		Remember state is SLEEP
void AppleScreamerAudio::GoSleepState( IOAudioDevicePowerState curState )
{
	switch (curState) {
		case kIOAudioDeviceSleep :							// Sleep -> Sleep
			break;
		case kIOAudioDeviceIdle : 							// Idle -> Sleep
			break;
		case kIOAudioDeviceActive :							// Run -> Sleep
			SetStateBits(kScreamerIdleState, kIdleRunDelay);// Set idle, clear doze
			setCurrentDevices(0);
			break;
		default :
			FailMessage("Invalid curState in GoSleepState");
			break;
	}
	powerState = kIOAudioDeviceSleep;						// Set state global to sleep
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Mute all outputs and set the state bits. At the end, restore the global mute
//	state to the proper user-selected state.
void AppleScreamerAudio::SetStateBits( UInt32 stateBits, UInt32 delay )
{
	UInt32		tempReg;
//	Boolean		localMuted;

	FailMessage((stateBits & kScreamerStateField) != stateBits);

//	localMuted = sndHWGetSystemMute();							// we'll want to restore this after muting
	sndHWSetSystemMute(true);									// Mute all outputs

	tempReg = sndHWGetRegister(kAWACsPowerReg);					// Get current state
	tempReg &= ~kScreamerStateField;							// Clear Idle and Doze bits
	tempReg |= (stateBits & kScreamerStateField);				// Or in the state bits

	IOSleep (delay);
	sndHWSetRegister (kAWACsPowerReg, tempReg);

//	gIsMute = localMuted;										// restore actual mute state

	return;
}