/*
 * 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@
 */
 
#include <libkern/c++/OSContainers.h>
#include <IOKit/system.h>
#include <IOKit/IOLib.h>
#include <IOKit/graphics/IODisplay.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/ndrvsupport/IONDRVFramebuffer.h>
#include <IOKit/assert.h>

#include "IVAD.h"


static void RestoreXPRAM( IODisplay * display, OSDictionary * params );
static void SaveXPRAM( OSDictionary * params );

static IOReturn
ApplePMUSendMiscCommand( UInt32 command,
                        IOByteCount sendLength, UInt8 * sendBuffer,
                        IOByteCount * readLength, UInt8 * readBuffer );

IOReturn
AppleCudaWriteIIC( UInt8 address, const UInt8 * buffer, IOByteCount * count );
IOReturn
AppleCudaReadIIC( UInt8 address, UInt8 * buffer, IOByteCount * count );

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

class AppleIVAD : public IODisplayParameterHandler
{
    OSDeclareDefaultStructors(AppleIVAD);

private:
    OSDictionary *	fDisplayParams;
    IOFramebuffer *	fFramebuffer;
    IONotifier *	fNotifier;
    UInt32 		fIICAddress;
    IORegistryEntry * 	fIVAD;
    const OSSymbol *	fIVADPrefsKey;
    EEPROM *		fEEPROM;
    IVADUserPrefs *	fUserPrefs;
    UInt32		fModeIndex;
    bool		fUsesXPRAM;
    bool		fTheatreMode;

public:
    virtual bool start(	IOService * provider );

    virtual bool setDisplay( IODisplay * display );
    virtual bool doIntegerSet( OSDictionary * params,
                               const OSSymbol * paramName, UInt32 value );
    virtual bool doDataSet( const OSSymbol * paramName, OSData * value );
    virtual bool doUpdate( void );
    virtual IOReturn framebufferEvent( IOFramebuffer * framebuffer, 
                                        IOIndex event, void * info );

private:
    bool setupIVAD( void );
    bool setTheatreMode( UInt32 newMode );
    static IOReturn _framebufferEvent( OSObject * self, void * ref,
                    IOFramebuffer *framebuffer, IOIndex event, void * info );
};


class AppleVCP : public IODisplayParameterHandler
{
    OSDeclareDefaultStructors(AppleVCP);

private:
    OSDictionary *	fDisplayParams;
    UInt32 		fIICAddress;
    EEPROM *		fEEPROM;
    IVADUserPrefs *	fUserPrefs;
    UInt32		fModeIndex;
    bool		fUsesXPRAM;
    bool		fTheatreMode;

public:
    virtual bool start(	IOService * provider );

    virtual bool setDisplay( IODisplay * display );
    virtual bool doIntegerSet( OSDictionary * params,
                               const OSSymbol * paramName, UInt32 value );
    virtual bool doDataSet( const OSSymbol * paramName, OSData * value );
    virtual bool doUpdate( void );

private:
    bool readVCPRange( UInt8 command, SInt32 * max, SInt32 * current );
    bool setupVCP( void );

};

class AppleG3AIO : public IODisplayParameterHandler
{
    OSDeclareDefaultStructors(AppleG3AIO);

private:
    OSDictionary *	fDisplayParams;

public:
    virtual IOService * probe( IOService * provider, SInt32 * score );
    virtual bool start(	IOService * provider );

    virtual bool setDisplay( IODisplay * display );
    virtual bool doIntegerSet( OSDictionary * params,
                               const OSSymbol * paramName, UInt32 value );
    virtual bool doDataSet( const OSSymbol * paramName, OSData * value );
    virtual bool doUpdate( void );
};

class ApplePanel : public IODisplayParameterHandler
{
    OSDeclareAbstractStructors(ApplePanel);

protected:

    struct ApplePanelInfo {
        UInt8	min;
        UInt8	max;
    };

    OSDictionary *	fDisplayParams;
    IORegistryEntry *	fBacklightNode;
    UInt32		fPanelFamily;
    ApplePanelInfo * 	fPanelInfo;

public:
    virtual bool start(	IOService * provider );

    virtual bool setDisplay( IODisplay * display );
    virtual bool doDataSet( const OSSymbol * paramName, OSData * value );
};

class ApplePanelA : public ApplePanel
{
    OSDeclareDefaultStructors(ApplePanelA);

private:
    IONDRVFramebuffer * fFramebuffer;

public:
    virtual bool start(	IOService * provider );

    virtual bool setDisplay( IODisplay * display );
    virtual bool doIntegerSet( OSDictionary * params,
                               const OSSymbol * paramName, UInt32 value );
    virtual bool doUpdate( void );
};

class ApplePMUPanel : public ApplePanel
{
    OSDeclareDefaultStructors(ApplePMUPanel);

    UInt8	fHasContrast;

public:
    virtual bool start(	IOService * provider );

    virtual bool setDisplay( IODisplay * display );
    virtual bool doIntegerSet( OSDictionary * params,
                               const OSSymbol * paramName, UInt32 value );
    virtual bool doUpdate( void );
};

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#define super IODisplayParameterHandler

OSDefineMetaClassAndStructors(AppleIVAD, IODisplayParameterHandler);
OSDefineMetaClassAndStructors(AppleVCP, IODisplayParameterHandler);
OSDefineMetaClassAndStructors(AppleG3AIO, IODisplayParameterHandler);

OSDefineMetaClass(ApplePanel, IODisplayParameterHandler);
OSDefineAbstractStructors(ApplePanel, IODisplayParameterHandler);

OSDefineMetaClassAndStructors(ApplePanelA, ApplePanel);
OSDefineMetaClassAndStructors(ApplePMUPanel, ApplePanel);

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

bool AppleIVAD::start( IOService * provider )
{
    OSData * data;

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

    if( 0 == (data = OSDynamicCast(OSData,
                    provider->getProperty("iic-address"))))
        return( false );

    fIVAD = provider->childFromPath("ivad", gIODTPlane); // (iicAddress == iicIVAD)
    if( !fIVAD)
        return( false );

    fDisplayParams = OSDynamicCast(OSDictionary, getProperty(gIODisplayParametersKey));
    if( !fDisplayParams)
        return( false );

    fIICAddress = *((UInt32 *) data->getBytesNoCopy());

    fIVADPrefsKey  = OSSymbol::withCString("geometry");

    return( true );
}

