Source to iokit/Drivers/audio/drvPPCDACA/PPCDACA.cpp


Enter a symbol's name here to quickly find it.

/*
 * 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, 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@
 */
/*
 * Copyright (c) 1998 Apple Computer, Inc.  All rights reserved. 
 *
 * Interface implementation for the DAC 3550A audio Controller
 *
 * HISTORY
 *
 *
 */

#include <IOKit/assert.h>
#include <IOKit/system.h>

#include <IOKit/IOLib.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/ppc/IODBDMA.h>

// Driver headers
#include "daca_hw.h"
#include "PPCDACA_inlined.h"
#include "PPCDACA.h"

#define super IOAudioBus
OSDefineMetaClassAndStructors( PPCDACA, IOAudioBus )

/* ==============
 * Public Methods
 * ============== */
bool
PPCDACA::init(OSDictionary * properties)
{
    if (!super::init(properties))
            return false;
 
    // Nulls the pointers for the arrays we allocate:
    fOutputComponents = NULL;
    numOutputComponents = NULL;

    // Initialize  the defualt registers to non zero values (so they will be revitten)
    sampleRateReg = 0xFF;
    analogVolumeReg = kPowerOnDefaultAVOL;
    configurationReg = 0xFF;

    // Mirror the analogVolumeReg for the speaker and for the headphones.
    internalSpeakerVolume = analogVolumeReg;
    internalHeadphonesVolume = analogVolumeReg;

    // Last value of the status is 0
    lastStatus = 0;
 
    // Clears the interface
    interface = NULL;
    
    // Forget the provider:
    sound = NULL;

    // Initialize the sound format:
    dacaSerialFormat = kSndIOFormatUnknown;

    return true;
}

void
PPCDACA::free()
{
    // Releases the sound:
    if(sound != NULL)
        sound->release();
    sound = NULL;

    super::free();
}

IOService*
PPCDACA::probe(IOService* provider, SInt32* score)
{
    // Finds the possible candidate for sound, to be used in
    // reading the caracteristics of this hardware:
    IORegistryEntry *soundCandidate = provider->childFromPath("sound", gIODTPlane);

    if (super::probe(provider, score) && implementsDACA(soundCandidate)) {
#ifdef DEBUGMODE
        IOLog("PPCDACA::probe we are on a PPCDACA device !!\n");
#endif // DEBUGMODE

        // we did not fail:
        *score = kIODefaultProbeScore;
        sound = soundCandidate;
        return this;
    }

    return (NULL);
}

