IOBacklightDisplay.cpp   [plain text]


/*
 * 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@
 */

#define IOFRAMEBUFFER_PRIVATE

#include <IOKit/IOLib.h>
#include <IOKit/IOUserClient.h>
#include <IOKit/IOTimerEventSource.h>
#include <IOKit/IOHibernatePrivate.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#include <IOKit/pwr_mgt/IOPM.h>

#include <IOKit/graphics/IOGraphicsPrivate.h>
#include <IOKit/graphics/IODisplay.h>
#include <IOKit/ndrvsupport/IOMacOSVideo.h>

#include "IOGraphicsKTrace.h"

/*
    We further divide the actual display panel brightness levels into four
    IOKit power states which we export to our superclass.
    
    In the lowest state, the display is off.  This state consists of only one
    of the brightness levels, the lowest. In the next state it is in the dimmest
    usable state. The highest state consists of all the brightness levels.
    
    The display has no state or configuration or programming that would be
    saved/restored over power state changes, and the driver does not register
    with the superclass as an interested driver.
    
    This driver doesn't have much to do. It changes between the four power state
    brightnesses on command from the superclass, and it notices which brightness
    level the user has set.
    
    The only smart thing it does is keep track of which of the brightness levels
    the user has selected, and it never exceeds that on command from the display
    device object.
*/

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

#define super IODisplay

OSDefineMetaClassAndStructors(IOBacklightDisplay, IODisplay)

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

OSMetaClassDefineReservedUnused(IOBacklightDisplay, 0);
OSMetaClassDefineReservedUnused(IOBacklightDisplay, 1);
OSMetaClassDefineReservedUnused(IOBacklightDisplay, 2);
OSMetaClassDefineReservedUnused(IOBacklightDisplay, 3);
OSMetaClassDefineReservedUnused(IOBacklightDisplay, 4);
OSMetaClassDefineReservedUnused(IOBacklightDisplay, 5);
OSMetaClassDefineReservedUnused(IOBacklightDisplay, 6);
OSMetaClassDefineReservedUnused(IOBacklightDisplay, 7);
OSMetaClassDefineReservedUnused(IOBacklightDisplay, 8);
OSMetaClassDefineReservedUnused(IOBacklightDisplay, 9);

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

class AppleBacklightDisplay : public IOBacklightDisplay
{
    OSDeclareDefaultStructors(AppleBacklightDisplay)

protected:
    // User preferred brightness level
//    SInt32      fCurrentUserBrightness;
    UInt32      fCurrentPowerState;
    SInt32      fMinBrightness;
    SInt32      fMaxBrightness;

    IOInterruptEventSource * fDeferredEvents;

    IOTimerEventSource *     fFadeTimer;
	AbsoluteTime		     fFadeDeadline;
	AbsoluteTime		     fFadeInterval;
	uint32_t				 fFadeState;
	uint32_t				 fFadeStateEnd;
	uint32_t				 fFadeStateFadeMin;
	uint32_t				 fFadeStateFadeMax;
	uint32_t				 fFadeStateFadeDelta;
    uint8_t                  fClamshellSlept;
	uint8_t					 fDisplayDims;
	uint8_t					 fProviderPower;
	uint8_t					 fFadeBacklight;
	uint8_t					 fFadeGamma;
	uint8_t					 fFadeDown;
	uint8_t					 fFadeAbort;

public:

    // IOService overrides
    virtual IOService * probe( IOService *, SInt32 * ) APPLE_KEXT_OVERRIDE;
    virtual bool start( IOService * provider ) APPLE_KEXT_OVERRIDE;
    virtual void stop( IOService * provider ) APPLE_KEXT_OVERRIDE;

    virtual IOReturn setPowerState( unsigned long, IOService * ) APPLE_KEXT_OVERRIDE;
    virtual unsigned long maxCapabilityForDomainState( IOPMPowerFlags ) APPLE_KEXT_OVERRIDE;
    virtual unsigned long initialPowerStateForDomainState( IOPMPowerFlags ) APPLE_KEXT_OVERRIDE;
    virtual unsigned long powerStateForDomainState( IOPMPowerFlags ) APPLE_KEXT_OVERRIDE;