bool AppleIVAD::setDisplay( IODisplay * display )
{
    OSNumber * num;

    fFramebuffer = OSDynamicCast(IOFramebuffer, display->getProvider()->getProvider());
    if( !fFramebuffer)
        return( false );

    fNotifier = fFramebuffer->addFramebufferNotification(
                    &AppleIVAD::_framebufferEvent, this, NULL );

    setupIVAD();
    display->setProperty( gIODisplayParametersKey, fDisplayParams);

    num = OSNumber::withNumber( kAppleOnboardGUID, 64 );
    display->setProperty( gIODisplayGUIDKey, num);
    num->release();

    return( true );
}

#pragma options align=mac68k

enum {
    kATISetAuxWin	= 0x96,
    kATIGetAuxWin	= 0x96,
    kATISetFader	= 0x97,
    kATIGetFader	= 0x97,
    kAuxWinDelay	= 6,
    kAuxWinPolarity	= 0,
    kEnableAuxWinDelay  = 1
};

struct ATIAuxWinRec {
    UInt16	x;
    UInt16	y;
    UInt16	w;
    UInt16	h;
    UInt8	enable;
    UInt8	delay;
    UInt8	bppDelayEnable;
    UInt8	polarity;
    UInt32	neededPadding[4];
};

struct ATIFaderRec {
    UInt8	enableFader;
};

#pragma options align=reset

/*  PMU99 I2C command equates (command $9A)*/
enum {
    kPMUI2CCmd			= 0x9A,

    kI2CStatusBus               = 0,                            /* pseudo bus used for status retrieval*/
    kSystemClockBus             = 1,
    kPowerSupplyBus             = 2,                            /* (IVAD is here)*/
    kSimpleI2CStream            = 0,
    kSubaddressI2CStream        = 1,
    kCombinedI2CStream          = 2,
    kI2CReplyPendingErr         = -4,
    kI2CTransactionErr          = -3,
    kI2CBusyErr                 = -2,
    kI2CParameterErr            = -1,
    kI2CNoErr                   = 0,
    kI2CReadData                = 1,
    kI2CDataBufSize             = 249
};

struct PMUI2CPB {
    UInt8                           bus;
    UInt8                           xferType;
    UInt8                           secondaryBusNum;
    UInt8                           address;
    UInt8                           subAddr;
    UInt8                           combAddr;
    UInt8                           dataCount;
    UInt8                           data[249];                  /* sizeof(PMUI2CPB) = 256*/
};
typedef struct PMUI2CPB                 PMUI2CPB;

#define MAXIICRETRYCOUNT	10
#define STATUS_DATAREAD		1
#define STATUS_OK			0
#define STATUS_BUSY			0xfe

static IOReturn
ApplePMUReadIIC( UInt8 bus, UInt8 address, UInt8 subAddress, UInt8 * readBuf, IOByteCount count )
{
    PMUI2CPB		iicPB;
    IOByteCount		readLen;
    UInt32		retries;		// loop counter for retry attempts
    IOReturn		ioResult;

    ioResult = kIOReturnError;			// assume error
    retries = MAXIICRETRYCOUNT;
    while (retries--) {

        iicPB.bus		= bus;
        iicPB.xferType		= kCombinedI2CStream;
        iicPB.secondaryBusNum	= 0;
        iicPB.address		= address;
        iicPB.subAddr		= subAddress;
        iicPB.combAddr		= 1 + address;
        iicPB.dataCount		= count - 1;

        readLen 		= count;
        readBuf[0] 		= 0xff;
        ApplePMUSendMiscCommand( kPMUI2CCmd, 8, (UInt8 *) &iicPB, &readLen, readBuf );
        if (readBuf[0] == STATUS_OK)
            break;				// if pb accepted, proceed to status/read phase

        IOSleep( 15 );
    }

    if (readBuf[0] == STATUS_OK) {

        retries = MAXIICRETRYCOUNT;
        while (retries--) {

            IOSleep( 15 );

            iicPB.bus		= kI2CStatusBus;
            readLen 		= count;
            readBuf[0]		= 0xff;
            ApplePMUSendMiscCommand( kPMUI2CCmd, 1, (UInt8 *) &iicPB, &readLen, readBuf );

            if (readBuf[0] == STATUS_DATAREAD) {
                ioResult = kIOReturnSuccess;
                break;
            }
        }
    }

    return( ioResult );
}

static IOReturn
ApplePMUWriteIIC( UInt8 bus, UInt8 address, UInt8 subAddress, UInt8 * buffer, IOByteCount count )
{
    PMUI2CPB		iicPB;
    IOByteCount		readLen;
    UInt8		readBuf[8];
    UInt32		retries;		// loop counter for retry attempts
    IOReturn		ioResult;

    ioResult = kIOReturnError;			// assume error

    retries = MAXIICRETRYCOUNT;
    while (retries--) {
        iicPB.bus		= bus;
        iicPB.xferType		= kSimpleI2CStream;
        iicPB.secondaryBusNum	= 0;
        iicPB.address		= address;
        iicPB.subAddr		= 0;
        iicPB.combAddr		= 0;
        iicPB.dataCount		= 2;
        iicPB.data[0]		= subAddress;
        bcopy( buffer, &iicPB.data[1], count );

        readLen 		= sizeof( readBuf );
        readBuf[0]		= 0xff;
        ApplePMUSendMiscCommand( kPMUI2CCmd, 8 + count, (UInt8 *) &iicPB, &readLen, readBuf );


        if (readBuf[0] == STATUS_OK)
            break;			// if pb accepted, proceed to status phase

        IOSleep( 15 );
    }

    if (readBuf[0] == STATUS_OK) {

        retries = MAXIICRETRYCOUNT;
        while (retries--) {

            IOSleep( 15 );

            // attempt to recover status

            iicPB.bus		= kI2CStatusBus;
            readLen 		= sizeof( readBuf );
            readBuf[0]		= 0xff;
            ApplePMUSendMiscCommand( kPMUI2CCmd, 1, (UInt8 *) &iicPB, &readLen, readBuf );

            if (readBuf[0] == STATUS_OK) {
                ioResult = kIOReturnSuccess;
                break;
            }
        }
    }

    return( ioResult );
}