#define kNumDMAStreams 1
bool
PPCDACA::start(IOService* provider)
{
    // Gets the base for the DAC-3550 registers:
    IOMemoryMap *map;
    int i;

#ifdef DEBUGMODE
    // Delay for 30 seconds so we have the time to attach the debugger and look at
    // what is going on here (only if DEBUGMODE of course).
    IOLog("Starting DAC-3550 with provider %s ...", provider->getName());
    IOSleep(30 * 1000);
    IOLog("...\n");
#endif // DEBUGMODE

    if(!super::start(provider)) {
#ifdef DEBUGMODE
        IOLog("PPCDACA::start if(!super::start(provider)) fails !!\n");
#endif // DEBUGMODE
        return (false);
    }

    if (!findAndAttachI2C(provider))  {
#ifdef DEBUGMODE
        IOLog("PPCDACA::start if (!findAndAttachI2C(IOService *provider)) fails !!\n");
#endif // DEBUGMODE
        return (false);
    }

    map = provider->mapDeviceMemoryWithIndex(kAudioDMAdeviceInt);
    if(!map)  {
#ifdef DEBUGMODE
        IOLog("PPCDACA::start if(!map) %d fails !!\n",kAudioDMAdeviceInt);
#endif // DEBUGMODE
        return (false);
    }
    soundConfigSpace = (UInt8 *)map->getPhysicalAddress();

    if (!AllocateStreams(kNumDMAStreams)) {
#ifdef DEBUGMODE
        IOLog("PPCDACA::start if (!AllocateStreams(kNumDMAStreams)) fails !!\n");
#endif // DEBUGMODE
        return (false);
    }

    map = provider->mapDeviceMemoryWithIndex(kAudioDMAtxInt);
    if(!map)  {
#ifdef DEBUGMODE
        IOLog("PPCDACA::start if(!map) %d fails !!\n",kAudioDMAtxInt);
#endif // DEBUGMODE
        return (false);
    }
    DefineStream(kAudioDMAOutputStream, kOutput, frameRate(0), (IODBDMAChannelRegisters*)map->getVirtualAddress());

    // Creates the array of output components
    numOutputComponents = numberOfInputComponents();

    fOutputComponents = (IOAudioComponentImplPtr*)IOMalloc(sizeof(IOAudioComponentImplPtr) * numOutputComponents);
    if (fOutputComponents == NULL) {
#ifdef DEBUGMODE
        IOLog("PPCDACA::start if (fOutputComponents == NULL) fails !!\n");
#endif // DEBUGMODE

        // Free the DMA and exit:
        FreeStreams();
        return false;
    }

    // Initalizes all the components:
    for (i = 0; i < numOutputComponents; i++)
        fOutputComponents[i] = NULL;

    // 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 = 0;
    }
    else if ((((UInt32)soundConfigSpace ^ kI2S1BaseOffset) & 0x0001FFFF) == 0) {
        ioBaseAddress = (void *)((UInt32)soundConfigSpace - kI2S1BaseOffset);
        i2SInterfaceNumber = 1;
    }
    else {
        IOLog("PPCDACA::start failed to setup ioBaseAddress and ioClockBaseAddress\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);
 
    // Starts the workloop and exit since there is nothing we can be interested about anymore
    startWorkLoop();

    return true;
}

void
PPCDACA::stop(IOService *provider)
{
    // Releases all DMA streams:
    FreeStreams();

    // Releases the output components
    if (fOutputComponents != NULL) {
        IOFree(fOutputComponents, sizeof(IOAudioComponentImplPtr) * numOutputComponents);
        fOutputComponents = NULL;
        numOutputComponents = 0;
    }

    // Call the parent stop:
    super::stop(provider);
}

void
PPCDACA::CreateAudioTopology(IOCommandQueue *queue)
{
    IOAudioComponentImpl *mixerOut;
    IOAudioComponentImpl *modem;
    IOAudioComponentImpl *headphones;
    IOAudioComponentImpl *cd;
    IOAudioComponentImpl *speaker;
    IOAudioComponentImpl *passthru;

    AudioStreamIndex outputStream;
    
    if (!fStreams)
        return;

    outputStream = firstStreamAfter(kOutput, 0);

    if (outputStream == kInvalidStreamIndex) {
        IOLog("PPCDACA::CreateAudioTopology called without available DMA streams\n");
        return;
    }
    else
        IOLog("PPCDACA::CreateAudioTopology output stream is %d\n", outputStream);

    mixerOut = buildComponentAndAttach(GetStream(outputStream), NULL, componentDictionaryMixerOut(), queue);
    headphones = buildComponentAndAttach(mixerOut, NULL, componentDictionaryHeadphones(), queue);
    speaker = buildComponentAndAttach(mixerOut, NULL, componentDictionarySpeaker(), queue);

    cd = buildComponentAndAttach(NULL, NULL,  componentDictionaryCD(), queue);
    modem = buildComponentAndAttach(NULL, NULL, componentDictionaryModem(), queue);

    passthru = buildComponentAndAttach(NULL, mixerOut, componentDictionaryPassThru(), queue);

    // FIXME: the component connection could be done better than so
    if (fOutputComponents != NULL) {
        fOutputComponents[kSpeaker] = speaker;

        if (numOutputComponents > 1)
            fOutputComponents[kHeadphones] = headphones;
    }

    // Sets ups the DAC-3550 as required by this machine wiring
    // configuration:
    if (!dependentSetup())
        IOLog("PPCDACA::CreateAudioTopology DAC-3550 setup failed\n");
}

void
PPCDACA::DoClockTick(IOTimerEventSource *t)
{
    checkStatusRegister();
    super::DoClockTick(t);
}

void
PPCDACA::calculateTickInterval(AbsoluteTime *tickInterval)
{
    AbsoluteTime maxInterval;

    // We want to check the status at least one a second
    nanoseconds_to_absolutetime(NSEC_PER_SEC, &maxInterval);
    if(CMP_ABSOLUTETIME(&maxInterval, tickInterval) < 0) {
        *tickInterval = maxInterval;
    }
    super::calculateTickInterval(tickInterval);
}

IOReturn
PPCDACA::SetControl(UInt16 id, int val)
{
    IOReturn res = kIOReturnSuccess;
    
    switch(id) {
        case kSpeakerMute:
            muteInternalSpeaker(val != 0);
            muteHeadphones(val == 0);
            break;

        case kHeadphonesMute:
            muteHeadphones(val != 0);
            muteInternalSpeaker(val == 0);
            break;

        case kModemMute:
            setModemInput(val == 0);
            break;

        case kSpeakerVolLeft:
            volumeInternalSpeakerLeft(val);
            break;

        case kSpeakerVolRight:
            volumeInternalSpeakerRight(val);
            break;

        case kHeadphonesVolLeft:
            volumeHeadphonesLeft(val);
            break;

        case kHeadphonesVolRight:
            volumeHeadphonesRight(val);
            break;

        case kCDMute:
            muteCDLine(val != 0);
            break;

        case kPassThruVolLeft:
            break;

        case kPassThruVolRight:
            break;

        case kPassThruMute:
            break;

        default:
            res = kIOReturnUnsupported;
    }
    return res;
}

/* ===============
 * Private Methods
 * =============== */

// --------------------------------------------------------------------------
// Method: findAndAttachI2C
//
// Purpose:
//   Attaches to the i2c interface:
bool
PPCDACA::findAndAttachI2C(IOService *provider)
{
    OSData *t;
    UInt32 baseAddress;
    UInt32 addressSteps;
    UInt32 rate;
    IORegistryEntry *i2cCandidate;

    // Searches the i2c:
    for (i2cCandidate = NULL;
         (provider != NULL) && (i2cCandidate == NULL);
         provider = provider->getProvider()) {
        i2cCandidate = provider->childFromPath("i2c", gIODTPlane);
#ifdef DEBUGMODE
        IOLog("Looking for i2c in %s -> 0x%08lx\n", provider->getName(), (UInt32)i2cCandidate);
#endif // DEBUGMODE
    }

    if (i2cCandidate == NULL) {
#ifdef DEBUGMODE
        IOLog("PPCDACA::findAndAttachI2C can't find the i2c in the registry\n");
#endif // DEBUGMODE
        return false;
    }

    // creates the interface for real:
    interface = new PPCI2CInterface;
    if (interface == NULL) {
#ifdef DEBUGMODE
        IOLog("PPCDACA::findAndAttachI2C can't allocate memory for the PPCI2CInterface\n");
#endif // DEBUGMODE
        return false;
    }

    // sets up the interface:
    t = OSDynamicCast(OSData, i2cCandidate->getProperty("AAPL,address"));
    if (t != NULL)
        baseAddress = *((UInt32*)t->getBytesNoCopy());
    else {
#ifdef DEBUGMODE
        IOLog( "PPCDACA::findAndAttachI2C missing property AAPL,address in i2c registry\n");
#endif
        return false;
    }

    t = OSDynamicCast(OSData, i2cCandidate->getProperty("AAPL,address-step"));
    if (t != NULL)
        addressSteps = *((UInt32*)t->getBytesNoCopy());
    else {
#ifdef DEBUGMODE
        IOLog( "PPCDACA::findAndAttachI2C missing property AAPL,address-step in i2c registry\n");
#endif
        return false;
    }

    t = OSDynamicCast(OSData, i2cCandidate->getProperty("AAPL,i2c-rate"));
    if (t != NULL)
        rate = *((UInt32*)t->getBytesNoCopy());
    else {
#ifdef DEBUGMODE
        IOLog( "PPCDACA::findAndAttachI2C missing property AAPL,i2c-rate in i2c registry\n");
#endif
        return false;
    }

    if (!interface->initI2CBus((UInt8*)baseAddress, (UInt8)addressSteps))
        return false;

    if (!interface->setKhzSpeed((UInt)rate))
        return false;

    // Also sets the default mode
    interface->setStandardSubMode();
  
    // Finds the port to use:
    return(interface->openI2CBus(getI2CPort()));
}

// --------------------------------------------------------------------------
// Method: detachFromI2C
//
// Purpose:
//   detaches from the I2C
bool
PPCDACA::detachFromI2C(IOService* /*provider*/)
{
    if (interface)
        delete interface;

    return (true);
}

// --------------------------------------------------------------------------
// Method: implementsDACA
//
// Purpose:
//   Attempts to discover if the device implements DAC-3550:
bool
PPCDACA::implementsDACA(IORegistryEntry *device)
{
    if (device != NULL) {
        OSData *s = NULL;

#ifdef DEBUGMODE
        IOLog("Matching DAC-3550 compatibility with %s\n", device->getName());
#endif // DEBUGMODE
        s = OSDynamicCast(OSData, device->getProperty("compatible"));

        if (s != NULL) {
            if(s->isEqualTo("daca", sizeof("daca")-1)) {
                return true;
            }
            else {
#ifdef DEBUGMODE
                IOLog("PPCDAC-3550::probe sound is compatible with %s.\n", (char*)s->getBytesNoCopy(0, sizeof("daca")-1));
#endif // DEBUGMODE
            }
        }
        else {
#ifdef DEBUGMODE
            IOLog("PPCDAC-3550::probe sound does not have a compatible property.\n");
#endif // DEBUGMODE
        }
    }
    else{
#ifdef DEBUGMODE
        IOLog("PPCDAC-3550::probe this hardware does not have a sound chip\n");
#endif // DEBUGMODE
    }
    return false;
}

// --------------------------------------------------------------------------
// Method: numberOfInputComponents
//
// Purpose:
//        returns the number of components for the input lines
//        the name registry describes the CHIP (which has 3 inputs
//	  however, the iBook has no inputs, so I have to force 0
//        as output).
#define kCommonNumberOfInputComponents 0

int
PPCDACA::numberOfInputComponents()
{
    return kCommonNumberOfInputComponents;
}

// --------------------------------------------------------------------------
// Method: numberOfOutputComponents
//
// Purpose:
//        returns the number of components for the output lines
//        usually are 2 one is the internal speacker and the other
//        is the headphone line.
#define kCommonNumberOfOutputComponents 2

int
PPCDACA::numberOfOutputComponents()
{
    if(sound) {
        OSData *t;
        
        t = OSDynamicCast(OSData, sound->getProperty("#-outputs"));
        if (t != NULL) {
            return *(UInt32*)(t->getBytesNoCopy());
        }
    }
    return kCommonNumberOfOutputComponents;
}


// --------------------------------------------------------------------------
// Method: frameRate
//
// Purpose:
//        returns the frame rate as in the registry, if it is
//        not found in the registry, it returns the default value.
#define kCommonFrameRate 44100

UInt32
PPCDACA::frameRate(UInt32 index)
{
    if(sound) {
        OSData *t;

        t = OSDynamicCast(OSData, sound->getProperty("sample-rates"));
        if (t != NULL) {
            UInt32 *fArray = (UInt32*)(t->getBytesNoCopy());

            if ((fArray != NULL) && (index < fArray[0])){
                // I could do >> 16, but in this way the code is portable and
                // however any decent compiler will recognize this as a shift
                // and do the right thing.
                UInt32 fR = fArray[index + 1] / (UInt32)65536;

#ifdef DEBUGMODE
                IOLog( "PPCDACA::frameRate (%ld)\n",  fR);
#endif
                return fR;
            }
        }
    }

    return (UInt32)kCommonFrameRate;
}

// --------------------------------------------------------------------------
// Method: getI2CPort
//
// Purpose:
//        returns the i2c port to use for the audio chip.
UInt32
PPCDACA::getI2CPort()
{
    if(sound) {
        OSData *t;

        t = OSDynamicCast(OSData, sound->getProperty("AAPL,i2c-port-select"));
        if (t != NULL) {
            UInt32 myPort = *((UInt32*)t->getBytesNoCopy());
            return myPort;
        }
#ifdef DEBUGMODE
        else
            IOLog( "PPCDACA::getI2CPort missing property port\n");
#endif
    }
    
    return 0;
}

// --------------------------------------------------------------------------
// Method(s): componentDictionaryXXXXXX
//
// Purpose:
//        the next N methods return the strings for the component dictionaries
//        for each kind of component. As stated in the AWACS driver, these should
//        end in the driver resources.

// NOTE: (This is important !!!) the soundControls enum in the header file is
//       bounded to the following stings in this way:
//       for each component the control Id in the string must have the same value
//       of the corrispondent soundControls enum item. For example the sound control
//       enum item kHeadphonesVolLeft has value 4, so in sHeadphones the ID of the
//       volume left control must be 4.

char*
PPCDACA::componentDictionarySpeaker()
{
    static const char *sSpeaker =
    "{
    'Type' = 'Speaker';
    'Channels' = 2:8;
    'Master' = 1:8;
    'Controls' = {
        'MuteAll' = {
            'Type' = 'Mute';
            'Id' = 0:16;
            'Val' = 0:8;
            'Min' = 0:8;
            'Max' = 1:8;
            'Chan' = 0:8;
        };
        'VolumeLeft' = {
            'Type' = 'Volume';
            'Id' = 1:16;
            'Val' = 65535:16;
            'Min' = 0:16;
            'Max' = 65535:16;
            'Chan' = 1:8;
        };
        'VolumeRight' = {
            'Type' = 'Volume';
            'Id' = 2:16;
            'Val' = 65535:16;
            'Min' = 0:16;
            'Max' = 65535:16;
            'Chan' = 2:8;
        };
    };
    }";
    return (char*)sSpeaker;
}

