Source to iokit/Families/IONDRVSupport/IONDRVFramebuffer.cpp


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

/*
 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Copyright (c) 1997-1998 Apple Computer, Inc.
 *
 *
 * HISTORY
 *
 * sdouglas  22 Oct 97 - first checked in.
 * sdouglas  24 Jul 98 - start IOKit.
 * sdouglas  15 Dec 98 - cpp.
 *
 */

#include <IOKit/IOLib.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOLocks.h>
#include <IOKit/ndrvsupport/IONDRVFramebuffer.h>
#include <IOKit/assert.h>

#include <libkern/c++/OSContainers.h>

#include "IONDRV.h"

#include <string.h>

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

class IOATINDRV : public IONDRVFramebuffer
{
    OSDeclareDefaultStructors(IOATINDRV)

public:
    virtual IOReturn getStartupDisplayMode( IODisplayModeID * displayMode,
                            IOIndex * depth );
    virtual IODeviceMemory * findVRAM( void );

};

class IOATI128NDRV : public IOATINDRV
{
    OSDeclareDefaultStructors(IOATI128NDRV)

public:
    virtual void flushCursor( void );
};

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

struct _VSLService {
    class IONDRVFramebuffer *	framebuffer;
    IOSelect			type;
    IOFBInterruptProc  		handler;
    OSObject *			target;
    void *			ref;
    _VSLService *		next;
};

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

// frame buffer has two power states, off and on
#define number_of_power_states 2

static IOPMPowerState ourPowerStates[number_of_power_states] = {
  {1,0,0,0,0,0,0,0,0,0,0,0},
  {1,IOPMDeviceUsable,IOPMPowerOn,IOPMPowerOn,0,0,0,0,0,0,0,0}
};

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

#define super IOFramebuffer

OSDefineMetaClassAndStructors(IONDRVFramebuffer, IOFramebuffer)

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

//============
//= External =
//============

IOService * IONDRVFramebuffer::probe( IOService * 	provider,
                                        SInt32 *	score )
{
    IOService *		inst = this;
    IOService *		newInst = 0;
    const char *	name;

    if( !super::probe( provider, score ))
	return( 0 );

    if( IONDRV::fromRegistryEntry( provider )) {

        // temporary for in-kernel acceleration
        name = provider->getName();
        if( 0 == strncmp("ATY,Rage128", name, strlen("ATY,Rage128")))
            newInst = new IOATI128NDRV;
        else if( 0 == strncmp("ATY,", name, strlen("ATY,")))
            newInst = new IOATINDRV;

	if( newInst) {
            if( ! newInst->init( inst->getPropertyTable())) {
                newInst->release();
                newInst = 0;
            }
	    inst = newInst;
	}
    } else
	inst = 0;

    return( inst );
}

bool IONDRVFramebuffer::start( IOService * provider )
{
    bool   		ok = false;
    IOService *		parent;
    
    do {
   	nub = provider;
        ndrv = IONDRV::fromRegistryEntry( provider );
	if( 0 == ndrv)
	    continue;

	setName( ndrv->driverName());
	startAt8 = 3;
        consoleDevice = (0 != provider->getProperty("AAPL,boot-display"));

        if( 0 == nub->getDeviceMemoryCount()) {
            parent = OSDynamicCast( IOService, nub->getParentEntry(gIODTPlane));
            if( parent) {
                parent->getResources();
                OSArray * array = parent->getDeviceMemory();
                array->retain();
                nub->setDeviceMemory( array);
                array->release();
            }
        }

        if( false == super::start( nub ))
	    continue;

	ok = true;			// Success

    } while( false);
    
    return( ok);
}

bool IONDRVFramebuffer::isConsoleDevice( void )
{
    return( consoleDevice );
}

// osfmk/ppc/mappings.h
extern "C" { extern void ignore_zero_fault(boolean_t); }

IOReturn IONDRVFramebuffer::enableController( void )
{
    IOReturn		err;
    const char *	logname;
    
    logname = getProvider()->getName();

    if( 0 == strcmp( "control", logname))
        waitForService( resourceMatching( "IOiic0" ));

    err = IONDRVLibrariesInitialize( getProvider() );

    if( kIOReturnSuccess == err) do {

        ignore_zero_fault( true );
	err = checkDriver();
        ignore_zero_fault( false );

        if( err) {
            IOLog("%s: Not usable\n", logname );
            if( err == -999)
                IOLog("%s: driver incompatible.\n", logname );
            continue;
        }
        getCurrentConfiguration();
        vramMemory = findVRAM();

    } while( false);

    // initialize power management of the device
    initForPM();
    
    return( err);
}

IODeviceMemory * IONDRVFramebuffer::getVRAMRange( void )
{
    if( vramMemory)
	vramMemory->retain();

    return( vramMemory );
}

void IONDRVFramebuffer::free( void )
{
    super::free();
}

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

IOReturn IONDRVFramebuffer::registerForInterruptType( IOSelect interruptType,
                        IOFBInterruptProc proc, OSObject * target, void * ref,
			void ** interruptRef )