bool AppleIVAD::doUpdate( void )
{
    IOReturn 			err;
    IODisplayModeID		mode;
    IOIndex			depth;
    IOPixelInformation		pixInfo;

    err = fFramebuffer->getCurrentDisplayMode( &mode, &depth );
    if( kIOReturnSuccess == err)
        err = fFramebuffer->getPixelInformation( mode, depth,
                                kIOFBSystemAperture, &pixInfo );
    if( kIOReturnSuccess == err) switch( pixInfo.activeWidth ) {
        case 640:
            fModeIndex = 0;
            break;
        case 800:
        default:
            fModeIndex = 1;
            break;
        case 1024:
            fModeIndex = 2;
            break;
    }

    IODisplay::setParameter( fDisplayParams, gIODisplayContrastKey,
            fUserPrefs->contrast );
    IODisplay::setParameter( fDisplayParams, gIODisplayBrightnessKey,
            fUserPrefs->brightness );
    IODisplay::setParameter( fDisplayParams, gIODisplayHorizontalPositionKey,
            fUserPrefs->modeParams[fModeIndex].horizontalPosition ^ 0x7f );
    IODisplay::setParameter( fDisplayParams, gIODisplayHorizontalSizeKey,
            fUserPrefs->modeParams[fModeIndex].horizontalSize ^ 0x7f );
    IODisplay::setParameter( fDisplayParams, gIODisplayVerticalPositionKey,
            fUserPrefs->modeParams[fModeIndex].verticalPosition ^ 0x7f );
    IODisplay::setParameter( fDisplayParams, gIODisplayVerticalSizeKey,
            fUserPrefs->modeParams[fModeIndex].verticalSize );
    IODisplay::setParameter( fDisplayParams, gIODisplayTrapezoidKey,
            fUserPrefs->modeParams[fModeIndex].keystone );
    IODisplay::setParameter( fDisplayParams, gIODisplayPincushionKey,
            fUserPrefs->modeParams[fModeIndex].pincushion );
    IODisplay::setParameter( fDisplayParams, gIODisplayParallelogramKey,
            fUserPrefs->parallelogram  ^ 0x7f);
    IODisplay::setParameter( fDisplayParams, gIODisplayRotationKey,
            fUserPrefs->tilt);
    IODisplay::setParameter( fDisplayParams, gIODisplayParametersTheatreModeKey,
            fTheatreMode );

    return( true );
}

bool AppleIVAD::setupIVAD( void )
{
    UInt8	buffer[96 + 32];
    OSData *	data;
    UInt32	address;
    SInt32	i;
    bool	ok = false;

    if( 0 == (data = OSDynamicCast(OSData,
            fIVAD->getProperty("eeprom-address"))))
        return( false );

    address = *((UInt32 *) data->getBytesNoCopy());

    if( !fEEPROM)
        fEEPROM = IONew( EEPROM, 1 );
    if( !fUserPrefs)
        fUserPrefs = IONew( IVADUserPrefs, 1 );
    if( !fEEPROM || !fUserPrefs)
        return( false );

    bzero( buffer, sizeof(buffer) );
    for (i = 0; i < (6 * 16); i += 16) {
        if (kIOReturnSuccess == ApplePMUReadIIC(
                kPowerSupplyBus, address, i, buffer + i, 17 ))
            bcopy(1 + buffer + i, buffer + i, 16 );
    }

    bcopy( buffer, fEEPROM, sizeof(EEPROM) );

#if DEBUG
    IOLog("IVAD EEPROM(%x):\n", fEEPROM->id );
    for (int i = 0; i < (6 * 16); i++) {
        IOLog("%02x ", buffer[i]);
        if( 15 == (i & 15))
            IOLog("\n");
    }
#endif

    if( (data = OSDynamicCast(OSData,
            fIVAD->getProperty(fIVADPrefsKey))))
        bcopy( data->getBytesNoCopy(), fUserPrefs, sizeof(IVADUserPrefs));
    else {
        bcopy( fEEPROM->modeParams, fUserPrefs->modeParams, sizeof(fUserPrefs->modeParams));
        fUserPrefs->contrast	  = fEEPROM->stdParams.contrast;
        fUserPrefs->brightness	  = fEEPROM->stdParams.brightness;
        fUserPrefs->parallelogram = fEEPROM->stdParams.parallelogram;
        fUserPrefs->tilt	  = fEEPROM->stdParams.tilt;
    }

//    fDisplayParams->setCapacityIncrement(1);

    IODisplay::addParameter( fDisplayParams, gIODisplayContrastKey,
                    fEEPROM->userLimits.contrast.min,
                    fEEPROM->userLimits.contrast.max );
    IODisplay::addParameter( fDisplayParams, gIODisplayBrightnessKey,
                    fEEPROM->userLimits.brightness.min,
                    fEEPROM->userLimits.brightness.max );
    IODisplay::addParameter( fDisplayParams, gIODisplayHorizontalPositionKey,
                    fEEPROM->userLimits.horizontalPosition.min,
                    fEEPROM->userLimits.horizontalPosition.max );
    IODisplay::addParameter( fDisplayParams, gIODisplayHorizontalSizeKey,
                    fEEPROM->userLimits.horizontalSize.min,
                    fEEPROM->userLimits.horizontalSize.max );
    IODisplay::addParameter( fDisplayParams, gIODisplayVerticalPositionKey,
                    fEEPROM->userLimits.verticalPosition.min,
                    fEEPROM->userLimits.verticalPosition.max );
    IODisplay::addParameter( fDisplayParams, gIODisplayVerticalSizeKey,
                    fEEPROM->userLimits.verticalSize.min,
                    fEEPROM->userLimits.verticalSize.max );
    IODisplay::addParameter( fDisplayParams, gIODisplayTrapezoidKey,
                    fEEPROM->userLimits.keystone.min,
                    fEEPROM->userLimits.keystone.max );
    IODisplay::addParameter( fDisplayParams, gIODisplayPincushionKey,
                    fEEPROM->userLimits.pincushion.min,
                    fEEPROM->userLimits.pincushion.max );
    IODisplay::addParameter( fDisplayParams, gIODisplayParallelogramKey,
                    fEEPROM->userLimits.parallelogram.min,
                    fEEPROM->userLimits.parallelogram.max );
    IODisplay::addParameter( fDisplayParams, gIODisplayRotationKey,
                    fEEPROM->userLimits.tilt.min,
                    fEEPROM->userLimits.tilt.max );

    IODisplay::addParameter( fDisplayParams, gIODisplayParametersTheatreModeKey, 0, 1 );

    ATIFaderRec	fader;
    IONDRVFramebuffer * ndrv = OSDynamicCast( IONDRVFramebuffer, fFramebuffer);
    if( ndrv && (kIOReturnSuccess == ndrv->doStatus( kATIGetFader, &fader)))
        IODisplay::addParameter( fDisplayParams, gIODisplayParametersTheatreModeWindowKey, 0, 1 );

    return( ok );
}