char*
PPCDACA::componentDictionaryHeadphones()
{
    static const char *sHeadphones =
    "{
    'Type' = 'Headphones';
    'Channels' = 2:8;
    'Master' = 1:8;
    'Controls' = {
        'MuteAll' = {
            'Type' = 'Mute';
            'Id' = 3:16;
            'Val' = 0:8;
            'Min' = 0:8;
            'Max' = 1:8;
            'Chan' = 0:8;
        };
        'VolumeLeft' = {
            'Type' = 'Volume';
            'Id' = 4:16;
            'Val' = 65535:16;
            'Min' = 0:16;
            'Max' = 65535:16;
            'Chan' = 1:8;
        };
        'VolumeRight' = {
            'Type' = 'Volume';
            'Id' = 5:16;
            'Val' = 65535:16;
            'Min' = 0:16;
            'Max' = 65535:16;
            'Chan' = 2:8;
        };
    };
    'Inputs' = {
        'Jack' = {
            'Val' = 0:8;
            'Min' = 0:8;
            'Max' = 1:8;
        };
    };
    }";
    return (char*)sHeadphones;
}

char*
PPCDACA::componentDictionaryModem()
{
    static const char *sModem =
    "{
    'Type' = 'Modem';
    'Channels' = 2:8;
    'Controls' = {
        'MuteAll' = {
            'Type' = 'Mute';
            'Id' = 6:16;
            'Val' = 0:8;
            'Min' = 0:8;
            'Max' = 1:8;
            'Chan' = 0:8;
        };
    };
    'Inputs' = {
        'Jack' = {
            'Val' = 0:8;
            'Min' = 0:8;
            'Max' = 1:8;
        };
    };
    }";
    return (char*)sModem;
}