    // IODisplay overrides
    virtual void initPowerManagement( IOService * ) APPLE_KEXT_OVERRIDE;
    virtual bool doIntegerSet( OSDictionary * params,
                               const OSSymbol * paramName, UInt32 value ) APPLE_KEXT_OVERRIDE;
    virtual bool doUpdate( void ) APPLE_KEXT_OVERRIDE;
    virtual void makeDisplayUsable( void ) APPLE_KEXT_OVERRIDE;
    virtual IOReturn framebufferEvent( IOFramebuffer * framebuffer,
                                       IOIndex event, void * info ) APPLE_KEXT_OVERRIDE;

private:
	bool updatePowerParam(void);
    void handlePMSettingCallback(const OSSymbol *, OSObject *, uintptr_t);
    static void _deferredEvent( OSObject * target,
                                IOInterruptEventSource * evtSrc, int intCount );
    void fadeAbort(void);
    void fadeWork(IOTimerEventSource * sender);
};

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

#undef super
#define super IOBacklightDisplay

OSDefineMetaClassAndStructors(AppleBacklightDisplay, IOBacklightDisplay)

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

//#define kIOBacklightUserBrightnessKey   "IOBacklightUserBrightness"
#define fPowerUsesBrightness	fMaxBrightness

#define IOG_FADE 1

enum 
{
    // fFadeState
    kFadeIdle        = (1 << 24),
    kFadePostDelay   = (2 << 24),
    kFadeUpdatePower = (3 << 24),

	kFadeDimLevel    = ((uint32_t) (0.75f * 1024)),
	kFadeMidLevel    = ((uint32_t) (0.50f * 1024)),
};

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// probe
//

IOService * AppleBacklightDisplay::probe( IOService * provider, SInt32 * score )
{
    ABL_START(probe,0,0,0);
    IOFramebuffer *     framebuffer;
    IOService *         ret = 0;
    UInt32              displayType;
    uintptr_t           connectFlags;

    do
    {
        if (!gIOFBHaveBacklight)
            continue;

        if (!super::probe(provider, score))
            continue;

        framebuffer = (IOFramebuffer *) getConnection()->getFramebuffer();

        FB_START(getConnectionCount,0,__LINE__,0);
        IOItemCount fbCount = framebuffer->getConnectionCount();
        FB_END(getConnectionCount,0,__LINE__,fbCount);
        for (IOItemCount idx = 0; idx < fbCount; idx++)
        {
            FB_START(getAttributeForConnection,kConnectionFlags,__LINE__,0);
            IOReturn err = framebuffer->getAttributeForConnection(idx, kConnectionFlags, &connectFlags);
            FB_END(getAttributeForConnection,err,__LINE__,connectFlags);
            if (kIOReturnSuccess != err)
                continue;
            if (0 == (kIOConnectionBuiltIn & connectFlags))
                continue;
            FB_START(getAppleSense,0,__LINE__,0);
            IOReturn error = framebuffer->getAppleSense(idx, NULL, NULL, NULL, &displayType);
            FB_END(getAppleSense,error,__LINE__,0);
            if (kIOReturnSuccess != error)
                continue;
            if ((kPanelTFTConnect != displayType)
                    && (kGenericLCD != displayType)
                    && (kPanelFSTNConnect != displayType))
                continue;

            ret = this;         // yes, we will control the panel
            break;
        }
    }
    while (false);

    ABL_END(probe,0,0,0);
    return (ret);
}

bool AppleBacklightDisplay::start( IOService * provider )
{
    ABL_START(start,0,0,0);
    IOFramebuffer * fb;
    if (!super::start(provider))
    {
        ABL_END(start,false,0,0);
        return (false);
    }

	fClamshellSlept = gIOFBCurrentClamshellState;
    fb = getConnection()->getFramebuffer();

    fDeferredEvents = IOInterruptEventSource::interruptEventSource(this, _deferredEvent);
    if (fDeferredEvents) fb->getControllerWorkLoop()->addEventSource(fDeferredEvents);

	fFadeTimer = IOTimerEventSource::timerEventSource(this, 
								OSMemberFunctionCast(IOTimerEventSource::Action, this, 
													&AppleBacklightDisplay::fadeWork));
    if (fFadeTimer) fb->getControllerWorkLoop()->addEventSource(fFadeTimer);

	fFadeState = kFadeIdle;

    fb->setProperty(kIOFBBuiltInKey, this, 0);

    ABL_END(start,true,0,0);
    return (true);
}