{
    _VSLService *	service;
    IOReturn		err;

    if( (interruptType == kIOFBVBLInterruptType)
        && (getProvider()->getProperty("Ignore VBL")))
	return( kIOReturnUnsupported );

    for( service = vslServices;
	 service && (service->type != interruptType);
	 service = service->next ) {}

    if( service) {

	if( service->handler)
	    err = kIOReturnBusy;

	else {
	    service->target	= target;
	    service->ref	= ref;
	    service->handler	= proc;
	    *interruptRef = service;
	    err = kIOReturnSuccess;
	}

    } else
	err = kIOReturnNoResources;

    return( err );
}

IOReturn IONDRVFramebuffer::unregisterInterrupt( void * interruptRef )
{
    _VSLService *	service = (_VSLService *) interruptRef;

    service->handler = 0;

    return( kIOReturnSuccess );
}

IOReturn IONDRVFramebuffer::setInterruptState( void * interruptRef, 
						UInt32 state )
{
    return( kIOReturnUnsupported );
}

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

//// VSL calls

OSStatus IONDRVFramebuffer::VSLNewInterruptService(
                                        void * entryID,
                                        IOSelect serviceType,
                                        _VSLService ** vslService )
{
    IORegistryEntry *	regEntry;
    IONDRVFramebuffer *	fb;
    _VSLService *	service;
    IOReturn		err = kIOReturnSuccess;

    REG_ENTRY_TO_OBJ( (const RegEntryID *) entryID, regEntry)

    fb = OSDynamicCast( IONDRVFramebuffer,
		regEntry->getChildEntry( gIOServicePlane ));
    assert( fb );

    if( fb) {
	service = IONew( _VSLService, 1 );

	if( service) {
            service->framebuffer	= fb;
            service->type		= serviceType;
	    service->handler		= 0;
            service->next = fb->vslServices;
            fb->vslServices = service;

            *vslService = service;

	} else
	    err = kIOReturnNoMemory;

    } else
	err = kIOReturnBadArgument;

    return( err );
}

OSStatus IONDRVFramebuffer::VSLDisposeInterruptService(_VSLService * vslService)
{
    IONDRVFramebuffer *	fb;
    _VSLService * 	next;
    _VSLService * 	prev;

    if( vslService) {

	fb = vslService->framebuffer;

        prev = fb->vslServices;
	if( prev == vslService)
	    fb->vslServices = vslService->next;
	else {
	    while( ((next = prev->next) != vslService) && next)
		prev = next;
	    if( next)
		prev->next = vslService->next;
	}

	IODelete( vslService, _VSLService, 1 );
    }

    return( kIOReturnSuccess );
}

OSStatus IONDRVFramebuffer::VSLDoInterruptService( _VSLService * vslService )
{
    IOFBInterruptProc	proc;

    if( vslService) {
	if( (proc = vslService->handler))
	    (*proc) (vslService->target, vslService->ref);
    }

    return( kIOReturnSuccess );
}

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

struct _VSLCursorRef {
    IOFramebuffer *	framebuffer;
    void * 		cursorImage;
};

Boolean IONDRVFramebuffer::VSLPrepareCursorForHardwareCursor(
                                        void * cursorRef,
                                        IOHardwareCursorDescriptor * hwDesc,
                                        IOHardwareCursorInfo * hwCursorInfo )
{
    _VSLCursorRef *	cursor = (_VSLCursorRef *) cursorRef;
    bool		ok;

    if( hwCursorInfo->colorMap)
        hwCursorInfo->colorMap += 1;
    ok = cursor->framebuffer->convertCursorImage(
		cursor->cursorImage, hwDesc, hwCursorInfo );
    if( hwCursorInfo->colorMap)
        hwCursorInfo->colorMap -= 1;

    return( ok );
}

IOReturn IONDRVFramebuffer::setCursorImage( void * cursorImage )
{
    _VSLCursorRef		cursorRef;
    VDSetHardwareCursorRec	setCursor;
    IOReturn			err;

    cursorRef.framebuffer = this;
    cursorRef.cursorImage = cursorImage;

    setCursor.csCursorRef = (void *) &cursorRef;
    setCursor.csReserved1 = 0;
    setCursor.csReserved2 = 0;

    err = doControl( cscSetHardwareCursor, &setCursor );

    return( err );
}

IOReturn IONDRVFramebuffer::setCursorState( SInt32 x, SInt32 y, bool visible )
{
    VDDrawHardwareCursorRec	drawCursor;
    IOReturn			err;

    if( 0 == OSIncrementAtomic( &ndrvEnter))
    {

        drawCursor.csCursorX 	= x;
        drawCursor.csCursorY 	= y;
        drawCursor.csCursorVisible 	= visible;
        drawCursor.csReserved1 	= 0;
        drawCursor.csReserved2 	= 0;

        err = doControl( cscDrawHardwareCursor, &drawCursor );

    } else
	err = kIOReturnBusy;

    OSDecrementAtomic( &ndrvEnter );

    return( err );
}

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

//============
//= Internal =
//============