char*
PPCDACA::componentDictionaryCD()
{
    static const char *sCD =
    "{
    'Type' = 'CD';
    'Channels' = 2:8;
    'Controls' = {
        'MuteAll' = {
            'Type' = 'Mute';
            'Id' = 7:16;
            'Val' = 0:8;
            'Min' = 0:8;
            'Max' = 1:8;
            'Chan' = 0:8;
        };
    };
    }";
    return (char*)sCD;
}

char*
PPCDACA::componentDictionaryMixerOut()
{
    static const char *sMixerOut =
    "{
    'Type' = 'Mixer';
    'Channels' = 2:8;
    }";
    return (char*)sMixerOut;
}

char*
PPCDACA::componentDictionaryPassThru()
{
    static const char *sPassThru =
    "{
    'Type' = 'Feature';
    'Channels' = 2:8;
    'Controls' = {
        'MuteAll' = {
            'Type' = 'Mute';
            'Id' = 12:16;
            'Val' = 1:8;
            'Min' = 0:8;
            'Max' = 1:8;
            'Chan' = 0:8;
        };
        'VolumeLeft' = {
            'Type' = 'Volume';
            'Id' = 10:16;
            'Val' = 65535:16;
            'Min' = 0:16;
            'Max' = 65535:16;
            'Chan' = 1:8;
        };
        'VolumeRight' = {
            'Type' = 'Volume';
            'Id' = 11:16;
            'Val' = 65535:16;
            'Min' = 0:16;
            'Max' = 65535:16;
            'Chan' = 2:8;
        };
    };
    }";
    return (char*)sPassThru;
}

