/*
 * 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/IOMessage.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#include <IOKit/assert.h>

#ifndef offsetof
#include <stddef.h>
#endif

#include "IVAD.h"

enum {
	kIVADInvertDataMask	= 0x100,
};

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

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

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

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

class AppleIVAD : public IODisplayParameterHandler
	{
	OSDeclareDefaultStructors( AppleIVAD );

private:
	OSDictionary*				fDisplayParams;
	IOFramebuffer*				fFramebuffer;
	IONotifier*					fNotifier;
	IONotifier*					fPowerDownNotifier;
	UInt32						fIICAddress;
	IORegistryEntry*			fIVAD;
	const OSSymbol*				fIVADPrefsKey;
	IVAD1_EEPROM*				fEEPROM;
	IVAD1UserPrefs*				fUserPrefs;
	UInt32						fModeIndex;
	bool						fTheatreMode;
	bool						fAreChangingMode;
	bool						fIsSystemSleeping;
	bool						fIsDisplayOff;

public:
	virtual bool		start(	IOService* provider );
	virtual	IOReturn	message( UInt32 type, IOService* nub, void* arg );

	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 );
	virtual	IOReturn	powerDownHandler( void* refCon, UInt32 messageType, IOService* provider, void* messageArgument, vm_size_t argSize );

private:
			bool		setupIVAD( void );
			bool		setTheatreMode( UInt32 newMode );
	static	IOReturn	_framebufferEvent( OSObject* self, void* ref, IOFramebuffer* framebuffer, IOIndex event, void* info );
	static	IOReturn	_powerDownHandler( void* target, void* refCon, UInt32 messageType, IOService* provider, void* messageArgument, vm_size_t argSize );
	};


class AppleIVAD2 : public IODisplayParameterHandler
	{
	OSDeclareDefaultStructors( AppleIVAD2 );

private:
	OSDictionary*				fDisplayParams;
	IOFramebuffer*				fFramebuffer;
	IONotifier*					fNotifier;
	IONotifier*					fPowerDownNotifier;
	UInt32						fIICAddress;
	UInt32						fPWMIICAddress;
	IORegistryEntry*			fIVAD;
	const OSSymbol*				fIVADPrefsKey;
	IVAD2_EEPROM*				fEEPROM;
	IVAD2UserPrefs*				fUserPrefs;
	ModalDisplayParams*			fCurrentModeParams;

	UInt32						fIVADPHandle;
	UInt32						fIVADEEPROMPHandle;
	UInt32						fIVADPWMPHandle;

	bool						fTheatreMode;
	bool						fAreChangingMode;
	bool						fIsSystemSleeping;
	bool						fIsDisplayOff;

public:
	virtual	bool		start( IOService* provider );
	virtual	IOReturn	message( UInt32 type, IOService* nub, void* arg );

	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 );
	virtual	IOReturn	powerDownHandler( void* refCon, UInt32 messageType, IOService* provider, void* messageArgument, vm_size_t argSize );
			IOReturn	writeI2CData( UInt8 bus, UInt8 address, UInt8 subAddress, UInt8* buffer, IOByteCount count );
			IOReturn	readI2CData( UInt8 bus, UInt8 address, UInt8 subAddress, UInt8* buffer, IOByteCount count );

private:
			bool		setupIVAD( void );
			bool		setTheatreMode( UInt32 newMode );
			void		resetModeParams( void );
			OSData*		PackGeometryProperty( void );
			void		UnpackGeometryProperty( OSData * data );
			IORegistryEntry*		searchDeviceTreeForPHandle( UInt32 pHandleValue );
			IOReturn				doI2CReadWrite( const char* functionName, UInt8 bus, UInt8 address, UInt8 subAddress, UInt8* buffer, IOByteCount count );

	static	IOReturn	_framebufferEvent( OSObject* self, void* ref, IOFramebuffer* framebuffer, IOIndex event, void* info );
	static	IOReturn	_powerDownHandler( void* target, void* refCon, UInt32 messageType, IOService* provider, void* messageArgument, vm_size_t argSize );
	};


class AppleVCP : public IODisplayParameterHandler
	{
	OSDeclareDefaultStructors( AppleVCP );

private:
	OSDictionary*			fDisplayParams;
	UInt32					fIICAddress;

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 getSymbolRange( const OSSymbol* sym, SInt32* oMin, SInt32* oMax, SInt32* oCurrent );
	bool readVCPRange( UInt8 command, SInt32* max, SInt32* current );
	bool setupVCP( void );
	};

class ApplePanel : public IODisplayParameterHandler
{
    OSDeclareAbstractStructors(ApplePanel);

protected:

    struct ApplePanelInfo {
        UInt8	off;
        UInt8	min;
        UInt8	mid;
        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);

    enum { kApplePanelLevels = 128 };

private:
    IONDRVFramebuffer*	fFramebuffer;
    UInt8		fValues[ kApplePanelLevels + 1 ];
    bool 		fNoNVRAM ;

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(AppleIVAD2, IODisplayParameterHandler);
OSDefineMetaClassAndStructors(AppleVCP, IODisplayParameterHandler);

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

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

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

#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::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" );

	fAreChangingMode = false;
	fIsSystemSleeping = false;
	fIsDisplayOff = false;

	// Install handler for shutdown notifications

	fPowerDownNotifier = registerPrioritySleepWakeInterest( &AppleIVAD::_powerDownHandler, this );

	return( true );
	}


IOReturn AppleIVAD::message( UInt32 type, IOService* nub, void* arg )
	{
	IOReturn					status = kIOReturnSuccess;

	if ( type == kIOMessageServiceIsRequestingClose )
		{
		// Being asked to close.  Remove notifiers.  We cannot do this from a stop, as there
		// is the possibility of a race condition between getting notified, and trying to 
		// dispose of the notifier.

		if ( fPowerDownNotifier )
			{
			fPowerDownNotifier->remove();
			fPowerDownNotifier = NULL;
			}

		if ( fNotifier )
			{
			fNotifier->remove();
			fNotifier = NULL;
			}
		}
	else
		{
		status = super::message( type, nub, arg );
		}

	return( status );
	}


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 );
}

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( IVAD1_EEPROM, 1 );
    if( !fUserPrefs)
        fUserPrefs = IONew( IVAD1UserPrefs, 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(IVAD1_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(IVAD1UserPrefs));
    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	turningOnRegs[ kTMRegCount ] =	{
														kIVADContrast, kIVADBrightness,
														kIVADDrive1, kIVADDrive2, kIVADDrive3,
														kIVADCutoff1, kIVADCutoff2, kIVADCutoff3,
														kIVADVBLDCLevel
														};
	static const UInt8	turningOffRegs[ kTMRegCount ] =	{
														kIVADVBLDCLevel,
														kIVADCutoff3, kIVADCutoff2, kIVADCutoff1,
														kIVADDrive3, kIVADDrive2, kIVADDrive1,
														kIVADBrightness, kIVADContrast
														};
	const UInt8*		regs;
	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 );

			regs = turningOnRegs;
			}
		else
			{
			//
			// #2864322
			// When we are turning theater mode off, program the registers in the reverse order from above.
			//

			values[ 0 ] = fEEPROM->stdParams.vblDCLevel;
			values[ 1 ] = fEEPROM->stdParams.cutoff3;
			values[ 2 ] = fEEPROM->stdParams.cutoff2;
			values[ 3 ] = fEEPROM->stdParams.cutoff1;
			values[ 4 ] = fEEPROM->stdParams.drive3;
			values[ 5 ] = fEEPROM->stdParams.drive2;
			values[ 6 ] = fEEPROM->stdParams.drive1;
			values[ 7 ] = fUserPrefs->brightness;
			values[ 8 ] = fUserPrefs->contrast;

			regs = turningOffRegs;
			}

		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::_powerDownHandler( void* target, void* refCon, UInt32 messageType, IOService* provider, void* messageArgument, vm_size_t argSize )
	{
	AppleIVAD*			self = ( AppleIVAD * ) target;

	return( self->powerDownHandler( refCon, messageType, provider, messageArgument, argSize ) );
	}


IOReturn AppleIVAD::powerDownHandler( void* refCon, UInt32 messageType, IOService* provider, void* messageArgument, vm_size_t argSize )
	{
	IOReturn				status = kIOReturnUnsupported;
	UInt8					regValue;

	switch ( messageType )
		{
		case	kIOMessageSystemWillRestart:
		case	kIOMessageSystemWillPowerOff:
			{
			// We need to set the contrast to 0 on restarts and shutdown, to minimize the flash
			// that occurs when the power supply is turning off.

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

			status = kIOReturnSuccess;
			break;
			}

		case	kIOMessageSystemWillSleep:
			{
			fIsSystemSleeping = true;
			status = kIOReturnSuccess;

			if ( fIsDisplayOff )
				{
				// If the display is already off, then we won't get another frame buffer notification
				// telling us to turn off.  So, we need to turn off the PLL1 register here.

				regValue = 0;
				ApplePMUWriteIIC( kPowerSupplyBus, fIICAddress, kIVADPLL1, &regValue, sizeof( regValue ) );
				}

			break;
			}

		case	kIOMessageSystemHasPoweredOn:
			{
			fIsSystemSleeping = false;
			status = kIOReturnSuccess;

			break;
			}
		}

	return( status );
	}


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;

	switch ( event )
		{
		case	kIOFBNotifyDisplayModeWillChange:
			{
			fAreChangingMode = true;

			// Before changing resolution, turn off IVAD by setting contrast to 0.
			regValue = 0;
			ApplePMUWriteIIC( kPowerSupplyBus, fIICAddress, kIVADContrast, &regValue, sizeof( regValue) );
			break;
			}

		case	kIOFBNotifyDisplayModeDidChange:
			{
			fAreChangingMode = false;

			// After changing resolution, turn on IVAD by restoring contrast to user preferences.
			regValue = fUserPrefs->contrast;
			ApplePMUWriteIIC( kPowerSupplyBus, fIICAddress, kIVADContrast, &regValue, sizeof( regValue ) );
			break;
			}

		case	kIOFBNotifyWillPowerOff:
			{
			regValue = 0;
			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:
			{
			if ( fIsSystemSleeping )
				{
				//
				// #2983513
				// Restore the PLL1 register.
				//

				regValue = fEEPROM->stdParams.pll1FVC;
				ApplePMUWriteIIC( kPowerSupplyBus, fIICAddress, kIVADPLL1, &regValue, sizeof( regValue ) );
				fIsSystemSleeping = false;
				}

			fIsDisplayOff = false;
			break;
			}

		case	kIOFBNotifyDidPowerOff:
			{
			if ( fIsSystemSleeping )
				{
				//
				// #2983513
				// Put PLL1 register in free-running mode.  Only do this at system sleep time.
				//

				regValue = 0;
				ApplePMUWriteIIC( kPowerSupplyBus, fIICAddress, kIVADPLL1, &regValue, sizeof( regValue ) );
				}

			fIsDisplayOff = true;
			break;
			}
		}

	return( kIOReturnSuccess );
	}


bool AppleIVAD::doIntegerSet( OSDictionary * params, const OSSymbol * sym, UInt32 value )
	{
	OSNumber*					num;
	UInt32						reg;
	UInt8						regValue;
	bool						ok = true;
	bool						skipUpdatingHardware = 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( IVAD1UserPrefs ) );

			if ( data && dtPlaform)
				ok = ( kIOReturnSuccess == dtPlaform->writeNVRAMProperty( fIVAD, fIVADPrefsKey, data ) );
			if ( data)
				data->release();
			return( ok );
			}
		else
			if ( sym == gIODisplayParametersTheatreModeKey )
				return( setTheatreMode( value ) );

	// Since we want to leave the contrast off while we are updating IVAD, delay updating
	// contrast until we get the kIOFBNotifyDisplayModeDidChange message in framebufferEvent().

	if ( ( fAreChangingMode == true ) && ( sym == gIODisplayContrastKey ) )
		{
		skipUpdatingHardware = true;
		}

	if ( !params )
		return( true );

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

	if ( reg & kIVADInvertDataMask )
		regValue = value ^ 0x7F;
	else
		regValue = value;

	if ( !skipUpdatingHardware )
		{
		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 AppleIVAD2::start( IOService* provider )
	{
	OSData*					data;

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

	if ( !provider )
		return( false );

	// Even though we matched against the "ivad2" node, we need to attach ourselves to it's parent.
	// The display software will searching through the framebuffers, looking for the "IODisplayParameters"
	// dictionary that I publish.

	IOService*				frameBufferDevice;

	frameBufferDevice = OSDynamicCast( IOService, provider->copyParentEntry( gIODTPlane ) );
	if ( !frameBufferDevice )
		return( false );

	attach( frameBufferDevice );

    fIVAD = provider;

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

	// Get the IVAD2 IIC address.

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

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

	// Get the IVAD2 PWM IIC address.

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

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

	// Get the saved geometry preferences.

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

	// Get "platform-*" information, to allow us to communicate to the different IVAD-related I2C devices.

	if ( ( data = OSDynamicCast( OSData, fIVAD->getProperty( "platform-ivad" ) ) ) != NULL )
		{
		fIVADPHandle = *( ( UInt32 * ) data->getBytesNoCopy() );
		}

	if ( ( data = OSDynamicCast( OSData, fIVAD->getProperty( "platform-ivad-eeprom" ) ) ) != NULL )
		{
		fIVADEEPROMPHandle = *( ( UInt32 * ) data->getBytesNoCopy() );
		}

	if ( ( data = OSDynamicCast( OSData, fIVAD->getProperty( "platform-ivad-pwm" ) ) ) != NULL )
		{
		fIVADPWMPHandle = *( ( UInt32 * ) data->getBytesNoCopy() );
		}

	fAreChangingMode = false;
	fIsSystemSleeping = false;
	fIsDisplayOff = false;

	// Install handler for shutdown notifications

	fPowerDownNotifier = registerPrioritySleepWakeInterest( &AppleIVAD2::_powerDownHandler, this );

	return( true );
	}


IOReturn AppleIVAD2::message( UInt32 type, IOService* nub, void* arg )
	{
	IOReturn					status = kIOReturnSuccess;

	if ( type == kIOMessageServiceIsRequestingClose )
		{
		// Being asked to close.  Remove notifiers.  We cannot do this from a stop, as there
		// is the possibility of a race condition between getting notified, and trying to 
		// dispose of the notifier.

		if ( fPowerDownNotifier )
			{
			fPowerDownNotifier->remove();
			fPowerDownNotifier = NULL;
			}

		if ( fNotifier )
			{
			fNotifier->remove();
			fNotifier = NULL;
			}
		}
	else
		{
		status = super::message( type, nub, arg );
		}

	return( status );
	}


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

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

	if ( fNotifier != NULL )
		{
		// #3188989
		// This routine can get called each time the display configuration changes.  For example,
		// if an external monitor is attached, then this routine would get called.  In those cases,
		// we want to release the previous notifier before we add a new one.

		fNotifier->release();
		}

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

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

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

	return( true );
	}


bool AppleIVAD2::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)
		{
		UInt32							width;

		// Find the mode which corresponds to the current horizontal screen width.

		width = pixInfo.activeWidth;

		for ( int i = 0; i < fUserPrefs->modeParamsCount; i++ )
			{
			if ( fUserPrefs->modeParams[ i ].width == width )
				{
				fCurrentModeParams = &fUserPrefs->modeParams[ i ].mode;
				break;
				}
			}
		}

	// Initialize the dictionary to contain the current values for all the display parameters.

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

	return( true );
	}


void AppleIVAD2::UnpackGeometryProperty( OSData* data )
	{
	UInt8*				buffer;

	if ( !data )
		return;

	buffer = ( UInt8 * ) data->getBytesNoCopy();

	fUserPrefs->contrast			= *buffer++;
	fUserPrefs->brightness			= *buffer++;
	fUserPrefs->parallelogram		= *buffer++;
	fUserPrefs->tilt				= *buffer++;
	fUserPrefs->pad					= *buffer++;	// Not really used, just to be explicit.
	fUserPrefs->modeParamsCount		= *buffer++;

	for ( int i = 0; i < fUserPrefs->modeParamsCount; i++ )
		{
		fUserPrefs->modeParams[ i ].width						= *( UInt32 * ) buffer;
		buffer = ( UInt8 * ) ( ( UInt32 ) buffer + sizeof( UInt32 ) );
		fUserPrefs->modeParams[ i ].mode.horizontalPosition		= *buffer++;
		fUserPrefs->modeParams[ i ].mode.horizontalSize			= *buffer++;
		fUserPrefs->modeParams[ i ].mode.verticalPosition		= *buffer++;
		fUserPrefs->modeParams[ i ].mode.verticalSize			= *buffer++;
		fUserPrefs->modeParams[ i ].mode.keystone				= *buffer++;
		fUserPrefs->modeParams[ i ].mode.pincushion				= *buffer++;
		}
	}


OSData* AppleIVAD2::PackGeometryProperty( void )
	{
	OSData*				data;
	UInt32				geometryPropertySize;

	geometryPropertySize = 6 + ( fUserPrefs->modeParamsCount * 10 );

	data = OSData::withCapacity( geometryPropertySize );

	if ( data )
		{
		// If there is a specific call for appending a byte, then why do I have to pass in the length?
		// Dumb...
		data->appendByte( fUserPrefs->contrast, 1 );
		data->appendByte( fUserPrefs->brightness, 1 );
		data->appendByte( fUserPrefs->parallelogram, 1 );
		data->appendByte( fUserPrefs->tilt, 1 );
		data->appendByte( fUserPrefs->pad, 1 );				// Make space for pad byte.
		data->appendByte( fUserPrefs->modeParamsCount, 1 );

		for ( int i = 0; i < fUserPrefs->modeParamsCount; i++ )
			{
			data->appendBytes( &fUserPrefs->modeParams[ i ].width, sizeof( UInt32 ) );
			data->appendByte( fUserPrefs->modeParams[ i ].mode.horizontalPosition, 1 );
			data->appendByte( fUserPrefs->modeParams[ i ].mode.horizontalSize, 1 );
			data->appendByte( fUserPrefs->modeParams[ i ].mode.verticalPosition, 1 );
			data->appendByte( fUserPrefs->modeParams[ i ].mode.verticalSize, 1 );
			data->appendByte( fUserPrefs->modeParams[ i ].mode.keystone, 1 );
			data->appendByte( fUserPrefs->modeParams[ i ].mode.pincushion, 1 );
			}
		}

	return( data );
	}


IORegistryEntry* AppleIVAD2::searchDeviceTreeForPHandle( UInt32 pHandleValue )
	{
	IORegistryIterator*								iterator;
	IORegistryEntry*								matchingEntry = NULL;

	iterator = IORegistryIterator::iterateOver( gIODTPlane, kIORegistryIterateRecursively );

	if ( iterator == NULL )
		return( matchingEntry );

	while ( ( matchingEntry = iterator->getNextObject() ) != NULL )
		{
		OSData*						property;

		if ( ( property = OSDynamicCast( OSData, matchingEntry->getProperty( "AAPL,phandle" ) ) ) != NULL )
			{
			if ( pHandleValue == *( ( UInt32 * ) property->getBytesNoCopy() ) )
				break;
			}
		}

	iterator->release();

	return( matchingEntry );
	}


IOReturn AppleIVAD2::doI2CReadWrite( const char* functionName, UInt8 bus, UInt8 address, UInt8 subAddress, UInt8* buffer, IOByteCount count )
	{
	static IOService*					pmuI2CDriver = NULL;
	IOReturn							status = kIOReturnSuccess;
	UInt8								adjustedAddress;

	if ( !pmuI2CDriver )
		{
		// I know that all of the IVAD properties are under the same I2C bus.  So, just find the parent of the node which
		// has a "AAPL,phandle" property with the value of fIVADPHandle.

		IORegistryEntry*					phandleEntry;
		IOService*							parentDevice;

		phandleEntry = searchDeviceTreeForPHandle( fIVADPHandle );

		if ( phandleEntry )
			{
			parentDevice = ( IOService * ) phandleEntry->getParentEntry( gIODTPlane );
			if ( parentDevice )
				{
				while ( ( pmuI2CDriver = ( IOService * ) parentDevice->getChildEntry( gIOServicePlane ) ) == NULL )
					IOSleep( 200 );
				}
			}
		}

	if ( !pmuI2CDriver )
		{
		return( kIOReturnError );
		}

	status = pmuI2CDriver->callPlatformFunction( "openI2CBus", true, ( void * ) ( ( UInt32 ) bus ), NULL, NULL, NULL );

	if ( status != kIOReturnSuccess )
		{
		return( status );
		}

	if ( address & 0x01 )
		{
		// Read case is combined mode.

		( void ) pmuI2CDriver->callPlatformFunction( "setCombinedMode", true, NULL, NULL, NULL, NULL );
		}
	else
		{
		// Write case is subStandard mode.

		( void ) pmuI2CDriver->callPlatformFunction( "setStandardSubMode", true, NULL, NULL, NULL, NULL );
		}

	// Traditionally, the low bit of the device address indicates whether this is a read or a write.  So, the PMU I2C driver
	// doesn't need that, as it knows whether this is a read or a write.  In either case, it will be the one who inserts the
	// write bit into the address.

	adjustedAddress = address >> 1;

	status = pmuI2CDriver->callPlatformFunction( functionName, true, ( void * ) ( ( UInt32 ) adjustedAddress ), ( void * ) ( ( UInt32 ) subAddress ), ( void * ) buffer, ( void * ) count );

	( void ) pmuI2CDriver->callPlatformFunction( "closeI2CBus", true, NULL, NULL, NULL, NULL );

	return( status );
	}


IOReturn AppleIVAD2::writeI2CData( UInt8 bus, UInt8 address, UInt8 subAddress, UInt8* buffer, IOByteCount count )
	{
	return( doI2CReadWrite( "writeI2CBus", bus, address & ~1, subAddress, buffer, count ) );
	}


IOReturn AppleIVAD2::readI2CData( UInt8 bus, UInt8 address, UInt8 subAddress, UInt8* buffer, IOByteCount count )
	{
	return( doI2CReadWrite( "readI2CBus", bus, address | 1, subAddress, buffer, count ) );
	}


//
//	resetModeParams
//
//	Reset the individual geometry parameters for all of the supported modes to the default
//	parameters read from the EEPROM.
//	Reset the global geometry parameters to the default parameters read from the EEPROM.
//

#define detectIVADModes 0

void AppleIVAD2::resetModeParams( void )
	{
#if !detectIVADModes
	const UInt32						supportedModes[ kIVAD2SupportedModes ]  = { 640, 800, 1024, 1152, 1280 };
#else
	IOItemCount							modeCount;
	IODisplayModeID*					displayModeList;
	IODisplayModeInformation			modeInfo;
	IOReturn							result;
#endif
	UInt32								i;

#if detectIVADModes
	modeCount = fUserPrefs->modeParamsCount;

	displayModeList = IONew( IODisplayModeID, modeCount );
	if ( !displayModeList )
		return;					//ееее should default to known modes.

	result = fFramebuffer->getDisplayModes( displayModeList );

	if ( result != kIOReturnSuccess )
		goto resetModeParams_getDisplayModesErr;

	for ( i = 0; i < modeCount; i++ )
		{
		bcopy( &fEEPROM->modeParams[ i ], &fUserPrefs->modeParams[ i ].mode, sizeof( fUserPrefs->modeParams[ i ].mode ) );

		fFramebuffer->getInformationForDisplayMode( displayModeList[ i ], &modeInfo );
		fUserPrefs->modeParams[ i ].width = modeInfo.nominalWidth;
		}

resetModeParams_getDisplayModesErr:
	IODelete( displayModeList, IODisplayModeID, modeCount );
#else
	for ( i = 0; i < fUserPrefs->modeParamsCount; i++ )
		{
		bcopy( &fEEPROM->modeParams[ i ], &fUserPrefs->modeParams[ i ].mode, sizeof( fUserPrefs->modeParams[ i ].mode ) );
		fUserPrefs->modeParams[ i ].width = supportedModes[ i ];
		}
#endif

	fUserPrefs->contrast = fEEPROM->stdParams.contrast;
	fUserPrefs->brightness = fEEPROM->stdParams.brightness;
	fUserPrefs->parallelogram = fEEPROM->stdParams.parallelogram;
	fUserPrefs->tilt = fEEPROM->stdParams.tilt;
	}


bool AppleIVAD2::setupIVAD( void )
	{
	UInt8				buffer[ sizeof( IVAD2_EEPROM ) + 32 ];
	OSData*				data;
	IOItemCount			modeCount;
	UInt32				address;
	UInt32				userPrefsSize;
	SInt32				i;
	SInt32				chunks;
	bool				ok = false;

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

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

#if detectIVADModes
	modeCount = fFramebuffer->getDisplayModeCount();
#else
	modeCount = kIVAD2SupportedModes;
#endif

	if ( !fEEPROM )
		fEEPROM = IONew( IVAD2_EEPROM, 1 );
	userPrefsSize = sizeof( ModalWidthDisplayParams ) * modeCount + offsetof( IVAD2UserPrefs, modeParams );
	if ( !fUserPrefs )
		fUserPrefs = ( IVAD2UserPrefs * ) IOMalloc( userPrefsSize );
	if ( !fEEPROM || !fUserPrefs )
		return( false );

	fUserPrefs->modeParamsCount = modeCount;

	// Read in the EEPROM in 16 byte chunks.  16 bytes is the largest amount the PMU can transfer in
	// a single request.

	bzero( buffer, sizeof(buffer) );
	chunks = sizeof( IVAD2_EEPROM ) / 16 + 1;
	for ( i = 0; i < chunks * 16; i += 16 )
		{
		readI2CData( kPowerSupplyBus, address, i, buffer + i, 16 );
		}

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

	// Try and get the saved geometry preferences.  If we cannot find it,
	// then regenerate it from the EEPROM data.

	if ( ( data = OSDynamicCast( OSData, fIVAD->getProperty( fIVADPrefsKey ) ) ) )
		{
		UnpackGeometryProperty( data );
		}
	else
		{
		// We couldn't get the geometry preferences.  Build a new one from the EEPROM.
		resetModeParams();
		}

	// Set the current, default mode to the first entry.
	fCurrentModeParams = &fUserPrefs->modeParams[ 0 ].mode;

//	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 AppleIVAD2::doDataSet( const OSSymbol* paramName, OSData* value )
	{
	IOGBounds*						rect;
	IONDRVFramebuffer*				ndrv = OSDynamicCast( IONDRVFramebuffer, fFramebuffer );
	bool							ok = true;

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

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

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

	ok = ( ndrv != 0 );
	if ( ok )
		{
		ATIFaderRec						fader;
		ATIAuxWinRec					auxWin;
		UInt32							newMode;

		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 ) );
			}
		}

	return( ok );
	}


bool AppleIVAD2::setTheatreMode( UInt32 newMode )
	{
	enum { kTMRegCount = 9 };
	static const UInt16			turningOnRegs[ kTMRegCount ] =	{
																kIVADContrast, kIVADBrightness,
																kIVADDrive1, kIVADDrive2, kIVADDrive3,
																kIVADPWMCutoff1 + kIVADUsePWMAddress, kIVADPWMCutoff2 + kIVADUsePWMAddress, kIVADPWMCutoff3 + kIVADUsePWMAddress,
																kIVADVBLDCLevel
																};
	static const UInt16			turningOffRegs[ kTMRegCount ] =	{
																kIVADVBLDCLevel,
																kIVADPWMCutoff3 + kIVADUsePWMAddress, kIVADPWMCutoff2 + kIVADUsePWMAddress, kIVADPWMCutoff1 + kIVADUsePWMAddress,
																kIVADDrive3, kIVADDrive2, kIVADDrive1,
																kIVADBrightness, kIVADContrast
																};
	const UInt16*				regs;
	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 );

			regs = turningOnRegs;
			}
		else
			{
			//
			// #2864322
			// When we are turning theater mode off, program the registers in the reverse order from above.
			//

			values[ 0 ] = fEEPROM->stdParams.vblDCLevel;
			values[ 1 ] = fEEPROM->stdParams.cutoff3;
			values[ 2 ] = fEEPROM->stdParams.cutoff2;
			values[ 3 ] = fEEPROM->stdParams.cutoff1;
			values[ 4 ] = fEEPROM->stdParams.drive3;
			values[ 5 ] = fEEPROM->stdParams.drive2;
			values[ 6 ] = fEEPROM->stdParams.drive1;
			values[ 7 ] = fUserPrefs->brightness;
			values[ 8 ] = fUserPrefs->contrast;

			regs = turningOffRegs;
			}

		for ( int i = 0; i < kTMRegCount; i++ )
			{
			UInt16						subAddr;
			UInt8						iicAddress;

			subAddr = regs[ i ];

			// Certain IVAD control registers have moved to the new PWM part.  Those registers have
			// the kIVADUsePWMAddress mask set in the above regs structure.

			if ( subAddr & kIVADUsePWMAddress )
				iicAddress = fPWMIICAddress;
			else
				iicAddress = fIICAddress;

			ok &= ( kIOReturnSuccess == writeI2CData( kPowerSupplyBus, iicAddress, subAddr & 0x1F, &values[ i ], sizeof( UInt8 ) ) );
			}

		if ( ok )
			fTheatreMode = newMode;
		}
	return( ok );
	}


IOReturn AppleIVAD2::_powerDownHandler( void* target, void* refCon, UInt32 messageType, IOService* provider, void* messageArgument, vm_size_t argSize )
	{
	AppleIVAD2*			self = ( AppleIVAD2 * ) target;

	return( self->powerDownHandler( refCon, messageType, provider, messageArgument, argSize ) );
	}


IOReturn AppleIVAD2::powerDownHandler( void* refCon, UInt32 messageType, IOService* provider, void* messageArgument, vm_size_t argSize )
	{
	IOReturn				status = kIOReturnUnsupported;
	UInt8					regValue;

	switch ( messageType )
		{
		case	kIOMessageSystemWillRestart:
		case	kIOMessageSystemWillPowerOff:
			{
			// On a restart or shutdown, we need to: set the contrast to 0 and set all the cutoff
			// PWM registers to 0.  This minimizes the flash that occurs when the power supply is turning off.

			regValue = 0;

			writeI2CData( kPowerSupplyBus, fIICAddress, kIVADContrast, &regValue, sizeof( regValue) );
			writeI2CData( kPowerSupplyBus, fPWMIICAddress, kIVADPWMCutoff1, &regValue, sizeof( regValue ) );
			writeI2CData( kPowerSupplyBus, fPWMIICAddress, kIVADPWMCutoff2, &regValue, sizeof( regValue ) );
			writeI2CData( kPowerSupplyBus, fPWMIICAddress, kIVADPWMCutoff3, &regValue, sizeof( regValue ) );

			status = kIOReturnSuccess;
			break;
			}

		case	kIOMessageSystemWillSleep:
			{
			fIsSystemSleeping = true;
			status = kIOReturnSuccess;

			if ( fIsDisplayOff )
				{
				// If the display is already off, then we won't get another frame buffer notification
				// telling us to turn off.  So, we need to turn off the PLL1 register here.

				regValue = 0;
				writeI2CData( kPowerSupplyBus, fIICAddress, kIVADPLL1, &regValue, sizeof( regValue ) );
				}

			break;
			}

		case	kIOMessageSystemHasPoweredOn:
			{
			fIsSystemSleeping = false;
			status = kIOReturnSuccess;

			break;
			}
		}

	return( status );
	}


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

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


IOReturn AppleIVAD2::framebufferEvent( IOFramebuffer* framebuffer, IOIndex event, void* info )
	{
	UInt8						regValue;

	switch ( event )
		{
		case	kIOFBNotifyDisplayModeWillChange:
			{
			fAreChangingMode = true;

			// Before changing resolution, turn off IVAD by setting contrast to 0.

			regValue = 0;
			writeI2CData( kPowerSupplyBus, fIICAddress, kIVADContrast, &regValue, sizeof( regValue ) );
			break;
			}

		case	kIOFBNotifyDisplayModeDidChange:
			{
			fAreChangingMode = false;

			// After changing resolution, turn on IVAD by restoring contrast to user preferences.

			regValue = fUserPrefs->contrast;
			writeI2CData( kPowerSupplyBus, fIICAddress, kIVADContrast, &regValue, sizeof( regValue ) );
			break;
			}

		case	kIOFBNotifyWillPowerOff:
			{
			regValue = 0;
			writeI2CData( kPowerSupplyBus, fIICAddress, kIVADContrast, &regValue, sizeof( regValue ) );

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

		case	kIOFBNotifyDidPowerOn:
			{
			regValue = fCurrentModeParams->verticalSize;
			writeI2CData( kPowerSupplyBus, fIICAddress, kIVADVRamp, &regValue, sizeof( regValue ) );

			// Need to delay for 20 ms before setting the contrast.

			IOSleep( 20 );
			regValue = fUserPrefs->contrast;
			writeI2CData( kPowerSupplyBus, fIICAddress, kIVADContrast, &regValue, sizeof( regValue ) );
			break;
			}

		case	kIOFBNotifyWillPowerOn:
			{
			if ( fIsSystemSleeping )
				{
				//
				// #2983513
				// Restore the PLL1 register.
				//

				regValue = fEEPROM->stdParams.pll1FVC;
				writeI2CData( kPowerSupplyBus, fIICAddress, kIVADPLL1, &regValue, sizeof( regValue ) );
				fIsSystemSleeping = false;
				}

			regValue = fEEPROM->stdParams.cutoff1;
			writeI2CData( kPowerSupplyBus, fPWMIICAddress, kIVADPWMCutoff1, &regValue, sizeof( regValue ) );

			regValue = fEEPROM->stdParams.cutoff2;
			writeI2CData( kPowerSupplyBus, fPWMIICAddress, kIVADPWMCutoff2, &regValue, sizeof( regValue ) );

			regValue = fEEPROM->stdParams.cutoff3;
			writeI2CData( kPowerSupplyBus, fPWMIICAddress, kIVADPWMCutoff3, &regValue, sizeof( regValue ) );

			fIsDisplayOff = false;
			break;
			}

		case	kIOFBNotifyDidPowerOff:
			{
			regValue = 0;

			writeI2CData( kPowerSupplyBus, fPWMIICAddress, kIVADPWMCutoff1, &regValue, sizeof( regValue ) );
			writeI2CData( kPowerSupplyBus, fPWMIICAddress, kIVADPWMCutoff2, &regValue, sizeof( regValue ) );
			writeI2CData( kPowerSupplyBus, fPWMIICAddress, kIVADPWMCutoff3, &regValue, sizeof( regValue ) );

			if ( fIsSystemSleeping )
				{
				//
				// #2983513
				// Put PLL1 register in free-running mode.  Only do this at system sleep time.
				//

				writeI2CData( kPowerSupplyBus, fIICAddress, kIVADPLL1, &regValue, sizeof( regValue ) );
				}

			fIsDisplayOff = true;
			break;
			}
		}

	return( kIOReturnSuccess );
	}


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

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

	// Since we want to leave the contrast off while we are updating IVAD, delay updating
	// contrast until we get the kIOFBNotifyDisplayModeDidChange message in framebufferEvent().

	if ( ( fAreChangingMode == true ) && ( sym == gIODisplayContrastKey ) )
		{
		skipUpdatingHardware = true;
		}

	if ( sym == gIODisplayParametersCommitKey )
		{
		IODTPlatformExpert*				dtPlaform = OSDynamicCast( IODTPlatformExpert, getPlatform() );
		OSData*							data;

		// Create the data for the NVRAM property.

		data = PackGeometryProperty();

		if ( data && dtPlaform )
			ok = ( kIOReturnSuccess == dtPlaform->writeNVRAMProperty( fIVAD, fIVADPrefsKey, data ) );
		if ( data )
			data->release();
		return( ok );
		}

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

	if ( !params )
		return( true );

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

	if ( reg & kIVADInvertDataMask )
		regValue = value ^ 0x7F;
	else
		regValue = value;

	// Certain IVAD control registers have moved to the new PWM part.  Those registers have
	// the kIVADUsePWMAddress mask set in the IODisplayParameters dictionary.

	if ( reg & kIVADUsePWMAddress )
		iicAddress = fPWMIICAddress;
	else
		iicAddress = fIICAddress;

	if ( !skipUpdatingHardware )
		{
		ok = ( kIOReturnSuccess == writeI2CData( kPowerSupplyBus, iicAddress, reg & 0x1F, &regValue, sizeof( regValue ) ) );
		}

	if ( ok )
		{
		if ( sym == gIODisplayContrastKey )
			fUserPrefs->contrast = regValue;
		else
			if ( sym == gIODisplayBrightnessKey )
				fUserPrefs->brightness = regValue;
			else
				if ( sym == gIODisplayHorizontalPositionKey )
					fCurrentModeParams->horizontalPosition = regValue;
				else
					if ( sym == gIODisplayHorizontalSizeKey )
						fCurrentModeParams->horizontalSize = regValue;
					else
						if ( sym == gIODisplayVerticalPositionKey )
							fCurrentModeParams->verticalPosition = regValue;
						else
							if ( sym == gIODisplayVerticalSizeKey )
								fCurrentModeParams->verticalSize = regValue;
							else
								if ( sym == gIODisplayTrapezoidKey )
									fCurrentModeParams->keystone = regValue;
								else
									if ( sym == gIODisplayPincushionKey )
										fCurrentModeParams->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::getSymbolRange( const OSSymbol* sym, SInt32* oMin, SInt32* oMax, SInt32* oCurrent )
	{
	OSNumber*						num;
	OSDictionary*					params;
	UInt32							command;
	bool							success = false;

	if ( ( params = OSDynamicCast( OSDictionary, fDisplayParams->getObject( sym ) ) ) == NULL )
		return( false );

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

	*oMin = 0;

	command = num->unsigned32BitValue();
	success = readVCPRange( command, oMax, oCurrent );

	return( success );
	}


bool AppleVCP::doUpdate( void )
	{
	OSIterator*						iter;

	iter = OSCollectionIterator::withCollection( fDisplayParams );
	if ( iter )
		{
		const OSSymbol*						sym;

		while( ( sym = ( const OSSymbol * ) iter->getNextObject() ) )
			{
			SInt32							current;
			SInt32							max;
			SInt32							min;

			if ( sym == gIODisplayParametersCommitKey )
				continue;

			if ( sym == gIODisplayParametersDefaultKey )
				continue;

			if ( !getSymbolRange( sym, &min, &max, &current ) )
				continue;

			IODisplay::setParameter( fDisplayParams, sym, current );
			}

		iter->release();
		}

	return( true );
	}


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* oCurrent )
	{
	UInt8					buffer[ 16 ];
	IOByteCount				count;
	UInt32					retries;
	SInt32					max;
	SInt32					current;

	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
		( void ) AppleCudaWriteIIC( fIICAddress, buffer, &count );

		bzero( buffer, sizeof( buffer ) );
		count = kIICReadBufferSize;
		IOSleep( 50 );		// 50 in 9
		( void ) AppleCudaReadIIC( fIICAddress + 1, buffer, &count );

		max = ( buffer[ 0 ] << 8 ) | buffer[ 1 ];
		current = ( buffer[ 2 ] << 8 ) | buffer[ 3 ];
		}
	while ( ( max == 0 ) && ( retries++ < 10 ) );

	*oMax = max;
	*oCurrent = current;

	return( max != 0 );
	}


bool AppleVCP::setupVCP( void )
	{
	OSIterator*						iter;
	IOByteCount						zeroData;
	IOReturn						err;
	int								count;

	count = 0;

	do
		{
        zeroData = 0;
		IOSleep( 50 ); // 100 in 9
		err = AppleCudaWriteIIC( fIICAddress, ( UInt8 * ) &zeroData, &zeroData );
		}
	while ( err && ( count++ < 10 ) );

	if ( kIOReturnSuccess != err )
		{
#if DEBUG
		IOLog( "VCP warmup fail (%x)\n", err );
#endif
		return( false );
		}

	iter = OSCollectionIterator::withCollection( fDisplayParams );
	if ( iter )
		{
		const OSSymbol*						sym;

		while( ( sym = ( const OSSymbol * ) iter->getNextObject() ) )
			{
			SInt32							current;
			SInt32							max;
			SInt32							min;

			if ( sym == gIODisplayParametersCommitKey )
				continue;

			if ( sym == gIODisplayParametersDefaultKey )
				continue;

			if ( !getSymbolRange( sym, &min, &max, &current ) )
				continue;

			IODisplay::addParameter( fDisplayParams, sym, min, max );
			IODisplay::setParameter( fDisplayParams, sym, current );
			}
		iter->release();
		}

	return( true );
	}


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

	if ( !params )
		{
		return( true );
		}

	if ( ( sym == gIODisplayParametersCommitKey ) || ( sym == gIODisplayParametersDefaultKey ) )
		{
		value = 0;
		}

	IOSleep( 50 ); // 100 in 9

	if ( sym == gIODisplayParametersCommitKey )
		{
		// We need to issue two separate commands to save away the current graphics state: a kSaveDataCommand and
		// kSaveSystemDataCommand.

		command = kSaveDataCommand;
		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 ];

		count = kIICWriteBufferSize;

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

		IOSleep( 50 ); // 100 in 9

		command = kSaveSystemDataCommand;
		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 ];

		count = kIICWriteBufferSize;

		err = AppleCudaWriteIIC( fIICAddress, buffer, &count );
		}
	else
		{
		num = ( OSNumber * ) params->getObject( "reg" );
		if ( !num )
			{
			return( false );
			}

		command = num->unsigned32BitValue();

		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 ];

		count = kIICWriteBufferSize;

		err = AppleCudaWriteIIC( fIICAddress, buffer, &count );
		}

	if ( ( sym == gIODisplayParametersCommitKey ) || ( sym == gIODisplayParametersDefaultKey ) )
		IOSleep( 110 );
	else
		IOSleep( 50 );

	return( ok );
	}


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

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

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

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

    fBacklightNode = provider;

    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) );
            }
        }

#if DEBUG
        IOLog("ApplePanel(key %s)\n", key);
#endif

        if( !data)
            return( false );

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

    } while( false );

    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

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

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

bool ApplePanelA::start( IOService * provider )
{
    IORegistryEntry * parent;
    IORegistryEntry * regEntry;
    bool pmu = false;
    OSData*	    data;
    
    parent = provider->copyParentEntry( gIODTPlane );
    if( parent) {
        pmu = (0 == strcmp("via-pmu", parent->getName()));
        parent->release();
    }
    if( pmu)
        return( false );

    if( !super::start( provider))
        return( false );
    
   // Assume that there is no NVRAM
    fNoNVRAM  = true;
    if ((regEntry = IORegistryEntry::fromPath("/options", gIODTPlane)))
    {
        if ( 0 != ( data = OSDynamicCast( OSData, regEntry->getProperty( "aapl,pci" ) ) ) )
        {
            // If there is value in the aapl,pci property, NVRAM has been initialized
            if (data->getLength() > 0)
                 fNoNVRAM  = false;
        }
	regEntry->release();
    }
 
    return( true );
}

bool ApplePanelA::setDisplay( IODisplay * display )
{
    IONDRVFramebuffer * 	fb;
    VDBacklightControlInfo	info;
    IOReturn			err;

    if( (fb = OSDynamicCast(IONDRVFramebuffer, display->getProvider()->getProvider()))
      && super::setDisplay( display )) {

        fFramebuffer = fb;

        IODisplay::addParameter( fDisplayParams, gIODisplayBrightnessKey,
//                                  fPanelInfo->min, fPanelInfo->max );
//	                            0 + (16*fPanelInfo->min - fPanelInfo->max) / 15, fPanelInfo->max );
                                    0, kApplePanelLevels );
        UInt32 i, j, k;
        fValues[0] = fPanelInfo->off;
        for( i = 1, k = (kApplePanelLevels/16); i <= k; i++)
            fValues[i] = fPanelInfo->min;
    
        for( j = i, k = (15*kApplePanelLevels)/16; i <= k; i++)
            fValues[i] = (i - j) * (fPanelInfo->mid - fPanelInfo->min - 1) / (k - j)
                        + fPanelInfo->min + 1;

        for( j = i, k = kApplePanelLevels; i <= k; i++)
            fValues[i] = (i - j) * (fPanelInfo->max - fPanelInfo->mid - 1) / (k - j)
                        + fPanelInfo->mid + 1;
    
        fBacklightNode->setProperty("ApplePanelRawTable", fValues, sizeof(fValues));

        bzero(&info, sizeof(info));
        err = fFramebuffer->doStatus( kPanelABacklightControl, &info);
        if( (kIOReturnSuccess == err)
         && (kBacklightPowerOff == info.backlightOnOff)
         && (0 == info.backlightLevel) || fNoNVRAM ) {
            info.saveNVRAM = 0;
            info.backlightLevel = fPanelInfo->max;
            info.backlightOnOff = kBacklightPowerOn;
            fFramebuffer->doControl( kPanelABacklightControl, &info);
        }

        return( true );
    } else
        return( false );
}

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) {
	    if (kBacklightPowerOff == info.backlightOnOff)
		info.backlightLevel = fPanelInfo->off;
            info.saveNVRAM = 1;
        } else {
            info.saveNVRAM = 0;
            info.backlightLevel = (value <= kApplePanelLevels) ? fValues[value] : fPanelInfo->max;
            if(info.backlightLevel)
                info.backlightOnOff = kBacklightPowerOn;
            else
                info.backlightOnOff = kBacklightPowerOff;
        }

        err = fFramebuffer->doControl( kPanelABacklightControl, &info);
        if( err)
	    continue;
        fBacklightNode->setProperty("ApplePanelRawBrightness", info.backlightLevel, 32);
        ok = true;

    } while( false );

    return( ok );
}

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

    bzero(&info, sizeof(info));
    err = fFramebuffer->doStatus( kPanelABacklightControl, &info);
    if( kIOReturnSuccess == err) {
	if (kBacklightPowerOff == info.backlightOnOff)
	    value = 0;
	else
	{
	    UInt32 raw = info.backlightLevel;
	    for( value = kApplePanelLevels;
		(value > 0) && ((raw < fValues[value]) || (raw == fValues[value - 1]));
		value--)	{}
	}
    }
    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 )
{
    IORegistryEntry * parent;
    bool pmu = false;

    parent = provider->copyParentEntry( gIODTPlane );
    if( parent) {
        pmu = (0 == strcmp("via-pmu", parent->getName()));
        parent->release();
    }
    if( !pmu)
        return( false );

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

    return( true );
}

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

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

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

    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 );
}

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

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 );
}

static IOReturn
AppleCudaIIC( const OSSymbol * sym, UInt8 address, UInt8 * buffer, IOByteCount * count )
{
    static IOService * cuda;
    IOReturn ret = kIOReturnError;

    // Wait for Cuda to show up:
    if (!cuda)
        cuda = IOService::waitForService(IOService::serviceMatching("AppleCuda"));
    if (cuda)
        ret = cuda->callPlatformFunction( sym, false, (void *) (UInt32) address, buffer, count, 0 );

    return (ret);
}

static IOReturn
AppleCudaWriteIIC( UInt8 address, const UInt8 * buffer, IOByteCount * count )
{
    static const OSSymbol * sym;

    if (!sym)
        sym = OSSymbol::withCString("write_iic");

    return (AppleCudaIIC(sym, address, (UInt8 *) buffer, count));
}

static IOReturn
AppleCudaReadIIC( UInt8 address, UInt8 * buffer, IOByteCount * count )
{
    static const OSSymbol * sym;

    if (!sym)
        sym = OSSymbol::withCString("read_iic");

    return (AppleCudaIIC(sym, address, buffer, count));
}