IOReturn IONDRVFramebuffer::doControl( UInt32 code, void * params )
{
    IOReturn	err;
    CntrlParam	pb;

    if( ndrvState == 0)
	return( kIOReturnNotOpen);

    pb.qLink = 0;
    pb.csCode = code;
    pb.csParams = params;

    OSIncrementAtomic( &ndrvEnter );
    err = ndrv->doDriverIO( /*ID*/ (UInt32) &pb, &pb,
                            kControlCommand, kImmediateIOCommandKind );
    OSDecrementAtomic( &ndrvEnter );

    return( err);
}

IOReturn IONDRVFramebuffer::doStatus( UInt32 code, void * params )
{
    IOReturn	err;
    CntrlParam	pb;

    if( ndrvState == 0)
	return( kIOReturnNotOpen);

    pb.qLink = 0;
    pb.csCode = code;
    pb.csParams = params;

    OSIncrementAtomic( &ndrvEnter );
    err = ndrv->doDriverIO( /*ID*/ (UInt32) &pb, &pb,
                            kStatusCommand, kImmediateIOCommandKind );
    OSDecrementAtomic( &ndrvEnter );

    return( err);
}


IOReturn IONDRVFramebuffer::checkDriver( void )
{
    OSStatus			err = noErr;
    struct DriverInitInfo	initInfo;
    CntrlParam          	pb;
    VDClutBehavior		clutSetting;
    VDGammaRecord		gammaRec;
    VDSwitchInfoRec		switchInfo;
    IOTimingInformation 	info;
    VDPageInfo			pageInfo;

    if( ndrvState == 0) {
	do {
	    initInfo.refNum = 0xffcd;			// ...sure.
	    MAKE_REG_ENTRY(initInfo.deviceEntry, nub )
    
	    err = ndrv->doDriverIO( 0, &initInfo,
				kInitializeCommand, kImmediateIOCommandKind );
	    if( err) continue;

	    err = ndrv->doDriverIO( 0, &pb,
				kOpenCommand, kImmediateIOCommandKind );

	} while( false);

	if( err)
	    return( err);

        // allow calls to ndrv
        ndrvState = 1;

        if( (noErr == doStatus( cscGetCurMode, &switchInfo ))
            && (noErr == getTimingInfoForDisplayMode( switchInfo.csData, &info))
            && (timingApple_0x0_0hz_Offline == info.appleTimingID)) {

            IOLog("%s: display offline\n", getName());
            err = kIOReturnOffline;
            return( err);
        } else
            ndrvState = 2;

        // duplicate QD InitGDevice
        pageInfo.csMode = switchInfo.csMode;
        pageInfo.csData = 0;
        pageInfo.csPage = 0;
        doControl( cscGrayPage, &pageInfo);

        clutSetting = kSetClutAtSetEntries;
        lastClutSetting = clutSetting;
        doControl( cscSetClutBehavior, &clutSetting);

#if 1
	// bogus for ROM control
	do {

	    VDGetGammaListRec	scan;
	    VDRetrieveGammaRec	get;
	    GammaTbl *		table;
	    char		name[ 64 ];

	    scan.csPreviousGammaTableID = kGammaTableIDFindFirst;
	    scan.csGammaTableName = name;
	    err = doStatus( cscGetGammaInfoList, &scan);
	    if( err || (scan.csGammaTableID == kGammaTableIDNoMoreTables))
		continue;

	    table = (GammaTbl *)IOMalloc( scan.csGammaTableSize);
	    if( 0 == table)
		continue;
	    get.csGammaTableID = scan.csGammaTableID;
	    get.csGammaTablePtr = table;
	    
	    err = doStatus( cscRetrieveGammaTable, &get );
	    if( noErr == err) {
		kprintf("Setting gamma %s\n", scan.csGammaTableName);
		gammaRec.csGTable = (Ptr) table;
		doControl( cscSetGamma, &gammaRec );
	    }

	    IOFree( table, scan.csGammaTableSize);

	} while( false);
#endif
    }
    return( noErr);
}


UInt32 IONDRVFramebuffer::iterateAllModes( IODisplayModeID * displayModeIDs )
{
    VDResolutionInfoRec	info;
    UInt32		num = 0;

    info.csPreviousDisplayModeID = kDisplayModeIDFindFirstResolution;

    while( 
 	   (noErr == doStatus( cscGetNextResolution, &info))
	&& ((SInt32) info.csDisplayModeID > 0) ) {

	    if( displayModeIDs)
		displayModeIDs[ num ] = info.csDisplayModeID;

	    info.csPreviousDisplayModeID = info.csDisplayModeID;
	    num++;
    }
    return( num);
}


IOReturn IONDRVFramebuffer::getResInfoForMode( IODisplayModeID modeID,
				VDResolutionInfoRec ** theInfo )
{
    // unfortunately, there is no "kDisplayModeIDFindSpecific"

    if( (SInt32) modeID <= 0)
        return( kIOReturnUnsupportedMode);

    *theInfo = &cachedVDResolution;

    if( cachedVDResolution.csDisplayModeID == (UInt32) modeID)
	return( noErr);

    // try the next after cached mode
    cachedVDResolution.csPreviousDisplayModeID = cachedVDResolution.csDisplayModeID;

    if( (noErr == doStatus( cscGetNextResolution, &cachedVDResolution))
    && (cachedVDResolution.csDisplayModeID == (UInt32) modeID) )
	return( noErr);

    // full blown iterate
    cachedVDResolution.csPreviousDisplayModeID = kDisplayModeIDFindFirstResolution;

    while(
	(noErr == doStatus( cscGetNextResolution, &cachedVDResolution))
    && (cachedVDResolution.csDisplayModeID != (UInt32) modeID) 
    && ((SInt32) cachedVDResolution.csDisplayModeID > 0)) {

	cachedVDResolution.csPreviousDisplayModeID = cachedVDResolution.csDisplayModeID;
    }

    if( cachedVDResolution.csDisplayModeID == (UInt32) modeID)
	return( noErr);

    cachedVDResolution.csDisplayModeID = 0xffffffff;
    return( kIOReturnUnsupportedMode);
}