// --------------------------------------------------------------------------
// Method: dependentSetup
//
// Purpose:
//        this handles the setup of the DAC-3550 chip for each kind of
//        hosting hardware.
bool
PPCDACA::dependentSetup()
{
    // Sets the frame rate:
    UInt32 myFrameRate = frameRate(0);

    dacaSerialFormat = kSndIOFormatI2SSony;			// start out in Sony format

    // 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 (interface != NULL) {
        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));
    }

    // 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)) {
        IOLog("PPCDACA::dependentSetup can not set i2s sample rate\n");
        return false;
    }
    else if (!setDACASampleRate(myFrameRate)) {
        IOLog("PPCDACA::dependentSetup can not set DACA sample rate\n");
        return false;
    }
    else 
        setSerialFormatRegister(dacaClockSource, dacaMclkDivisor, dacaSclkDivisor, dacaSerialFormat);

    // Sets the molumes to max as in the audio registry:
    volumeInternalSpeakerLeft(65535);
    volumeInternalSpeakerRight(65535);
    volumeHeadphonesLeft(65535);
    volumeHeadphonesRight(65535);
    volumeCDLineLeft(65535);
    volumeCDLineRight(65535);

    return true;
}

// --------------------------------------------------------------------------
// Method: checkStatusRegister
//
// Purpose:
//        if the argument is true mutes the internal speaker, otherwise
//        it "unmutes" it.
void
PPCDACA::checkStatusRegister()
{
    if (statusChanged())  {
        int i;

        OSNumber * num = OSNumber::withNumber(lastStatus, 8);
        setProperty("Status", num);
        num->release();

#ifdef DEBUGMODE
        IOLog("New Status = 0x%02x\n", lastStatus);
#endif

        for (i = 0; i < numOutputComponents; i++)
            if (fOutputComponents[i] != NULL)
                fOutputComponents[i]->Set(gInputsSym, gJackSym, outputComponentStatus(i));
    }
}

// --------------------------------------------------------------------------
// Method: outputComponentStatus
//
// Purpose:
//        checks the status of the jack line of the ith output component:
bool
PPCDACA::outputComponentStatus(int index)
{
    switch(index)
    {
        case kSpeaker:
            return false;	// the internal speaker does not have a jack

        case kHeadphones:	// check the headphones:
            return headphonesInserted();
    }

    return false;	// by deafult there is no jack
}

// --------------------------------------------------------------------------
// Method: muteInternalSpeaker
//
// Purpose:
//        if the argument is true mutes the internal speaker, otherwise
//        it "unmutes" it.
void
PPCDACA::muteInternalSpeaker(bool mute)
{
    if (mute){
        if (!headphonesInserted())
            writeRegisterBits(i2cBusSubAddrAVOL, 0, kRightAVOLMask | kLeftAVOLMask);
        internalSpeakerMuted = mute;
    }
    else {
        internalSpeakerMuted = mute;

        if (!headphonesInserted())
            writeRegisterBits(i2cBusSubAddrAVOL, internalSpeakerVolume, kRightAVOLMask | kLeftAVOLMask);
    }
}