void AppleBacklightDisplay::stop( IOService * provider )
{
    ABL_START(stop,0,0,0);
    if (fDeferredEvents)
    {
        getConnection()->getFramebuffer()->getControllerWorkLoop()->removeEventSource(fDeferredEvents);
        fDeferredEvents->release();
        fDeferredEvents = 0;
    }

    fadeAbort();
    if (fFadeTimer)
    {
        // Don't want to be racing any notification events when we stop.  Should never be the case,
        // but doesn't hurt to protect ourselves.
        IOFramebuffer * framebuffer = (IOFramebuffer *) getConnection()->getFramebuffer();
        if (framebuffer)
            framebuffer->fbLock();

        fFadeTimer->disable();
        if (framebuffer)
            framebuffer->getControllerWorkLoop()->removeEventSource(fFadeTimer);
        fFadeTimer->release();
	    fFadeTimer = 0;

        if (framebuffer)
            framebuffer->fbUnlock();
    }

    super::stop(provider);
    ABL_END(stop,0,0,0);
    return;
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// initForPM
//
// This method overrides the one in IODisplay to do PowerBook-only
// power management of the display.

void AppleBacklightDisplay::initPowerManagement( IOService * provider )
{
    ABL_START(initPowerManagement,0,0,0);
    OSDictionary * displayParams;
	OSObject *     obj;
    OSNumber *     num;

    static const IOPMPowerState ourPowerStates[kIODisplayNumPowerStates] = {
        // version,
        //   capabilityFlags,      outputPowerCharacter, inputPowerRequirement,
        { 1, 0,                                     0, 0,           0,0,0,0,0,0,0,0 },
        { 1, 0,                                     0, 0,           0,0,0,0,0,0,0,0 },
        { 1, IOPMDeviceUsable,                      0, kIOPMPowerOn, 0,0,0,0,0,0,0,0 },
        { 1, IOPMDeviceUsable | IOPMMaxPerformance, 0, kIOPMPowerOn, 0,0,0,0,0,0,0,0 }
        // staticPower, unbudgetedPower, powerToAttain, timeToAttain, settleUpTime,
        // timeToLower, settleDownTime, powerDomainBudget
    };

    // Check initial state of "DisplaySleepUsesDim"
    obj = getPMRootDomain()->copyPMSetting(
                    const_cast<OSSymbol *>(gIOFBPMSettingDisplaySleepUsesDimKey));
    fDisplayDims = (!(num = OSDynamicCast(OSNumber, obj)) || (num->unsigned32BitValue()));
    if (obj) obj->release();

	fCurrentPowerState = -1U;

    OSObject *paramProp = copyProperty(gIODisplayParametersKey);
    displayParams = OSDynamicCast(OSDictionary, paramProp);
    if (displayParams)
    {
		SInt32 value, min, max;

        if (getIntegerRange(displayParams, gIODisplayPowerStateKey,
                             &value, &min, &max))
        {
        }
		else
		{
			fMinBrightness = 0;
			fMaxBrightness = 255;
		}
#if IOG_FADE
		OSDictionary * newParams;
		newParams = OSDictionary::withDictionary(displayParams);
		addParameter(newParams, gIODisplayFadeTime1Key, 0, 10000);
		setParameter(newParams, gIODisplayFadeTime1Key, 500);
		addParameter(newParams, gIODisplayFadeTime2Key, 0, 10000);
		setParameter(newParams, gIODisplayFadeTime2Key, 4000);
		addParameter(newParams, gIODisplayFadeTime3Key, 0, 10000);
		setParameter(newParams, gIODisplayFadeTime3Key, 500);
		addParameter(newParams, gIODisplayFadeStyleKey, 0, 10);
		setParameter(newParams, gIODisplayFadeStyleKey, 0);
		setProperty(gIODisplayParametersKey, newParams);
		newParams->release();
#endif
    }
    OSSafeReleaseNULL(paramProp);

    // initialize superclass variables
    PMinit();
    // attach into the power management hierarchy
    provider->joinPMtree(this);

    // register ourselves with policy-maker (us)
    registerPowerDriver(this, (IOPMPowerState *) ourPowerStates, kIODisplayNumPowerStates);

    ABL_END(initPowerManagement,0,0,0);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// setPowerState
//

//#define DEBGFADE(fmt, args...) do { kprintf(fmt, ## args); } while(false)          
#define DEBGFADE(fmt, args...) do {} while(false)          

IOReturn AppleBacklightDisplay::setPowerState( unsigned long powerState, IOService * whatDevice )
{
    IOG_KTRACE(DBG_IOG_SET_POWER_STATE,
               DBG_FUNC_NONE,
               kGMETRICS_DOMAIN_BACKLIGHT | kGMETRICS_DOMAIN_POWER,
               powerState,
               0, DBG_IOG_SOURCE_APPLEBACKLIGHTDISPLAY,
               0, 0,
               0, 0);
    ABL_START(setPowerState,powerState,0,0);

    IOReturn ret = IOPMAckImplied;
    UInt32   fromPowerState;

    if (powerState >= kIODisplayNumPowerStates)
    {
        ABL_END(setPowerState,IOPMAckImplied,__LINE__,0);
        return (IOPMAckImplied);
    }

    IOFramebuffer * framebuffer = (IOFramebuffer *) getConnection()->getFramebuffer();

    framebuffer->fbLock();

    if (isInactive() || (fCurrentPowerState == powerState))
    {
        framebuffer->fbUnlock();
        ABL_END(setPowerState,IOPMAckImplied,__LINE__,0);
        return (IOPMAckImplied);
    }
    fromPowerState = fCurrentPowerState;
    fCurrentPowerState = static_cast<UInt32>(powerState);
	if (fCurrentPowerState) fProviderPower = true;

	OSObject * obj;
	if ((!powerState) && (obj = copyProperty(kIOHibernatePreviewActiveKey, gIOServicePlane)))
	{
		obj->release();
	}
	else
	{
#if !IOG_FADE
        updatePowerParam();
#else /* IOG_FADE */
        SInt32         current, min, max, steps;
		uint32_t       fadeTime;
		uint32_t       dimFade;
		bool           doFadeDown, doFadeGamma, doFadeBacklight;

		fadeTime        = 0;
	    doFadeDown      = true;
	    doFadeGamma     = false;
	    doFadeBacklight = true;

		DEBGFADE("AppleBacklight: ps [%d->%ld]\n", fromPowerState, powerState);

        OSObject *paramProp = copyProperty(gIODisplayParametersKey);
        OSDictionary *displayParams = OSDynamicCast(OSDictionary, paramProp);
        bool haveRanges = displayParams
			&& getIntegerRange(displayParams, gIODisplayBrightnessFadeKey,
                    /* value */ NULL, &min, &max)
			&& getIntegerRange(displayParams, gIODisplayBrightnessKey,
                    /* value */ &current, /* min */ NULL, /* max */ NULL)
            && current;
        OSSafeReleaseNULL(paramProp);

		if (gIOGFades && haveRanges && !fFadeAbort)
		{
			if (current < ((kFadeMidLevel * max) / 1024)) dimFade = max;
			else                                          dimFade = (kFadeDimLevel * max) / 1024;

			if (-1U == fromPowerState) { /* boot */ }
			else if ((powerState > fromPowerState) && (1 >= fromPowerState))
			{
				// fade up from off
				fadeTime    = gIODisplayFadeTime3*1000;
				doFadeGamma = true;
				doFadeDown  = false;
			}
			if ((3 == powerState) && (1 >= fromPowerState))
			{
				// fade up from off
				fadeTime   = gIODisplayFadeTime3*1000;
				doFadeDown = false;
			}
			if ((3 == powerState) && (2 == fromPowerState))
			{
				// fade up from dim
				fadeTime   = gIODisplayFadeTime3*1000;
				max        = dimFade;
				doFadeDown = false;
			}
			else if ((0 == powerState) && (3 == fromPowerState))
			{
				// user initiated -> off
				fadeTime    = gIODisplayFadeTime1*1000;
				doFadeGamma = true;
			}
			else if (1 != gIODisplayFadeStyle)
			{
				 if ((2 == powerState) && (3 == fromPowerState))
				 {
					  if (fDisplayDims)
					  {
						 // idle initiated -> dim
						 fadeTime = gIODisplayFadeTime1*1000;
						 max      = dimFade;
					  }
				 }
				 if ((1 == powerState) && (2 == fromPowerState))
				 {
					 // idle initiated -> off
					 fadeTime    = gIODisplayFadeTime1*1000;
					 doFadeBacklight = (dimFade != static_cast<uint32_t>(max));
					 if (doFadeBacklight) current = max - dimFade;
					 doFadeGamma = true;
				 }
			}
			else if (1 == gIODisplayFadeStyle)
			{
				 if ((2 == powerState) && (3 == fromPowerState))
				 {
					 // idle initiated
					 fCurrentPowerState = 1; 
					 fadeTime           = gIODisplayFadeTime2*1000;
					 doFadeGamma        = true;
				 };
			}
			DEBGFADE("AppleBacklight: fadeTime %d style %d abort %d\n", fadeTime, gIODisplayFadeStyle, fFadeAbort);
		}

		if (fadeTime)
		{
			steps = fadeTime / 16667;

			DEBGFADE("AppleBacklight: p %d -> %ld, c %d -> m %d\n", fromPowerState, powerState, current, max);

			if (current > max)   current = max;

			fFadeStateFadeMin   = max - current;
			fFadeStateFadeMax   = max;
			fFadeStateFadeDelta = current;
			if (static_cast<uint32_t>(steps) > fFadeStateFadeDelta)
                steps = static_cast<SInt32>(fFadeStateFadeDelta);

			fFadeDown      = doFadeDown;
			fFadeGamma     = doFadeGamma;
			fFadeBacklight = doFadeBacklight;

			DEBGFADE("AppleBacklight:  %d -> %d\n", fFadeStateFadeMin, fFadeStateFadeDelta);

			fFadeState      = 0;
			if (!fFadeDown) updatePowerParam();

			fFadeDeadline = mach_absolute_time();
			fadeTime     /= steps;
			fFadeStateEnd = (steps - 1);
            // <rdar://problem/30194186> PanicTracer: 486 panics at com.apple.iokit.IOGraphicsFamily : AppleBacklightDisplay::fadeWork :: com.apple.iokit.IOGraphicsFamily : AppleBacklightDisplay::setPowerState
            //   Under hard to determine circumstances the fFadeStateEnd can be
            //   0. The correct fix is to not even start fading in that
            //   circumstance but that is too dangerous for a narrow bug fix.
            //   Just turn off all fading and let the engine run to completion.
            if (!fFadeStateEnd)
                fFadeBacklight = fFadeGamma = false;
		    if (framebuffer->isWakingFromHibernateGfxOn())
                fFadeState = fFadeStateEnd;
			clock_interval_to_absolutetime_interval(fadeTime, kMicrosecondScale, &fFadeInterval);
			fadeWork(fFadeTimer);
			if (fFadeDown)  ret = 20 * 1000 * 1000;
		}
		else
		{
			updatePowerParam();
			fFadeAbort = false;
		}
#endif
	}

    framebuffer->fbUnlock();

    ABL_END(setPowerState,ret,0,0);
    return (ret);
}

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

void AppleBacklightDisplay::fadeAbort(void)
{
    ABL_START(fadeAbort,0,0,0);
	if (kFadeIdle == fFadeState)
    {
        ABL_END(fadeAbort,0,__LINE__,0);
        return;
    }

    OSObject *paramProp = copyProperty(gIODisplayParametersKey);
    OSDictionary *displayParams = OSDynamicCast(OSDictionary, paramProp);
    if (!displayParams) goto bail;

    // abort
    DEBGFADE("AppleBacklight: fadeAbort\n");
    doIntegerSet(displayParams, gIODisplayBrightnessFadeKey, 0);
    if (fFadeGamma)
    {
        doIntegerSet(displayParams, gIODisplayGammaScaleKey, 65536);
        doIntegerSet(displayParams, gIODisplayParametersFlushKey, 0);
    }
    if (fFadeDown)
    {
        IOG_KTRACE(DBG_IOG_ACK_POWER_STATE,
                   DBG_FUNC_NONE,
                   kGMETRICS_DOMAIN_BACKLIGHT | kGMETRICS_DOMAIN_POWER,
                   DBG_IOG_SOURCE_IODISPLAY,
                   0, 0,
                   0, 0,
                   0, 0);
        acknowledgeSetPowerState();
    }
    fFadeState = kFadeIdle;

bail:
    OSSafeReleaseNULL(paramProp);
    ABL_END(fadeAbort,0,0,0);
}

void AppleBacklightDisplay::fadeWork(IOTimerEventSource * sender)
{
    ABL_START(fadeWork,0,0,0);
	SInt32 fade, gamma, point;
    
	DEBGFADE("AppleBacklight: fadeWork(fFadeStateEnd %d, fFadeState %d, %d, fFadeStateEnd %d)\n", 
			fFadeStateEnd, fFadeState & 0xffff, fFadeState >> 24, fFadeStateEnd);

	if (kFadeIdle == fFadeState)
    {
        ABL_END(fadeWork,0,__LINE__,0);
        return;
    }

    OSObject *paramProp = copyProperty(gIODisplayParametersKey);
    OSDictionary *displayParams = OSDynamicCast(OSDictionary, paramProp);
	if (!displayParams) goto bail;

    fFadeAbort = (fFadeDown && !fDisplayPMVars->displayIdle);

	if (fFadeAbort) fadeAbort();
	else if (fFadeState <= fFadeStateEnd)
    {
        if ((kIOWSAA_DeferStart != fWSAADeferState) || fFadeDown)
        {
            point = fFadeState;
            if (!fFadeDown) point = (fFadeStateEnd - point);
            if (fFadeBacklight)
            {
                if (!fFadeDown && !point) fade = 0;
                else if (fFadeStateFadeMax > 0x8000)
                {
                    fade = fFadeDown ? fFadeStateFadeMax : 0;
                    fFadeBacklight = false;
                }
                else
                {
                    fade = ((point * fFadeStateFadeDelta) / fFadeStateEnd);
                    fade = fFadeStateFadeMin + fade;
                }
                DEBGFADE("AppleBacklight: backlight: %d\n", fade);
                doIntegerSet(displayParams, gIODisplayBrightnessFadeKey, fade);
            }
            if (fFadeGamma)
            {
                gamma = 65536 - ((point * 65536) / fFadeStateEnd);
                DEBGFADE("AppleBacklight: gamma: %d\n", gamma);
                doIntegerSet(displayParams, gIODisplayGammaScaleKey, gamma);
                doIntegerSet(displayParams, gIODisplayParametersFlushKey, 0);
            }

            fFadeState++;
            if (fFadeState > fFadeStateEnd)
            {
                if (fFadeDown) updatePowerParam();
                fFadeState = kFadePostDelay;
                clock_interval_to_absolutetime_interval(500, kMillisecondScale, &fFadeInterval);
            }
        }
	}
	else if (kFadePostDelay == fFadeState)
	{
		fFadeState = kFadeIdle;
	}

	if (kFadeIdle == fFadeState)
	{
	    DEBGFADE("AppleBacklight: fadeWork ack\n");
        if (fFadeDown)
        {
            IOG_KTRACE(DBG_IOG_ACK_POWER_STATE,
                       DBG_FUNC_NONE,
                       kGMETRICS_DOMAIN_BACKLIGHT | kGMETRICS_DOMAIN_POWER,
                       DBG_IOG_SOURCE_IODISPLAY,
                       0, 0,
                       0, 0,
                       0, 0);
            acknowledgeSetPowerState();
        }
	}
	else
	{
		ADD_ABSOLUTETIME(&fFadeDeadline, &fFadeInterval);
		fFadeTimer->wakeAtTime(fFadeDeadline);
	}

bail:
    OSSafeReleaseNULL(paramProp);

    ABL_END(fadeWork,0,0,0);
}

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

void AppleBacklightDisplay::makeDisplayUsable( void )
{
    ABL_START(makeDisplayUsable,0,0,0);
    if (kIODisplayMaxPowerState == fCurrentPowerState)
        setPowerState(fCurrentPowerState, this);
    super::makeDisplayUsable();
    ABL_END(makeDisplayUsable,0,0,0);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// maxCapabilityForDomainState
//
// This simple device needs only power.  If the power domain is supplying
// power, the display can go to its highest state.  If there is no power
// it can only be in its lowest state, which is off.

unsigned long AppleBacklightDisplay::maxCapabilityForDomainState( IOPMPowerFlags domainState )
{
    ABL_START(maxCapabilityForDomainState,domainState,0,0);
    unsigned long   ret = 0;
    if (domainState & IOPMPowerOn)
        ret = (kIODisplayMaxPowerState);
    ABL_END(maxCapabilityForDomainState,ret,0,0);
    return (ret);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// initialPowerStateForDomainState
//
// The power domain may be changing state.  If power is on in the new
// state, that will not affect our state at all.  If domain power is off,
// we can attain only our lowest state, which is off.

unsigned long AppleBacklightDisplay::initialPowerStateForDomainState( IOPMPowerFlags domainState )
{
    ABL_START(initialPowerStateForDomainState,domainState,0,0);
    unsigned long   ret = 0;
    if (domainState & IOPMPowerOn)
        ret = (kIODisplayMaxPowerState);
    ABL_END(initialPowerStateForDomainState,ret,0,0);
    return (ret);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// powerStateForDomainState
//
// The power domain may be changing state.  If power is on in the new
// state, that will not affect our state at all.  If domain power is off,
// we can attain only our lowest state, which is off.

unsigned long AppleBacklightDisplay::powerStateForDomainState( IOPMPowerFlags domainState )
{
    ABL_START(powerStateForDomainState,domainState,0,0);
    unsigned long   ret = 0;
    if (domainState & IOPMPowerOn)
        ret = (kIODisplayMaxPowerState);
    ABL_END(powerStateForDomainState,ret,0,0);
    return (ret);
}

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

// Alter the backlight brightness by user request.

bool AppleBacklightDisplay::doIntegerSet( OSDictionary * params,
                                       const OSSymbol * paramName, UInt32 value )
{
    ABL_START(doIntegerSet,value,0,0);
    if ((paramName == gIODisplayParametersCommitKey)
        && (kIODisplayMaxPowerState != fCurrentPowerState))
    {
        ABL_END(doIntegerSet,true,0,0);
        return (true);
    }

    if (paramName == gIODisplayBrightnessKey)
    {
#if 0
        fCurrentUserBrightness = value;
        getPMRootDomain()->setProperty(kIOBacklightUserBrightnessKey, fCurrentUserBrightness, 32);
#endif
        if (fPowerUsesBrightness && fClamshellSlept)
			value = 0;
    }

    bool b = super::doIntegerSet(params, paramName, value);
    ABL_END(doIntegerSet,b,0,0);
	return (b);
}

bool AppleBacklightDisplay::doUpdate( void )
{
    ABL_START(doUpdate,0,0,0);
    bool ok;

    ok = super::doUpdate();

#if 0
    OSObject *paramProp = copyProperty(gIODisplayParametersKey);
    OSDictionary *displayParams = OSDynamicCast(OSDictionary, paramProp);
    if (fDisplayPMVars && displayParams);
    {
        setParameter(displayParams, gIODisplayBrightnessKey, fCurrentUserBrightness);	
    }

    OSSafeReleaseNULL(paramProp);
#endif

    ABL_END(doUpdate,ok,0,0);
    return (ok);
}

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

bool AppleBacklightDisplay::updatePowerParam(void)
{
    ABL_START(updatePowerParam,0,0,0);
	SInt32 		   value, current;
	bool	       ret;

	DEBG1("B", " fProviderPower %d, fClamshellSlept %d, fCurrentPowerState %d\n", 
			fProviderPower, fClamshellSlept, fCurrentPowerState);

	if (!fProviderPower)
    {
        ABL_END(updatePowerParam,false,__LINE__,0);
        return (false);
    }
	if (!fCurrentPowerState) fProviderPower = false;

    OSObject *paramProp = copyProperty(gIODisplayParametersKey);
    OSDictionary *displayParams = OSDynamicCast(OSDictionary, paramProp);
    if (!displayParams) {
        OSSafeReleaseNULL(paramProp);
        ABL_END(updatePowerParam,false,__LINE__,0);
        return (false);
    }

	value = fClamshellSlept ? 0 : fCurrentPowerState;

	if (fPowerUsesBrightness)
	{
		switch (value)
		{
			case 0:
				value = 0;
				break;
			case 1:
				value = fMinBrightness;
				break;
			case 2:
				value = fDisplayDims ? (fMinBrightness + 1) : fMaxBrightness;
				break;
			case 3:
				value = fMaxBrightness;
				break;
		}
        if (getIntegerRange(displayParams, gIODisplayBrightnessKey, &current, NULL, NULL)
		  && (value > current))
			value = current;
		//if(gIOFBSystemPower)
		if (value <= fMinBrightness)
			value = 0;
		ret = super::doIntegerSet(displayParams, gIODisplayBrightnessKey, value);
	}
	else
	{
		switch (value)
		{
			case 0:
				value = kIODisplayPowerStateOff;
				break;
			case 1:
				value = kIODisplayPowerStateOff;
				break;
			case 2:
				value = (fDisplayDims && (kFadeIdle == fFadeState)) ? kIODisplayPowerStateMinUsable : kIODisplayPowerStateOn;
				break;
			case 3:
				value = kIODisplayPowerStateOn;
				break;
		}
		DEBG1("B", " dsyp %d\n", value);
		ret = super::doIntegerSet(displayParams, gIODisplayPowerStateKey, value);
#if IOG_FADE2
        if (kIODisplayPowerStateOff == value) IOSleep(700);
#endif
	}

    displayParams->release();

    ABL_END(updatePowerParam,ret,0,0);
    return (ret);
}

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

void AppleBacklightDisplay::_deferredEvent( OSObject * target,
        IOInterruptEventSource * evtSrc, int intCount )
{
    ABL_START(_deferredEvent,intCount,0,0);
    AppleBacklightDisplay * abd = (AppleBacklightDisplay *) target;

	abd->updatePowerParam();
    ABL_END(_deferredEvent,0,0,0);
}

IOReturn AppleBacklightDisplay::framebufferEvent( IOFramebuffer * framebuffer,
        IOIndex event, void * info )
{
    ABL_START(framebufferEvent,event,0,0);
    UInt8 newValue;

    switch(event)
    {
        case kIOFBNotifyDidWake:
            if (info)
            {
                fProviderPower = true;
                //		fCurrentPowerState = kIODisplayMaxPowerState;
                //		updatePowerParam();
            }
            break;
        case kIOFBNotifyClamshellChange:
            if (kOSBooleanTrue == info)
            {
#if LCM_HWSLEEP
                fConnection->getFramebuffer()->changePowerStateTo(0);
#endif
                fClamshellSlept = true;
            }
            else if (fClamshellSlept)
            {
                fClamshellSlept = false;
            }
            // may be in the right power state already, but wrong brightness because
            // of the clamshell state at setPowerState time.
            if (fDeferredEvents)
                fDeferredEvents->interruptOccurred(0, 0, 0);
            break;
        case kIOFBNotifyProbed:
            if (fDeferredEvents)
                fDeferredEvents->interruptOccurred(0, 0, 0);
            break;
        case kIOFBNotifyDisplayDimsChange:
            newValue = (info != NULL);
            if (newValue != fDisplayDims)
            {
                fDisplayDims = newValue;
                if (fDeferredEvents)
                    fDeferredEvents->interruptOccurred(0, 0, 0);
            }
            break;
        case kIOFBNotifyWSAAWillEnterDefer:
            DEBGFADE("AppleBacklight: disable fadeWork\n");
            /* <rdar://problem/28283410> J80G ; EVT Sleep-Wake Bright Flash */
            // If we haven't started a fade as indicated by fFadeState == kFadeIdle and the WSAA state is the deferred start state,
            // disable the fade timer, else too bad, roll on with fade.  This means we started a fade prior to the deferred enter state notification and have no choice but to complete it.
            if (fFadeTimer && (kFadeIdle == fFadeState))
                fFadeTimer->disable();
            break;
        case kIOFBNotifyWSAADidExitDefer:
            DEBGFADE("AppleBacklight: enable fadeWork\n");
            if (fFadeTimer)
                fFadeTimer->enable();
            break;
        default:
            // defer to super
            break;
    }

    IOReturn err = super::framebufferEvent( framebuffer, event, info );
    ABL_END(framebufferEvent,err,0,0);
    return (err);
}