void IONDRVFramebuffer::getCurrentConfiguration( void )
{
    IOReturn		err;
    VDSwitchInfoRec	switchInfo;
    VDGrayRecord	grayRec;

    grayRec.csMode = 0;			// turn off luminance map
    err = doControl( cscSetGray, &grayRec );
    // driver refused => mono display
    grayMode = ((noErr == err) && (0 != grayRec.csMode));

    err = doStatus( cscGetCurMode, &switchInfo );
    if( err == noErr) {
        currentDisplayMode	= switchInfo.csData;
        currentDepth		= switchInfo.csMode - kDepthMode1;
        currentPage		= switchInfo.csPage;
	if( 0 == (physicalFramebuffer = pmap_extract( kernel_pmap,
		((vm_address_t) switchInfo.csBaseAddr) )))
	    physicalFramebuffer = (UInt32) switchInfo.csBaseAddr;
    } else
	IOLog("%s: cscGetCurMode failed\n", nub->getName());
}

IODeviceMemory * IONDRVFramebuffer::makeSubRange( 
	IOPhysicalAddress	start,
	IOPhysicalLength	length ) 
{
    IODeviceMemory *	mem = 0;
    UInt32		numMaps, i;
    IOService *		device;

    device = nub;
    numMaps = device->getDeviceMemoryCount();

    for( i = 0; (!mem) && (i < numMaps); i++) {
	mem = device->getDeviceMemoryWithIndex(i);
	if( !mem)
	    continue;
	mem = IODeviceMemory::withSubRange( mem,
			start - mem->getPhysicalAddress(), length );
    }
    if( !mem)
	mem = IODeviceMemory::withRange( start, length );

    return( mem );
}

IODeviceMemory * IONDRVFramebuffer::getApertureRange( IOPixelAperture aper )
{
    IOReturn			err;
    IOPixelInformation		info;
    IOByteCount			bytes;

    err = getPixelInformation( currentDisplayMode, currentDepth, aper,
                                &info );
    if( err)
	return( 0 );

    bytes = (info.bytesPerRow * info.activeHeight) + 128;

    return( makeSubRange( physicalFramebuffer, bytes ));
}

IODeviceMemory * IONDRVFramebuffer::findVRAM( void )
{
    VDVideoParametersInfoRec	pixelParams;
    VPBlock			pixelInfo;
    VDResolutionInfoRec		vdRes;
    UInt32			size;
    IOPhysicalAddress		vramBase;
    IOByteCount			vramLength;
    IOReturn			err;

    vramLength = 0;
    vdRes.csPreviousDisplayModeID = kDisplayModeIDFindFirstResolution;
    while(
        (noErr == doStatus( cscGetNextResolution, &vdRes))
    && ((SInt32) vdRes.csDisplayModeID > 0) )
    {
        pixelParams.csDisplayModeID = vdRes.csDisplayModeID;
        pixelParams.csDepthMode = vdRes.csMaxDepthMode;
        pixelParams.csVPBlockPtr = &pixelInfo;
        err = doStatus( cscGetVideoParameters, &pixelParams);
        if( err)
            continue;

        // Control hangs its framebuffer off the end of the aperture to support
        // 832 x 624 @ 32bpp. The commented out version will correctly calculate
        // the vram length, but DPS needs the full extent to be mapped, so we'll
        // end up mapping an extra page that will address vram through the
        // little endian aperture. No other drivers like this known.
#if 1
        size = 0x40 + pixelInfo.vpBounds.bottom *
			(pixelInfo.vpRowBytes & 0x7fff);
#else
        size = ( (pixelInfo.vpBounds.right * pixelInfo.vpPixelSize) / 8)	// last line
                + (pixelInfo.vpBounds.bottom - 1) *
		(pixelInfo.vpRowBytes & 0x7fff);
#endif
        if( size > vramLength)
            vramLength = size;

        vdRes.csPreviousDisplayModeID = vdRes.csDisplayModeID;
    }

    vramBase = physicalFramebuffer;
    vramLength = (vramLength + (vramBase & 0xffff) + 0xffff) & 0xffff0000;
    vramBase &= 0xffff0000;

    return( makeSubRange( vramBase, vramLength ));
}

//============
//= External =
//============