// --------------------------------------------------------------------------
// Method: muteHeadphones
//
// Purpose:
//        if the argument is true mutes the rear panel mini jack, otherwise
//        it "unmutes" it.
void
PPCDACA::muteHeadphones(bool mute)
{
    if (mute){
        if (headphonesInserted())
            writeRegisterBits(i2cBusSubAddrAVOL, 0, kRightAVOLMask | kLeftAVOLMask);
        internalHeadphonesMuted = mute;       
    }
    else {
        internalHeadphonesMuted = mute;        

        if (headphonesInserted())
            writeRegisterBits(i2cBusSubAddrAVOL, internalHeadphonesVolume, kRightAVOLMask | kLeftAVOLMask);
    }
}

// --------------------------------------------------------------------------
// Method: muteCDLine
//
// Purpose:
//        if the argument is true mutes the line of the cd player
void
PPCDACA::muteCDLine(bool mute)
{
    // FIXME: Figure out how this is wired up:
}

// --------------------------------------------------------------------------
// Method: setModemInput
//
// Purpose:
//        selects the modem as input source (if bool = true)
void
PPCDACA::setModemInput(bool mod)
{
#ifdef DEBUGMODE
    IOLog( "PPCDACA::setModemInput: %s\n", (mod ? "True" : "False"));
#endif
}


// --------------------------------------------------------------------------
// Method: statusChanged()
//
// Purpose:
//        returns true if something changed in the status register:.
bool
PPCDACA::statusChanged()
{
    UInt8 currentStatusRegister = *(UInt8*)ioStatusRegister_GPIO12;

    if (lastStatus == currentStatusRegister)
        return (false);

    // Otherwise refresh the value of the last status register read and
    // returns that the value changed:
    lastStatus = currentStatusRegister;
    return (true);
}

// --------------------------------------------------------------------------
// Method: headphonesInserted
//
// Purpose:
//        returns true if the headphones are inserted.
bool
PPCDACA::headphonesInserted()
{
    return((lastStatus & kHeadphoneBit) == 0);
}

// --------------------------------------------------------------------------
// Method: volumeHeadphonesLeft & volumeHeadphonesRight
//
// Purpose:
//        sets the volume for the left and right channel of the internal
//        headphones.
void
PPCDACA::volumeHeadphonesLeft(int vol)
{
    analogVolumeReg = internalHeadphonesVolume;
    setDACAVolume(vol, true);
    internalHeadphonesVolume = analogVolumeReg;
}

void
PPCDACA::volumeHeadphonesRight(int vol)
{
    analogVolumeReg = internalHeadphonesVolume;
    setDACAVolume(vol, false);
    internalHeadphonesVolume = analogVolumeReg;
}

// --------------------------------------------------------------------------
// Method: volumeInternalSpeakerLeft & volumeInternalSpeakerRight
//
// Purpose:
//        sets the volume for the left and right channel of the internal
//        speaker.
void
PPCDACA::volumeInternalSpeakerLeft(int vol)
{
    analogVolumeReg = internalSpeakerVolume;
    setDACAVolume(vol, true);
    internalSpeakerVolume = analogVolumeReg;
}

void
PPCDACA::volumeInternalSpeakerRight(int vol)
{
    analogVolumeReg = internalSpeakerVolume;
    setDACAVolume(vol, false);
    internalSpeakerVolume = analogVolumeReg;
}

// --------------------------------------------------------------------------
// Method: volumeCDLineLeft & volumeCDLineRight
//
// Purpose:
//        sets the volume for the left and right channel of the CD
//        line.
void
PPCDACA::volumeCDLineLeft(int vol)
{
}

void
PPCDACA::volumeCDLineRight(int vol)
{
}

/* =============================================================
 * VERY Private Methods used to access to the DAC-3550 registers
 * ============================================================= */

// --------------------------------------------------------------------------
// Method: enableInterrupts
//
// Purpose:
//         enable the interrupts for the I2S interface:
bool
PPCDACA::enableInterrupts()
{
    I2SSetIntCtlReg(kFrameCountEnable | kClocksStoppedEnable);
    return true;
}

// Method: disableInterrupts
//
// Purpose:
//         disable the interrupts for the I2S interface:
bool
PPCDACA::disableInterrupts()
{
    I2SSetIntCtlReg(I2SGetIntCtlReg() & (~(kFrameCountEnable | kClocksStoppedEnable)));
    return true;
}