bool AppleIVAD::doDataSet( const OSSymbol * paramName, OSData * value )
{
    IONDRVFramebuffer *		ndrv = OSDynamicCast( IONDRVFramebuffer, fFramebuffer);
    ATIFaderRec			fader;
    ATIAuxWinRec		auxWin;
    IOGBounds *			rect;
    UInt32			newMode;
    bool 			ok = true;

    if( paramName != gIODisplayParametersTheatreModeWindowKey)
        return( false );

    if( sizeof(IOGBounds) != value->getLength())
        return( false );

    rect = (IOGBounds *) value->getBytesNoCopy();

    ok = (ndrv != 0);
    if( ok) do {

        newMode = (rect->maxx != rect->minx);
        bzero( &auxWin, sizeof( auxWin));
        auxWin.enable		= true;
        auxWin.delay		= kAuxWinDelay;
        auxWin.bppDelayEnable	= kEnableAuxWinDelay;
        fader.enableFader	= newMode;
        
        if( newMode) {
            auxWin.x		= rect->minx;
            auxWin.y		= rect->miny;
            auxWin.w		= rect->maxx - rect->minx;
            auxWin.h		= rect->maxy - rect->miny;
            auxWin.polarity	= kAuxWinPolarity;
            ok &= (kIOReturnSuccess == ndrv->doControl( kATISetAuxWin, &auxWin));
            ok &= (kIOReturnSuccess == ndrv->doControl( kATISetFader, &fader));

        } else {
            auxWin.x		= 0;
            auxWin.y		= 0;
            auxWin.w		= 1;
            auxWin.h		= 1;
            auxWin.polarity	= 0;
            ok &= (kIOReturnSuccess == ndrv->doControl( kATISetFader, &fader));
            ok &= (kIOReturnSuccess == ndrv->doControl( kATISetAuxWin, &auxWin));
        }
    } while( false );

    return( ok );
}

bool AppleIVAD::setTheatreMode( UInt32 newMode )
{
    enum { kTMRegCount = 9 };
    static const UInt8 regs[kTMRegCount] = {   kIVADContrast, kIVADBrightness,
                                                kIVADDrive1, kIVADDrive2, kIVADDrive3,
                                                kIVADCutoff1, kIVADCutoff2, kIVADCutoff3,
                                                kIVADVBLDCLevel };
    UInt8 values[kTMRegCount];
    bool 			ok = true;

    ok = true;
    newMode = (newMode != 0);
    if( newMode != fTheatreMode) {

        if( newMode) {

            values[0] = fEEPROM->briteParams.contrast;
            values[1] = fEEPROM->briteParams.brightness;
            values[2] = fEEPROM->briteParams.drive1;
            values[3] = fEEPROM->briteParams.drive2;
            values[4] = fEEPROM->briteParams.drive3;
            values[5] = fEEPROM->briteParams.cutoff1;
            values[6] = fEEPROM->briteParams.cutoff2;
            values[7] = fEEPROM->briteParams.cutoff3;
            values[8] = fEEPROM->stdParams.vblDCLevel
                    | (fEEPROM->briteParams.gainBoost ? kGainWin : 0);
        } else {
            values[0] = fUserPrefs->contrast;
            values[1] = fUserPrefs->brightness;
            values[2] = fEEPROM->stdParams.drive1;
            values[3] = fEEPROM->stdParams.drive2;
            values[4] = fEEPROM->stdParams.drive3;
            values[5] = fEEPROM->stdParams.cutoff1;
            values[6] = fEEPROM->stdParams.cutoff2;
            values[7] = fEEPROM->stdParams.cutoff3;
            values[8] = fEEPROM->stdParams.vblDCLevel;
        }

        for( int i = 0; i < kTMRegCount; i++)
            ok &= (kIOReturnSuccess == ApplePMUWriteIIC(
                    kPowerSupplyBus, fIICAddress, regs[i],
                    &values[i], sizeof(UInt8) ));
        if( ok)
            fTheatreMode = newMode;
    }
    return( ok );
}

IOReturn AppleIVAD::_framebufferEvent( OSObject * _self, void * ref,
                    IOFramebuffer * framebuffer, IOIndex event, void * info )
{
    AppleIVAD *	self = (AppleIVAD *) _self;

    return( self->framebufferEvent( framebuffer, event, info ));
}

IOReturn AppleIVAD::framebufferEvent( IOFramebuffer * framebuffer,
                                        IOIndex event, void * info )
{
    UInt8 regValue = 0x00;

    switch( event) {

        case kIOFBNotifyWillPowerOff:

            ApplePMUWriteIIC( kPowerSupplyBus, fIICAddress, kIVADContrast,
                              &regValue, sizeof(regValue) );

            regValue = fEEPROM->userLimits.verticalSize.min;
            ApplePMUWriteIIC( kPowerSupplyBus, fIICAddress, kIVADVRamp,
                              &regValue, sizeof(regValue) );

            break;

        case kIOFBNotifyDidPowerOn:

            regValue = fUserPrefs->modeParams[fModeIndex].verticalSize;
            ApplePMUWriteIIC( kPowerSupplyBus, fIICAddress, kIVADVRamp,
                              &regValue, sizeof(regValue) );

            regValue = fUserPrefs->contrast;
            ApplePMUWriteIIC( kPowerSupplyBus, fIICAddress, kIVADContrast,
                              &regValue, sizeof(regValue) );

            break;

        case kIOFBNotifyWillPowerOn:
            regValue = fEEPROM->stdParams.pll1FVC;
            // fall thru

        case kIOFBNotifyDidPowerOff:

            ApplePMUWriteIIC( kPowerSupplyBus, fIICAddress, kIVADPLL1,
                              &regValue, sizeof(regValue) );
            break;

        default:
            break;
    }

    return( kIOReturnSuccess);
}