const char * IONDRVFramebuffer::getPixelFormats( void )
{
    static const char * ndrvPixelFormats =
        IO1BitIndexedPixels "\0"
        IO2BitIndexedPixels "\0"
        IO4BitIndexedPixels "\0"
        IO8BitIndexedPixels "\0"
        IO16BitDirectPixels "\0"
        IO32BitDirectPixels "\0"
        "\0";

    return( ndrvPixelFormats);
}

IOItemCount IONDRVFramebuffer::getDisplayModeCount( void )
{
    return( iterateAllModes( 0 ));
}

IOReturn IONDRVFramebuffer::getDisplayModes( IODisplayModeID * allDisplayModes )
{
    iterateAllModes( allDisplayModes );
    return( kIOReturnSuccess );
}

IOReturn IONDRVFramebuffer::getInformationForDisplayMode(
		IODisplayModeID displayMode, IODisplayModeInformation * info )
{
    IOReturn			err;
    VDResolutionInfoRec	*	resInfo;

    bzero( info, sizeof( *info));
    do {
	err = getResInfoForMode( displayMode, &resInfo );
	if( err)
	    continue;
	info->maxDepthIndex	= resInfo->csMaxDepthMode - kDepthMode1;
	info->nominalWidth	= resInfo->csHorizontalPixels;
	info->nominalHeight	= resInfo->csVerticalLines;
	info->refreshRate	= resInfo->csRefreshRate;
	return( noErr);
    } while( false);

    return( kIOReturnUnsupportedMode);
}


UInt64 IONDRVFramebuffer::getPixelFormatsForDisplayMode(
		IODisplayModeID /* displayMode */, IOIndex depthIndex )
{
    return( 1 << (depthIndex + startAt8));
}

IOReturn IONDRVFramebuffer::getPixelInformation(
	IODisplayModeID displayMode, IOIndex depth,
	IOPixelAperture aperture, IOPixelInformation * info )
{
    SInt32			err;
    VDVideoParametersInfoRec	pixelParams;
    VPBlock			pixelInfo;
    const char *		formats;
    UInt32			mask;
    int				index;

    bzero( info, sizeof( *info));

    if( aperture)
        return( kIOReturnUnsupportedMode);

    do {
    	pixelParams.csDisplayModeID = displayMode;
	pixelParams.csDepthMode = depth + kDepthMode1;
	pixelParams.csVPBlockPtr = &pixelInfo;
	err = doStatus( cscGetVideoParameters, &pixelParams );
	if( err)
	    continue;

	//info->flags = kFramebufferSupportsCopybackCache;    

	info->activeWidth	= pixelInfo.vpBounds.right;
	info->activeHeight	= pixelInfo.vpBounds.bottom;
	info->bytesPerRow       = pixelInfo.vpRowBytes & 0x7fff;
	info->bytesPerPlane	= pixelInfo.vpPlaneBytes;
	info->bitsPerPixel 	= pixelInfo.vpPixelSize;

        formats = getPixelFormats();
        mask = getPixelFormatsForDisplayMode( displayMode, depth );

        for( index = 0; index < 32; index++) {
            if( (mask & (1 << index)) && ((aperture--) == 0)) {
                strcpy( info->pixelFormat, formats);
                break;
            }
            formats += strlen( formats) + 1;
        }

        if( 0 == strcmp("PPPPPPPP", info->pixelFormat)) {
            info->pixelType = kIOCLUTPixels;
            info->componentMasks[0] = 0xff;
            info->bitsPerPixel = 8;
            info->componentCount = 1;
            info->bitsPerComponent = 8;

        } else if( 0 == strcmp("-RRRRRGGGGGBBBBB", info->pixelFormat)) {
            info->pixelType = kIORGBDirectPixels;
            info->componentMasks[0] = 0x7c00;
            info->componentMasks[1] = 0x03e0;
            info->componentMasks[2] = 0x001f;
            info->bitsPerPixel = 16;
            info->componentCount = 3;
            info->bitsPerComponent = 5;

        } else if( 0 == strcmp("--------RRRRRRRRGGGGGGGGBBBBBBBB",
                                        info->pixelFormat)) {
            info->pixelType = kIORGBDirectPixels;
            info->componentMasks[0] = 0x00ff0000;
            info->componentMasks[1] = 0x0000ff00;
            info->componentMasks[2] = 0x000000ff;
            info->bitsPerPixel = 32;
            info->componentCount = 3;
            info->bitsPerComponent = 8;
        }

    } while( false);

    return( err);
}

IOReturn IONDRVFramebuffer::getTimingInfoForDisplayMode(
		IODisplayModeID displayMode, IOTimingInformation * info )
{
    VDTimingInfoRec		timingInfo;
    OSStatus			err;

    timingInfo.csTimingMode = displayMode;
    // in case the driver doesn't do it:
    timingInfo.csTimingFormat = kDeclROMtables;
    err = doStatus( cscGetModeTiming, &timingInfo);
    if( err == noErr) {
	if( timingInfo.csTimingFormat == kDeclROMtables)
	    info->appleTimingID = timingInfo.csTimingData;
	else
	    info->appleTimingID = timingInvalid;

	return( kIOReturnSuccess);
    }

    return( kIOReturnUnsupportedMode);
}