// --------------------------------------------------------------------------
// Method: clockRun
//
// Purpose:
//         starts and stops the clock count:
bool
PPCDACA::clockRun(bool start)
{
    bool success = true;
    UInt32 *baseInterrupt = (UInt32*)soundConfigSpace + kI2SIntCtlOffset;

#ifdef DEBUGMODE
    IOLog("PPCDACA::clockRun(%s) 1] 0x%08lx -> 0x%08lx\n", (start ? "true" : "false"), (UInt32)baseInterrupt, *baseInterrupt);
#endif

    if (start) {
        KLSetRegister(ioClockBaseAddress, KLGetRegister(ioClockBaseAddress) | kI2S0ClockEnable);
    }
    else {
        UInt16 loop = 50;
        KLSetRegister(ioClockBaseAddress, KLGetRegister(ioClockBaseAddress) & (~kI2S0ClockEnable));
        
        while (((I2SGetIntCtlReg() & kClocksStoppedPending) == 0) && (loop--)) {
            // it does not do anything, jut waites for the clock
            // to stop
            IOSleep(10);
#ifdef DEBUGMODE
            IOLog("PPCDACA::clockRun(%s) 2] 0x%08lx -> 0x%08lx\n", (start ? "true" : "false"), (UInt32)baseInterrupt, *baseInterrupt);
#endif
        }

        // we are successful if the clock actually stopped.
        success =  ((I2SGetIntCtlReg() & kClocksStoppedPending) != 0);
    }

#ifdef DEBUGMODE
    if (!success)
        IOLog("PPCDACA::clockRun(%s) failed\n", (start ? "true" : "false"));
#endif

    return success;
}

// --------------------------------------------------------------------------
// Method: setSampleRate
//
// Purpose:
//        Sets the sample rate on the I2S bus
bool
PPCDACA::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
        dacaClockSource = kClock45MHz;
    }
    else if ((kClock49MHz % reqMClkRate) == 0) {
        // last, try 49 Mhz clock
        dacaClockSource = kClock49MHz;
    }
    else {
        IOLog("PPCDACA::setSampleParameters Unable to find a suitable source clock (no globals changes take effect)\n");
        return false;
    }

    // get the MClk divisor
    IOLog("PPCDACA:setSampleParameters %ld / %ld =", (UInt32)dacaClockSource, (UInt32)reqMClkRate); 
    dacaMclkDivisor = dacaClockSource / reqMClkRate;
    IOLog("%ld\n", dacaMclkDivisor);
    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:
            IOLog("PPCDACA::setSampleParameters Invalid serial format\n");
            return false;
            break;
    }

    return true;
 }


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

#ifdef DEBUGMODE
    IOLog("PPCDACA::SetSerialFormatRegister(%d,%d,%d,%d)\n",(int)clockSource, (int)mclkDivisor, (int)sclkDivisor, (int)serialFormat);
#endif // DEBUGMODE

    switch ((int)clockSource)
    {
        case kClock18MHz:
            regValue = kClockSource18MHz;
            break;
        case kClock45MHz:
            regValue = kClockSource45MHz;
            break;
        case kClock49MHz:
            regValue = kClockSource49MHz;
            break;
        default:
            IOLog("PPCDACA::SetSerialFormatRegister(%d,%d,%d,%d): Invalid clock source\n",(int)clockSource, (int)mclkDivisor, (int)sclkDivisor, (int)serialFormat);
            break;
    }

    switch (mclkDivisor)
    {
        case 1:
            regValue |= kMClkDivisor1;
            break;
        case 3:
            regValue |= kMClkDivisor3;
            break;
        case 5:
            regValue |= kMClkDivisor5;
            break;
        default:
            regValue |= (((mclkDivisor / 2) - 1) << kMClkDivisorShift) & kMClkDivisorMask;
            break;
    }

    switch ((int)sclkDivisor)
    {
        case 1:
            regValue |= kSClkDivisor1;
            break;
        case 3:
            regValue |= kSClkDivisor3;
            break;
        default:
            regValue |= (((sclkDivisor / 2) - 1) << kSClkDivisorShift) & kSClkDivisorMask;
            break;
    }
    regValue |= kSClkMaster;										// force master mode

    switch (serialFormat)
    {
        case kSndIOFormatI2SSony:
            regValue |= kSerialFormatSony;
            break;
        case kSndIOFormatI2S64x:
            regValue |= kSerialFormat64x;
            break;
        case kSndIOFormatI2S32x:
            regValue |= kSerialFormat32x;
            break;
        default:
            IOLog("PPCDACA::SetSerialFormatRegister(%d,%d,%d,%d): Invalid serial format\n",(int)clockSource, (int)mclkDivisor, (int)sclkDivisor, (int)serialFormat);
            break;
    }

    // This is a 3 step process:

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

    // 2] Setup the serial format register
    I2SSetSerialFormatReg(regValue);

    // 3 restarts the clock:
    clockRun(true);    
}

// --------------------------------------------------------------------------
// Method: setDACASampleRate
//
// Purpose:
//        Gets the sample rate and makes it in a format that is compatible
//        with the adac register. The funtion returs false if it fails.
bool
PPCDACA::setDACASampleRate(UInt rate)
{
    UInt32 dacRate = 0;

#ifdef DEBUGMODE
    IOLog("PPCDACA::setDACASampleRate(%d)\n", rate);
#endif // DEBUGMODE

        switch (rate)
        {
                case 44100: 				// 32 kHz - 48 kHz
                        dacRate = kSRC_48SR_REG;
                        break;

                default:
                IOLog("PPCDACA::setDACASampleRate, supports only 44100 Hz (for now)\n");
                        break;
        }

        return(writeRegisterBits(i2cBusSubAddrSR_REG, dacRate, kSampleRateControlMask));
}