bool AppleIVAD::doIntegerSet( OSDictionary * params, const OSSymbol * sym, UInt32 value )
{
    OSNumber *	num;
    UInt32	reg;
    UInt8	regValue;
    bool 	ok = false;

    if( sym == gIODisplayParametersDefaultKey) {
        bcopy( fEEPROM->modeParams, fUserPrefs->modeParams,
                    sizeof(fUserPrefs->modeParams));
        fUserPrefs->contrast	  = fEEPROM->stdParams.contrast;
        fUserPrefs->brightness	  = fEEPROM->stdParams.brightness;
        fUserPrefs->parallelogram = fEEPROM->stdParams.parallelogram;
        fUserPrefs->tilt	  = fEEPROM->stdParams.tilt;
        return( true );

    } else if( sym == gIODisplayParametersCommitKey) {
        IODTPlatformExpert * dtPlaform = OSDynamicCast( IODTPlatformExpert, getPlatform() );
        OSData * data = OSData::withBytesNoCopy( fUserPrefs, sizeof(IVADUserPrefs));
        if( data && dtPlaform)
            ok = (kIOReturnSuccess ==
                dtPlaform->writeNVRAMProperty( fIVAD, fIVADPrefsKey, data ));
        if( data)
            data->release();
        return( ok );

    } else if( sym == gIODisplayParametersTheatreModeKey)
        return( setTheatreMode( value ));

    if( !params)
        return( true );

    num = (OSNumber *) params->getObject("reg");
    if( !num)
        return( false );
    reg = num->unsigned32BitValue();

    if( reg & 0x100)
        regValue = value ^ 0x7f;
    else
        regValue = value;

    ok = (kIOReturnSuccess == ApplePMUWriteIIC(
                kPowerSupplyBus, fIICAddress, reg & 0x1f,
                &regValue, sizeof(regValue) ));

    if( ok) {
        if( sym == gIODisplayContrastKey)
            fUserPrefs->contrast = regValue;
        else if( sym == gIODisplayBrightnessKey)
            fUserPrefs->brightness = regValue;
        else if( sym == gIODisplayHorizontalPositionKey)
            fUserPrefs->modeParams[fModeIndex].horizontalPosition = regValue;
        else if( sym == gIODisplayHorizontalSizeKey)
            fUserPrefs->modeParams[fModeIndex].horizontalSize = regValue;
        else if( sym == gIODisplayVerticalPositionKey)
            fUserPrefs->modeParams[fModeIndex].verticalPosition = regValue;
        else if( sym == gIODisplayVerticalSizeKey)
            fUserPrefs->modeParams[fModeIndex].verticalSize = regValue;
        else if( sym == gIODisplayTrapezoidKey)
            fUserPrefs->modeParams[fModeIndex].keystone = regValue;
        else if( sym == gIODisplayPincushionKey)
            fUserPrefs->modeParams[fModeIndex].pincushion = regValue;
        else if( sym == gIODisplayParallelogramKey)
            fUserPrefs->parallelogram = regValue;
        else if( sym == gIODisplayRotationKey)
            fUserPrefs->tilt = regValue;
    }

    return( ok );
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

bool AppleVCP::start( IOService * provider )
{
    OSData * data;

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

    if( 0 == (data = OSDynamicCast(OSData,
                    provider->getProperty("iic-address"))))
        return( false );

    fDisplayParams = OSDynamicCast(OSDictionary, getProperty(gIODisplayParametersKey));
    if( !fDisplayParams)
        return( false );
        
    fIICAddress = *((UInt32 *) data->getBytesNoCopy());

    if( !setupVCP())
        return( false );

    return( true );
}

bool AppleVCP::setDisplay( IODisplay * display )
{
    OSNumber * num;

    display->setProperty( gIODisplayParametersKey, fDisplayParams);

    num = OSNumber::withNumber( kAppleOnboardGUID, 64 );
    display->setProperty( gIODisplayGUIDKey, num);
    num->release();

    return( true );
}

bool AppleVCP::doUpdate( void )
{
    return( false );
}

bool AppleVCP::doDataSet( const OSSymbol * paramName, OSData * value )
{
    return( false );
}

enum {
    kGetVCPFeature		= 0x01,
    kReplyVCPFeature		= 0x02,
    kSetVCPFeature		= 0x03,
    kGetControlKeyMode		= 0x04,
    kReplyControlKeyMode	= 0x05,
    kSetControlKeyMode		= 0x06,
    kGetTimingReport		= 0x07,
    kGetKeyReport		= 0x08,
    kResetVCPFeature		= 0x09,
    kDisableVCPFeature		= 0x0A,
    kEnableVCPFeature		= 0x0B,
    kSaveCurrentSettings	= 0x0C
};

// VCP minor commands from LGE
enum {
    kDegaussCommand		= 0x00,
    kPresetDefaultCommand	= 0x04,
    kExtBrightnessCommand	= 0x10,
    kExtContrastCommand		= 0x12,
    kRedDriveCommand		= 0x16,
    kGreenDriveCommand		= 0x18,
    kBlueDriveCommand		= 0x1A,
    kHorizontalPositionCommand	= 0x20,
    kHorizontalSizeCommand	= 0x22,
    kPincushionCommand		= 0x24,
    kPincushionBalanceCommand	= 0x26,
    kVerticalPositionCommand	= 0x30,
    kVerticalSizeCommand	= 0x32,
    kParallelogramCommand	= 0x40,
    kTrapezoidCommand		= 0x42,
    kRotationCommand		= 0x44,
    kRedCutoffCommand		= 0x80,
    kGreenCutoffCommand		= 0x82,
    kBlueCutoffCommand		= 0x84,
    kSubContrastCommand		= 0x86,
    kSubBrightnessCommand	= 0x88,
    kABLCommand			= 0x8A,
    kPresetStartCommand		= 0xE0,
    kPresetEndCommand		= 0xE2,
    kClearDataCommand		= 0xE4,
    kSaveDataCommand		= 0xEA,
    kSaveColorCommand		= 0xEB,
    kSaveSystemDataCommand	= 0xEC
};

enum {
    // this many byte to read to get vcp feature
    kIICReadBufferSize			= 4,
    kIICWriteBufferSize			= 7,
    kIICSourceAddress			= 0x50,
    kVCPCommandDataLength		= 0x84,
    // special address that LGE check to not to go into master mode (Cuda can't do multi master mode)
    kIICBogusSourceAddress		= 0xAF,
    // Throttle to 30 times a second and round up a bit.
    kMinMillisecondsBetweenIICCommands 	= 35
};

bool AppleVCP::readVCPRange( UInt8 command, SInt32 * oMax, SInt32 * current )
{
    UInt8	buffer[16];
    IOByteCount count;
    SInt32	max;
    UInt32	retries;

    retries = 0;
    do {
        buffer[0] = kIICBogusSourceAddress;
        buffer[1] = kVCPCommandDataLength;
        buffer[2] = kSetVCPFeature;
        buffer[3] = command;
        buffer[4] = 0;
        buffer[5] = 0;
        buffer[6] = fIICAddress
                    ^ buffer[0]
                    ^ buffer[1]
                    ^ buffer[2]
                    ^ buffer[3];
    
        count = kIICWriteBufferSize;
        IOSleep( 100 ); 		// 100 in 9
        AppleCudaWriteIIC( fIICAddress, buffer, &count );
        bzero( buffer, sizeof( buffer));
        count = kIICReadBufferSize;
        IOSleep( 50 );		// 50 in 9
        AppleCudaReadIIC( fIICAddress + 1, buffer, &count );
        max = (buffer[0] << 8) | buffer[1];

    } while( (max == 0) && (retries++ < 10));

    *oMax = max;
    *current = (buffer[2] << 8) | buffer[3];

    return( max != 0 );
}

bool AppleVCP::setupVCP( void )
{
    OSIterator *	iter;
    OSSymbol *		sym;
    OSDictionary *	params;
    OSNumber *		num;
    UInt32		command;
    SInt32	 	current, max;

    IOByteCount		count;
    IOReturn		err;

    current = 0;
    do {
        count = 0;
	IOSleep( 50 ); // 100 in 9
        err = AppleCudaWriteIIC( fIICAddress, (UInt8 *) &count, &count );
    } while( err && (current++ < 10));
    if( kIOReturnSuccess != err) {
        IOLog("VCP warmup fail (%x)\n", err);
        return( false );
    }

    iter = OSCollectionIterator::withCollection( fDisplayParams );
    if( iter) {
        while( (sym = (OSSymbol *) iter->getNextObject())) {

            if( sym == gIODisplayParametersCommitKey)
                continue;
            params = OSDynamicCast( OSDictionary, fDisplayParams->getObject(sym));
            if( !params)
                continue;
            num = (OSNumber *) params->getObject("reg");
            if( !num)
                continue;
            command = num->unsigned32BitValue();
            if( !readVCPRange( command, &max, &current ))
                continue;
            IODisplay::addParameter( fDisplayParams, sym, 0, max );
            IODisplay::setParameter( fDisplayParams, sym, current );
        }
        iter->release();
    }

    return( true );
}

bool AppleVCP::doIntegerSet( OSDictionary * params, const OSSymbol * sym, UInt32 value )
{
    OSNumber *	num;
    UInt8	buffer[16];
    IOByteCount count;
    bool	ok = true;
    UInt8	command;

    if( sym == gIODisplayParametersDefaultKey) {
        return( true );
    }

    if( !params)
        return( true );

    num = (OSNumber *) params->getObject("reg");
    if( !num)
        return( false );
    command = num->unsigned32BitValue();

    if( sym == gIODisplayParametersCommitKey)
        value = 0;

    buffer[0] = kIICSourceAddress;
    buffer[1] = kVCPCommandDataLength;
    buffer[2] = kSetVCPFeature;
    buffer[3] = command;
    buffer[4] = 0;
    buffer[5] = value;
    buffer[6] = fIICAddress
                ^ buffer[0]
                ^ buffer[1]
                ^ buffer[2]
                ^ buffer[3]
                ^ buffer[4]
                ^ buffer[5];

    if( sym == gIODisplayParametersCommitKey)
        IOSleep( 110 );
    else
        IOSleep( 50 ); // 100 in 9

    count = kIICWriteBufferSize;
    AppleCudaWriteIIC( fIICAddress, buffer, &count );

    if( sym == gIODisplayParametersCommitKey)
        IOSleep( 110 );

    return( ok );
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

bool ApplePanel::start( IOService * provider )
{
    OSData * data;
    OSIterator * iter;

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

    fDisplayParams = OSDynamicCast(OSDictionary, getProperty(gIODisplayParametersKey));
    if( !fDisplayParams)
        return( false );

    iter = getMatchingServices( nameMatching("backlight") );
    if( iter) {
        fBacklightNode = OSDynamicCast(IORegistryEntry, iter->getNextObject()); 
        iter->release();
    }
    if( fBacklightNode
     && (data = OSDynamicCast( OSData, fBacklightNode->getProperty("panel-family"))))
        fPanelFamily = *((UInt32 *) data->getBytesNoCopy());
    else
        fPanelFamily = 1;

    return( true );
}

bool ApplePanel::setDisplay( IODisplay * display )
{
    OSNumber *		num;
    OSData *		data;
    OSDictionary *	panelInfo;
    char		key[32];

    if( !OSDynamicCast( IOBacklightDisplay, display))
        return( false );

    if( !fPanelInfo) do {
        data = 0;
        panelInfo = OSDynamicCast( OSDictionary, getProperty("ApplePanels"));
        if( panelInfo) {

            if( (num = OSDynamicCast( OSNumber, display->getProperty(kDisplayProductID)))) {
                sprintf( key, "F%ldP%04x", fPanelFamily, num->unsigned32BitValue() );
                data = OSDynamicCast( OSData, panelInfo->getObject(key) );
            }
        
            if( !data && (num = OSDynamicCast( OSNumber, display->getProperty(kAppleSenseKey)))) {
                sprintf( key, "F%ldS%04x", fPanelFamily, num->unsigned32BitValue() );
                data = OSDynamicCast( OSData, panelInfo->getObject(key) );
            }
        
            if( !data) {
                sprintf( key, "F%ld", fPanelFamily );
                data = OSDynamicCast( OSData, panelInfo->getObject( key ));
            }

            if( !data) {
                sprintf( key, "Default" );
                data = OSDynamicCast( OSData, panelInfo->getObject(key) );
            }
        }

        IOLog("ApplePanel(key %s)\n", key);
    
        if( !data)
            return( false );

        fPanelInfo = (ApplePanelInfo *) data->getBytesNoCopy();

    } while( false );

    IODisplay::addParameter( fDisplayParams, gIODisplayBrightnessKey,
                                fPanelInfo->min, fPanelInfo->max );

    display->setProperty( gIODisplayParametersKey, fDisplayParams);

    num = OSNumber::withNumber( kAppleOnboardGUID, 64 );
    display->setProperty( gIODisplayGUIDKey, num);
    num->release();

    return( true );
}

bool ApplePanel::doDataSet( const OSSymbol * paramName, OSData * value )
{
    return( false );
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#undef super
#define super ApplePanel

bool ApplePanelA::start( IOService * provider )
{
    if( !super::start( provider))
        return( false );

    return( true );
}

bool ApplePanelA::setDisplay( IODisplay * display )
{
    IONDRVFramebuffer * fb;

    if( (fb = OSDynamicCast(IONDRVFramebuffer, display->getProvider()->getProvider()))
      && super::setDisplay( display )) {
        fFramebuffer = fb;
        return( true );
    } else
        return( false );
}

enum {
    kPanelABacklightControl	= 0xc0,
    kBacklightPowerOff	 	= 0,
    kBacklightPowerOn    	= 7
};

struct VDBacklightControlInfo {
    UInt32	backlightOnOff;
    UInt32	backlightLevel;
    UInt32	saveNVRAM;
};

bool ApplePanelA::doIntegerSet( OSDictionary * params, const OSSymbol * paramName,
                                UInt32 value )
{
    VDBacklightControlInfo	info;
    IOReturn			err;
    bool 			ok = false;

    if( (paramName != gIODisplayBrightnessKey) && (paramName != gIODisplayParametersCommitKey))
        return( false );

    do {
        bzero(&info, sizeof(info));
        err = fFramebuffer->doStatus( kPanelABacklightControl, &info);
        if( err)
            continue;

        if( paramName == gIODisplayParametersCommitKey) {
            info.saveNVRAM = 1;
        } else {
            info.saveNVRAM = 0;
            info.backlightLevel = value;
            info.backlightOnOff = value ? kBacklightPowerOn : kBacklightPowerOff;
        }

        err = fFramebuffer->doControl( kPanelABacklightControl, &info);
        if( err)
            continue;
        ok = true;

    } while( false );

    return( ok );
}

bool ApplePanelA::doUpdate( void )
{
    VDBacklightControlInfo	info;
    IOReturn			err;
    SInt32			value = 0xff;

    bzero(&info, sizeof(info));
    err = fFramebuffer->doStatus( kPanelABacklightControl, &info);
    if( kIOReturnSuccess == err) {
        value = info.backlightLevel;
        if( value > fPanelInfo->max)
            value = fPanelInfo->max;
    }
    IODisplay::setParameter( fDisplayParams, gIODisplayBrightnessKey, value );

    return( true );
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#undef super
#define super ApplePanel

enum {
    kPMUSetContrast	= 0x40,
    kPMUSetBrightness	= 0x41,
    kPMUReadContrast	= 0x48,
    kPMUReadBrightness	= 0x49,
};

enum {
    kSTNPanelSense	= 0x02,
    kSTNContrastMin	= 0,
    kSTNContrastMax	= 63,
};

#if 0
enum {
    kPMUpower1Read          = 0x19,        // more power status (DBLite)
    kPMUpower1Cntl          = 0x11,        // more power control (DBLite)
};

    UInt8		displayOn = kDisplayOn;
    UInt8		displayOff = kDisplayOff;
                localSendMiscCommand(kPMUpower1Cntl,1, &displayOn, &unused,NULL);
#define kScreenBit	0x01
#define kPowerOn	0x80
#define kPowerOff	0x00
#define kDisplayOn	kScreenBit | kPowerOn
#define kDisplayOff	kScreenBit | kPowerOff

#endif

bool ApplePMUPanel::start( IOService * provider )
{
    if( !super::start( provider))
        return( false );

    return( true );
}

bool ApplePMUPanel::setDisplay( IODisplay * display )
{
    OSNumber * num;

    if( !super::setDisplay( display ))
        return( false );

    do {
        if( 1 != fPanelFamily)
            continue;
        num = OSDynamicCast( OSNumber, display->getProperty(kAppleSenseKey));
        if( !num)
            continue;
        if( kSTNPanelSense != (num->unsigned32BitValue() >> 8))
            continue;

        fHasContrast = true;
        OSDictionary * dict = OSDictionary::withCapacity(1);
        if( dict) {
            fDisplayParams->setObject(gIODisplayContrastKey, dict);
            dict->release();
            IODisplay::addParameter( fDisplayParams, gIODisplayContrastKey,
                                     kSTNContrastMin, kSTNContrastMax );
        }
    } while( false );
    
    return( true );
}

bool ApplePMUPanel::doIntegerSet( OSDictionary * params, const OSSymbol * paramName,
                            UInt32 value )
{
    UInt32	   command;
    SInt32 	   contrast;
    IOByteCount    readBytes = 0;
    OSDictionary * param;

    if( !params)
        return( true );
    if( paramName == gIODisplayBrightnessKey)
        command = kPMUSetBrightness;
    else if( fHasContrast && (paramName == gIODisplayContrastKey))
        command = kPMUSetContrast;
    else
        return( false );

    if( value > 127)
        value = 127;
    UInt8 byte = 127 - value;

    ApplePMUSendMiscCommand( command, 1, &byte, &readBytes, NULL );

    if( fHasContrast && (paramName == gIODisplayBrightnessKey)) {

        if( (param = IODisplay::getIntegerRange( fDisplayParams, gIODisplayContrastKey, &contrast, 0, 0))) {
            if( contrast > 127)
                contrast = 127;
            byte = 127 - contrast;
            readBytes = 0;
            ApplePMUSendMiscCommand( kPMUSetContrast, 1, &byte, &readBytes, NULL );
        }
    }

    return( true );
}

bool ApplePMUPanel::doUpdate( void )
{
    SInt32	value;
    UInt8	buf[ 10 ];
    IOByteCount	readBytes = sizeof( buf );

    bzero( buf, sizeof(buf) );
    ApplePMUSendMiscCommand( kPMUReadBrightness, 0, NULL, &readBytes, buf );

    value = buf[0];
    if( value > 127)
        value = 127;
    IODisplay::setParameter( fDisplayParams, gIODisplayBrightnessKey, 127 - value );

    if( fHasContrast) {
        readBytes = sizeof( buf );
        bzero( buf, sizeof(buf) );
        ApplePMUSendMiscCommand( kPMUReadContrast, 0, NULL, &readBytes, buf );
        value = buf[0];
        if( value > 127)
            value = 127;
        IODisplay::setParameter( fDisplayParams, gIODisplayContrastKey, 127 - value );
    }

    return( true );
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#undef super
#define super IODisplayParameterHandler

#if 1
#define 	heathrowBrightnessControl ((volatile UInt8 *)0xf3000033)
#define 	heathrowContrastControl ((volatile UInt8 *)0xf3000032)
#endif
#if 0
#define		defaultBrightness	144
#define		defaultContrast		183
#endif

IOService * AppleG3AIO::probe( IOService * provider, SInt32 * score )
{
    if( 0 == getPlatform()->getProperty("AllInOne"))
        return( 0 );
    else
        return( super::probe( provider, score ));
}

bool AppleG3AIO::start( IOService * provider )
{
    if( !super::start( provider))
        return( false );

    fDisplayParams = OSDynamicCast(OSDictionary, getProperty(gIODisplayParametersKey));
    if( !fDisplayParams)
        return( false );

    return( true );
}

bool AppleG3AIO::setDisplay( IODisplay * display )
{
    OSNumber * num;

    display->setProperty( gIODisplayParametersKey, fDisplayParams);

    num = OSNumber::withNumber( kAppleOnboardGUID, 64 );
    display->setProperty( gIODisplayGUIDKey, num);
    num->release();

    RestoreXPRAM( display, fDisplayParams );

    return( true );
}

bool AppleG3AIO::doDataSet( const OSSymbol * paramName, OSData * value )
{
    return( false );
}

bool AppleG3AIO::doIntegerSet( OSDictionary * params, const OSSymbol * paramName, UInt32 value )
{
    if( paramName == gIODisplayBrightnessKey) {
        *heathrowBrightnessControl = value;

    } else if( paramName == gIODisplayContrastKey) {
        *heathrowContrastControl = value;

    } else if( paramName == gIODisplayParametersCommitKey)
        SaveXPRAM( fDisplayParams );
    else
        return( false );

    return( true );
}

bool AppleG3AIO::doUpdate( void )
{
    return( false );
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

static void RestoreXPRAM( IODisplay * display, OSDictionary * params )
{
    UInt8 bytes[2];
    SInt32 value, min, max;
    OSDictionary * param;
    IODTPlatformExpert * dtPlaform = OSDynamicCast( IODTPlatformExpert, IOService::getPlatform() );

    if( !dtPlaform
    || (kIOReturnSuccess != dtPlaform->readXPRAM( 0x50, (UInt8 *) bytes, 2 )))
        bytes[0] = bytes[1] = 0;

    if( (param = IODisplay::getIntegerRange( params, gIODisplayBrightnessKey, 0, &min, &max))) {
        value = (bytes[0] + (((UInt8) max) + ((UInt8) min)) / 2) & 0xff;
        display->setForKey( param, gIODisplayBrightnessKey, value, min, max);
    }
    if( (param = IODisplay::getIntegerRange( params, gIODisplayContrastKey, 0, &min, &max))) {
        value = (bytes[1] + ((UInt8) max)) & 0xff;
        display->setForKey( param, gIODisplayContrastKey, value, min, max);
    }
}


static void SaveXPRAM( OSDictionary * params )
{
    UInt8 bytes[2];
    SInt32 value, min, max;
    OSDictionary * param;
    IODTPlatformExpert * dtPlaform = OSDynamicCast( IODTPlatformExpert, IOService::getPlatform() );

    if( dtPlaform
    && (kIOReturnSuccess == dtPlaform->readXPRAM( 0x50, (UInt8 *) bytes, 2 ))) {

        if( (param = IODisplay::getIntegerRange( params, gIODisplayBrightnessKey, &value, &min, &max)))
            bytes[0] = value - (min + max) / 2;

        if( (param = IODisplay::getIntegerRange( params, gIODisplayContrastKey, &value, &min, &max)))
            bytes[1] = value - max;
        dtPlaform->writeXPRAM( 0x50, (UInt8 *) bytes, 2 );
    }
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

static IOReturn
ApplePMUSendMiscCommand( UInt32 command,
                        IOByteCount sendLength, UInt8 * sendBuffer,
                        IOByteCount * readLength, UInt8 * readBuffer )
{
    struct SendMiscCommandParameterBlock {
        int command;  
        IOByteCount sLength;
        UInt8 *sBuffer;
        IOByteCount *rLength; 
        UInt8 *rBuffer;
    };
    IOReturn ret = kIOReturnError;
    static IOService * pmu;

    // Wait for the PMU to show up:
    if( !pmu)
        pmu = IOService::waitForService(IOService::serviceMatching("ApplePMU"));

    SendMiscCommandParameterBlock params = { command, sendLength, sendBuffer,
                                                      readLength, readBuffer };
    if( pmu)
        ret = pmu->callPlatformFunction( "sendMiscCommand", true,
                                         (void*)&params, NULL, NULL, NULL );
    return( ret );
}