IOReturn IONDRVFramebuffer::getCurrentDisplayMode( 
				IODisplayModeID * displayMode, IOIndex * depth )
{
    if( displayMode)
	*displayMode = currentDisplayMode;
    if( depth)
	*depth = currentDepth;

    return( kIOReturnSuccess);
}

IOReturn IONDRVFramebuffer::setDisplayMode( IODisplayModeID displayMode, IOIndex depth )
{
    SInt32		err;
    VDSwitchInfoRec	switchInfo;
    VDPageInfo		pageInfo;

    switchInfo.csData = displayMode;
    switchInfo.csMode = depth + kDepthMode1;
    switchInfo.csPage = 0;
    err = doControl( cscSwitchMode, &switchInfo);
    if(err)
	IOLog("%s: cscSwitchMode:%d\n", nub->getName(), (int)err);

    // duplicate QD InitGDevice
    pageInfo.csMode = switchInfo.csMode;
    pageInfo.csData = 0;
    pageInfo.csPage = 0;
    doControl( cscSetMode, &pageInfo);
    doControl( cscGrayPage, &pageInfo);

    getCurrentConfiguration();

    return( err);
}

IOReturn IONDRVFramebuffer::setStartupDisplayMode(
			IODisplayModeID displayMode, IOIndex depth )
{
    SInt32		err;
    VDSwitchInfoRec	switchInfo;

    switchInfo.csData = displayMode;
    switchInfo.csMode = depth + kDepthMode1;
    err = doControl( cscSavePreferredConfiguration, &switchInfo);
    return( err);
}

IOReturn IONDRVFramebuffer::getStartupDisplayMode(
				IODisplayModeID * displayMode, IOIndex * depth )
{
    SInt32		err;
    VDSwitchInfoRec	switchInfo;

    err = doStatus( cscGetPreferredConfiguration, &switchInfo);
    if( err == noErr) {
	*displayMode	= switchInfo.csData;
	*depth		= switchInfo.csMode - kDepthMode1;
    }
    return( err);
}

IOReturn IONDRVFramebuffer::setApertureEnable( IOPixelAperture /* aperture */,
						IOOptionBits /* enable */ )
{
    return( kIOReturnSuccess);
}

IOReturn IONDRVFramebuffer::setCLUTWithEntries(
			IOColorEntry * colors, UInt32 index, UInt32 numEntries,
			IOOptionBits options )
{
    IOReturn		err;
    UInt32		code;
    VDSetEntryRecord	setEntryRec;
    VDClutBehavior	clutSetting;
    VDGrayRecord	grayRec;

    if( options & kSetCLUTWithLuminance)
        grayRec.csMode = 1;		// turn on luminance map
    else
        grayRec.csMode = 0;		// turn off luminance map

    if( grayRec.csMode != lastGrayMode) {
	doControl( cscSetGray, &grayRec);
	lastGrayMode = grayRec.csMode;
    }

    if( options & kSetCLUTImmediately)
        clutSetting = kSetClutAtSetEntries;
    else
        clutSetting = kSetClutAtVBL;

    if( clutSetting != lastClutSetting) {
	doControl( cscSetClutBehavior, &clutSetting);
	lastClutSetting = clutSetting;
    }

    if( options & kSetCLUTByValue)
        setEntryRec.csStart = -1;
    else
        setEntryRec.csStart = index;

    setEntryRec.csTable = (ColorSpec *) colors;
    setEntryRec.csCount = numEntries - 1;
    if( directMode)
        code = cscDirectSetEntries;
    else
        code = cscSetEntries;
    err = doControl( code, &setEntryRec);

    return( err);
}

IOReturn IONDRVFramebuffer::setGammaTable( UInt32 channelCount, UInt32 dataCount,
                                            UInt32 dataWidth, void * data )
{
    IOReturn		err;
    VDGammaRecord	gammaRec;
    struct GammaTbl {
        short gVersion;		/*gamma version number*/
        short gType;		/*gamma data type*/
        short gFormulaSize;	/*Formula data size*/
        short gChanCnt;		/*number of channels of data*/
        short gDataCnt;		/*number of values/channel*/
        short gDataWidth;	/*bits/corrected value */
				/* (data packed to next larger byte size)*/
        UInt8 gFormulaData[0];	/*data for formulas followed by gamma values*/
    };
    GammaTbl * 	table = NULL;
    IOByteCount	dataLen = 0;

    if( data) {
        dataLen = (dataWidth + 7) / 8;
        dataLen *= dataCount * channelCount;
        table = (GammaTbl *) IOMalloc( dataLen + sizeof( struct GammaTbl));
        if( NULL == table)
            return( kIOReturnNoMemory);

	table->gVersion		= 0;
	table->gType		= 0;
	table->gFormulaSize	= 0;
	table->gChanCnt		= channelCount;
	table-> gDataCnt	= dataCount;
	table->gDataWidth	= dataWidth;
	bcopy( data, table->gFormulaData, dataLen);
    }

    gammaRec.csGTable = (Ptr) table;
    err = doControl( cscSetGamma, &gammaRec);
    if( table)
        IOFree( table, dataLen + sizeof( struct GammaTbl));

    return( err);
}