// --------------------------------------------------------------------------
// Method: setDACAVolume
//
// Purpose:
//        sets the volume for the left or right channel. The first argument is
//        the wnated volume (as a range from 0 to 255), while the second argument
//        is a boolean (true for the right channel, false for the left channel).
bool
PPCDACA::setDACAVolume(UInt16 volume, bool isLeft)
{
    UInt8 newVolume = ((UInt32)((UInt32)volume * (UInt32)kVolumeRangeLevel_VOL) / (UInt32)65535 + (UInt32)kMinVolumeLevel_VOL);
    bool success = true;

    if (isLeft) // Sets the volume on the right channel
        success = writeRegisterBits(i2cBusSubAddrAVOL, (newVolume << kLeftAVOLShift), kLeftAVOLMask);
    else	// Sets the volume on the left channel
        success = writeRegisterBits(i2cBusSubAddrAVOL, (newVolume << kRightAVOLShift), kRightAVOLMask);

    return success;
}

// ---------------------------------------------------------------------------------------
// Method: writeRegisterBits
//
// Purpose:
//      This function sets or clears bits in the Daca registers.  The first argument is the
//      register sub-address, the second argument is a mask for turning bits on, while the third
//      argument is a mask for turning bits off.
bool
PPCDACA::writeRegisterBits(UInt8 subAddress, UInt32 bitMaskOn, UInt32 bitMaskOff)
{
    UInt8 bitsOn = 0, bitsOff = 0, value;
    UInt16 shortBitsOn = 0, shortBitsOff = 0, value16;
    bool success = false;

#ifdef DEBUGMODE
    IOLog("PPCDACA::writeRegisterBits(0x%x, 0x%08lx, 0x%08lx)\n", (UInt16)subAddress, bitMaskOn, bitMaskOff);
#endif // DEBUGMODE

    // mask off irrelevant bytes
    if (subAddress == i2cBusSubAddrAVOL) {
        // 16-bit register
        shortBitsOn = bitMaskOn & 0x0000FFFF;
        shortBitsOff = bitMaskOff & 0x0000FFFF;
    }
    else {
        // 8-bit registers
        bitsOn = bitMaskOn & 0x000000FF;
        bitsOff = bitMaskOff & 0x000000FF;
    }

    switch (subAddress)
    {
        case i2cBusSubAddrSR_REG:
            value = sampleRateReg | bitsOn;
            value &= ~bitsOff;
            // continue only if on bits are not already on and off bits are not already off
            if (value != sampleRateReg) {
                success = interface->writeI2CBus((UInt8)i2cBusAddrDAC3550A, subAddress, &value, sizeof(value));

                if (success)
                    sampleRateReg = value;
            }
                else {
                    // If the sample rate is already set at the wanted value
                    // we do not do anything, but we return success since from the
                    // caller point of view it is as the sample rate was correctly
                    // set.
                    success = true;
                }
                break;

        case i2cBusSubAddrAVOL:
            value16 = analogVolumeReg;
            value16 &= ~shortBitsOff;
            value16 |= shortBitsOn;
            // continue only if on bits are not already on and off bits are not already off
            if (value16 !=analogVolumeReg)
            {
                // It changes the volume for real only if the current output is not muted.
                if (((!headphonesInserted()) && (!internalSpeakerMuted)) ||
                    (headphonesInserted() && (!internalHeadphonesMuted)))
                    success = interface->writeI2CBus((UInt8)i2cBusAddrDAC3550A, subAddress, (UInt8*)&value16, sizeof(value16));
                else
                    success = true;

                if (success)
                    analogVolumeReg = value16;
            }
                else {
                    // If the volume is already set at the wanted value
                    // we do not do anything, but we return success since from the
                    // caller point of view it is as the volume was correctly
                    // set.
                    success = true;
                }
                break;

        case i2cBusSubaddrGCFG:
            value = configurationReg | bitsOn;
            value &= ~bitsOff;
            // continue only if on bits are not already on and off bits are not already off
            if (value != configurationReg)
            {
                success = interface->writeI2CBus((UInt8)i2cBusAddrDAC3550A, subAddress, &value, sizeof(value));
                if (success)
                    configurationReg = value;
            }
                else {
                    // If the config register is already set at the wanted value
                    // we do not do anything, but we return success since from the
                    // caller point of view it is as the configuration was correctly
                    // set.
                    success = true;
                }
                break;

        default:
            IOLog("PPCDACA::writeRegisterBits 0x%x unknown subaddress\n", (UInt16)subAddress);
            break;
    }
    return success;
}