IOReturn IONDRVFramebuffer::getAttribute( IOSelect attribute, UInt32 * value )
{
    IOReturn			err = kIOReturnSuccess;
    VDSupportsHardwareCursorRec	hwCrsrSupport;

    switch( attribute ) {

	case kIOHardwareCursorAttribute:

	    *value = ((kIOReturnSuccess ==
			doStatus( cscSupportsHardwareCursor, &hwCrsrSupport))
                    && (hwCrsrSupport.csSupportsHardwareCursor));
	    break;

	default:
	    err = super::getAttribute( attribute, value );
    }

    return( err );
}

UInt32 IONDRVFramebuffer::getConnectionCount( void )
{
    VDMultiConnectInfoRec	theRecord;

    if( doStatus(cscGetMultiConnect,&theRecord) == 0 ) {
        return theRecord.csDisplayCountOrNumber;
    }
    return 1;
}

IOReturn IONDRVFramebuffer::setAttributeForConnection( IOIndex connectIndex,
                                         IOSelect attribute, UInt32  info )
{
    IOReturn	ret;
    VDSyncInfoRec	theVDSyncInfoRec;

    switch( attribute ) {

        case kConnectionSyncEnable:
            theVDSyncInfoRec.csMode = (unsigned char)(info>>8);
            theVDSyncInfoRec.csFlags = (unsigned char)(info & 0xFF);
            doControl(cscSetSync,&theVDSyncInfoRec);
            ret = kIOReturnSuccess;
            break;
        default:
            ret = super::setAttributeForConnection( connectIndex,
					attribute, info );
            break;
    }
    return( ret );
}

            
IOReturn IONDRVFramebuffer::getAttributeForConnection( IOIndex connectIndex,
                                         IOSelect attribute, UInt32  * value )
{
    IOReturn	ret;
    VDSyncInfoRec	theVDSyncInfoRec;
    
    switch( attribute ) {

        case kConnectionSyncFlags:
            // find out current state of sync lines
            theVDSyncInfoRec.csMode = 0x00;
            doStatus(cscGetSync,&theVDSyncInfoRec);
            * value = theVDSyncInfoRec.csMode;
            ret = kIOReturnSuccess;
            break;
        case kConnectionSyncEnable:
            // what are the sync-controlling capabilities of the ndrv?
            theVDSyncInfoRec.csMode = 0xFF;
            doStatus(cscGetSync,&theVDSyncInfoRec);
            * value = (UInt32)theVDSyncInfoRec.csMode;
            ret = kIOReturnSuccess;
            break;
        case kConnectionSupportsHLDDCSense:
        case kConnectionSupportsAppleSense:
            ret = kIOReturnSuccess;
            break;
        default:
            ret = super::getAttributeForConnection( connectIndex,
				attribute, value );
            break;
    }

    return( ret );
}

IOReturn IONDRVFramebuffer::getAppleSense( IOIndex  connectIndex,
                                            UInt32 * senseType,
                                            UInt32 * primary,
                                            UInt32 * extended,
                                            UInt32 * displayType )
{
    OSStatus		err;
    VDMultiConnectInfoRec	multiConnect;
    UInt32			sense, extSense;

    if( connectIndex == 0 )
        err = doStatus( cscGetConnection, &multiConnect.csConnectInfo);

    else {
        multiConnect.csDisplayCountOrNumber = connectIndex;
        err = doControl( cscSetMultiConnect, &multiConnect);
    }
    if( err)
	return( err);

    if( multiConnect.csConnectInfo.csConnectFlags 
      & ((1<<kReportsTagging) | (1<<kTaggingInfoNonStandard))
	!= ((1<<kReportsTagging)) )

	err = kIOReturnUnsupported;

    else {

        sense 		= multiConnect.csConnectInfo.csConnectTaggedType;
        extSense 	= multiConnect.csConnectInfo.csConnectTaggedData;
	// bug fixes for really old ATI driver
        if( sense == 0) {
            if( extSense == 6) {
                sense          	= kRSCSix;
                extSense        = kESCSixStandard;
            }
            else
                if( extSense == 4) {
                sense		= kRSCFour;
                extSense        = kESCFourNTSC;
                }
            }
        if( primary)
            *primary = sense;
        if( extended)
            *extended = extSense;
        if( displayType)
            *displayType = multiConnect.csConnectInfo.csDisplayType;
        if( senseType)
            *senseType = 0;
    }
    return( err);
}

IOReturn IONDRVFramebuffer::connectFlags( IOIndex /* connectIndex */,
                             IODisplayModeID displayMode, IOOptionBits * flags )
{
    VDTimingInfoRec		timingInfo;
    OSStatus			err;

    timingInfo.csTimingMode = displayMode;
    // in case the driver doesn't do it:
    timingInfo.csTimingFormat = kDeclROMtables;
    err = doStatus( cscGetModeTiming, &timingInfo);
    *flags = timingInfo.csTimingFlags;
    return( err );
}


bool IONDRVFramebuffer::hasDDCConnect( IOIndex  connectIndex )
{
    OSStatus		err;
    VDMultiConnectInfoRec	multiConnect;
    enum		{	kNeedFlags = (1<<kReportsDDCConnection)
					   | (1<<kHasDDCConnection) };
    if( connectIndex == 0 )
        err = doStatus( cscGetConnection, &multiConnect.csConnectInfo);
    else {
        multiConnect.csDisplayCountOrNumber = connectIndex;
        err = doControl( cscSetMultiConnect, &multiConnect);
    }
    if( err)
        return( err);

    return( (multiConnect.csConnectInfo.csConnectFlags & kNeedFlags)
		== kNeedFlags );
}

IOReturn IONDRVFramebuffer::getDDCBlock( IOIndex /* connectIndex */, 
					UInt32 blockNumber,
                                        IOSelect blockType,
					IOOptionBits options,
                                        UInt8 * data, IOByteCount * length )

{
    OSStatus		err = 0;
    VDDDCBlockRec	ddcRec;
    ByteCount		actualLength = *length;

    ddcRec.ddcBlockNumber 	= blockNumber;
    ddcRec.ddcBlockType 	= blockType;
    ddcRec.ddcFlags 		= options;

    err = doStatus( cscGetDDCBlock, &ddcRec);

    if( err == noErr) {

	if( actualLength < kDDCBlockSize)
            actualLength = actualLength;
	else
            actualLength = kDDCBlockSize;
        bcopy( ddcRec.ddcBlockData, data, actualLength);
	*length = actualLength;
    }
    return( err);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// initForPM
//
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void IONDRVFramebuffer::initForPM ( void )
{
    // register ourselves with superclass policy-maker
    registerControllingDriver(this,ourPowerStates,number_of_power_states);
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// maxCapabilityForDomainState
//
// This simple device needs only power.  If the power domain is supplying
// power, the frame buffer can be on.  If there is no power it can only be off.
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

unsigned long  IONDRVFramebuffer::maxCapabilityForDomainState(
					IOPMPowerFlags domainState )
{
   if( domainState &  IOPMPowerOn )
       return number_of_power_states-1;
   else
       return 0;
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// 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.
//
// This implementation is incomplete.  It only works in a system where
// the frame buffer is never turned off.  When we cross that bridge,
// instead of returning 1, it should return 1 if the frame buffer
// is on, or 0 if it is off.
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
unsigned long IONDRVFramebuffer::initialPowerStateForDomainState(
					 IOPMPowerFlags domainState )
{
   if( domainState &  IOPMPowerOn )
       return number_of_power_states-1;
   else
       return 0;
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// 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  IONDRVFramebuffer::powerStateForDomainState(
					IOPMPowerFlags domainState )
{
   if( domainState &  IOPMPowerOn )
       return pm_vars->myCurrentState;
   else
       return 0;
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// setPowerState
//
// Called by the superclass to turn the frame buffer on and off.
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
IOReturn IONDRVFramebuffer::setPowerState( unsigned long powerStateOrdinal,
						IOService* whatDevice )
{
    if ( powerStateOrdinal == 0 ) {
        kprintf("frame buffer would be powered off here\n");
    }
    if ( powerStateOrdinal == 1 ) {
        kprintf("frame buffer would be powered on here\n");
    }
    return IOPMAckImplied;
}

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

// ATI patches.
// Real problem : getStartupMode doesn't.

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

#undef super
#define super IONDRVFramebuffer

OSDefineMetaClassAndStructors(IOATINDRV, IONDRVFramebuffer)
OSDefineMetaClassAndStructors(IOATI128NDRV, IOATINDRV)

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

IOReturn IOATINDRV::getStartupDisplayMode(
				IODisplayModeID * displayMode, IOIndex * depth )
{
    UInt16 *		nvram;
    OSData *		prop;

    prop = OSDynamicCast( OSData, nub->getProperty("Sime"));
    if( prop) {
	nvram = (UInt16 *) prop->getBytesNoCopy();
	*displayMode = nvram[ 0 ];	// 1 is physDisplayMode
	*depth = nvram[ 2 ] - kDepthMode1;
        return( kIOReturnSuccess);
    } else
        return(super::getStartupDisplayMode( displayMode, depth));
}

IODeviceMemory * IOATINDRV::findVRAM( void )
{
    OSData *		prop;
    IOPhysicalAddress	vramBase;
    IOByteCount		vramLength;

    prop = OSDynamicCast( OSData, nub->getProperty("APPL,VRAMSize"));
    if( !prop)
	prop = OSDynamicCast( OSData, nub->getProperty("ATY,memsize"));
    if( !prop)
	return( super::findVRAM());

    vramBase = physicalFramebuffer;
    vramLength = *((IOByteCount *)prop->getBytesNoCopy());
    if( !vramLength)
        return( super::findVRAM());
    vramLength = (vramLength + (vramBase & 0xffff)) & 0xffff0000;
    vramBase &= 0xffff0000;

    return( makeSubRange( vramBase, vramLength ));
}

static int g128ExtraCurs = 8;
static int g128DeltaCurs = 0x25c0;

void IOATI128NDRV::flushCursor( void )
{
    volatile UInt32 *	fb;
    UInt32		x;
    int			i;

    fb = (volatile UInt32 *) frameBuffer;
    for( i = 0; i < g128ExtraCurs; i++) {
	x += *(fb++);
	fb += g128DeltaCurs;
    }
}