IODisplayWrangler.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOPlatformExpert.h>
#include "IODisplayWrangler.h"
bool wranglerHasRoot( OSObject * us, void *, IOService * yourDevice );
#define DODEFAULTMODE 0
#if DODEFAULTMODE
enum {
kAquaMinWidth = 800,
kAquaMinHeight = 600
};
#endif
#undef super
#define super IOService
OSDefineMetaClassAndStructors(IODisplayConnect, IOService)
bool IODisplayConnect::initWithConnection( IOIndex _connection )
{
char name[ 12 ];
if( !super::init())
return( false);
connection = _connection;
sprintf( name, "display%ld", connection);
setName( name);
return( true);
}
IOFramebuffer * IODisplayConnect::getFramebuffer( void )
{
return( (IOFramebuffer *) getProvider());
}
IOIndex IODisplayConnect::getConnection( void )
{
return( connection);
}
IOReturn IODisplayConnect::getAttributeForConnection( IOIndex connectIndex, IOSelect selector, UInt32 * value )
{
return ((IOFramebuffer *) getProvider())->getAttributeForConnection( connectIndex, selector, value );
}
IOReturn IODisplayConnect::setAttributeForConnection( IOIndex connectIndex, IOSelect selector, UInt32 info )
{
return ((IOFramebuffer *) getProvider())->setAttributeForConnection( connectIndex, selector, info );
}
void IODisplayConnect::joinPMtree ( IOService * driver )
{
getProvider()->addPowerChild(driver);
}
#define super IOService
OSDefineMetaClassAndStructors(IODisplayWrangler, IOService);
IODisplayWrangler * gIODisplayWrangler;
bool IODisplayWrangler::start( IOService * provider )
{
OSObject * notify;
if( !super::start( provider))
return( false);
assert( gIODisplayWrangler == 0 );
gIODisplayWrangler = this;
fMatchingLock = IOLockAlloc();
fFramebuffers = OSSet::withCapacity( 1 );
fDisplays = OSSet::withCapacity( 1 );
assert( fMatchingLock && fFramebuffers && fDisplays );
notify = addNotification( gIOPublishNotification,
serviceMatching("IODisplay"), _displayHandler,
this, fDisplays );
assert( notify );
notify = addNotification( gIOPublishNotification,
serviceMatching("IODisplayConnect"), _displayConnectHandler,
this, 0, 50000 );
assert( notify );
rootDomain = NULL;
addNotification( gIOPublishNotification,serviceMatching("IOPMrootDomain"), (IOServiceNotificationHandler)wranglerHasRoot, this, 0 );
gIODisplayWrangler->initForPM();
gIODisplayWrangler->setAggressiveness ( kPMMinutesToDim, 30 );
return( true );
}
bool wranglerHasRoot( OSObject * us, void *, IOService * yourDevice )
{
if ( yourDevice != NULL ) {
((IODisplayWrangler *)us)->rootDomain = yourDevice;
}
return true;
}
bool IODisplayWrangler::_displayHandler( void * target, void * ref,
IOService * newService )
{
return( ((IODisplayWrangler *)target)->displayHandler( (OSSet *) ref,
(IODisplay *) newService ));
}
bool IODisplayWrangler::_displayConnectHandler( void * target, void * ref,
IOService * newService )
{
return( ((IODisplayWrangler *)target)->displayConnectHandler( ref,
(IODisplayConnect *) newService ));
}
bool IODisplayWrangler::displayHandler( OSSet * set,
IODisplay * newDisplay )
{
assert( OSDynamicCast( IODisplay, newDisplay ));
IOTakeLock( fMatchingLock );
set->setObject( newDisplay );
IOUnlock( fMatchingLock );
return( true );
}
bool IODisplayWrangler::displayConnectHandler( void * ,
IODisplayConnect * connect )
{
SInt32 score = 50000;
OSIterator * iter;
IODisplay * display;
bool found = false;
assert( OSDynamicCast( IODisplayConnect, connect ));
IOTakeLock( fMatchingLock );
iter = OSCollectionIterator::withCollection( fDisplays );
if( iter) {
while( !found && (display = (IODisplay *) iter->getNextObject())) {
if( display->getConnection())
continue;
do {
if( !display->attach( connect ))
continue;
found = ((display->probe( connect, &score ))
&& (display->start( connect )));
if( !found)
display->detach( connect );
} while( false);
}
iter->release();
}
IOUnlock( fMatchingLock );
return( true);
}
IOReturn IODisplayWrangler::clientStart( IOFramebuffer * fb )
{
IOReturn err = kIOReturnSuccess;
if( gIODisplayWrangler &&
gIODisplayWrangler->fFramebuffers->setObject( fb )) {
err = fb->open();
if( kIOReturnSuccess == err) {
gIODisplayWrangler->makeDisplayConnects( fb );
gIODisplayWrangler->findStartupMode( fb );
}
}
return( err );
}
bool IODisplayWrangler::makeDisplayConnects( IOFramebuffer * fb )
{
IODisplayConnect * connect;
IOItemCount i;
for( i = 0; i < fb->getConnectionCount(); i++) {
connect = new IODisplayConnect;
if( 0 == connect)
continue;
if( (connect->initWithConnection( i ))
&& (connect->attach( fb ))) {
connect->registerService( kIOServiceSynchronous );
}
connect->release();
}
return( true );
}
IODisplayConnect * IODisplayWrangler::getDisplayConnect(
IOFramebuffer * fb, IOIndex connect )
{
OSIterator * iter;
OSObject * next;
IODisplayConnect * connection = 0;
iter = fb->getClientIterator();
if( iter) {
while( (next = iter->getNextObject())) {
connection = OSDynamicCast( IODisplayConnect, next);
if( connection && (0 == (connect--)))
break;
}
iter->release();
}
return( connection );
}
IOReturn IODisplayWrangler::getConnectFlagsForDisplayMode(
IODisplayConnect * connect,
IODisplayModeID mode, UInt32 * flags )
{
IOReturn err = kIOReturnUnsupported;
IODisplay * display;
display = OSDynamicCast( IODisplay, connect->getClient());
if( display)
err = display->getConnectFlagsForDisplayMode( mode, flags );
else {
kprintf("%s: no display\n", connect->getFramebuffer()->getName());
err = connect->getFramebuffer()->connectFlags(
connect->getConnection(), mode, flags );
}
return( err );
}
IOReturn IODisplayWrangler::getFlagsForDisplayMode(
IOFramebuffer * fb,
IODisplayModeID mode, UInt32 * flags )
{
IODisplayConnect * connect;
connect = gIODisplayWrangler->getDisplayConnect( fb, 0 );
if( !connect) {
kprintf("%s: no display connect\n", fb->getName());
return( kIOReturnUnsupported );
}
return( gIODisplayWrangler->
getConnectFlagsForDisplayMode( connect, mode, flags ));
}
IOReturn IODisplayWrangler::getDefaultMode( IOFramebuffer * fb,
IODisplayModeID * mode, IOIndex * depth )
{
#if DODEFAULTMODE
UInt32 thisFlags, bestFlags = 0;
IODisplayModeID thisMode, bestMode = 0;
IOIndex bestDepth;
UInt32 i;
IOReturn err;
IODisplayModeInformation info;
char arg[ 64 ];
const char * param;
UInt32 lookWidth, lookHeight, lookRefresh, lookDepth;
static const char bitsToIndex[] = { 0, 0, 1, 1, 2 };
UInt32 numModes;
IODisplayModeID * allModes;
bool foundForced;
bool killedDefault = false;
bool haveSubst = false;
numModes = fb->getDisplayModeCount();
allModes = IONew( IODisplayModeID, numModes );
if( NULL == allModes)
return( kIOReturnNoMemory);
err = fb->getDisplayModes( allModes );
if( err) return( err );
if( PE_parse_boot_arg("dm", arg)) {
param = arg;
lookWidth = strtol( param, (char **) ¶m, 0);
param++;
lookHeight = strtol( param, (char **) ¶m, 0);
param++;
lookRefresh = strtol( param, (char **) ¶m, 0);
param++;
lookDepth = strtol( param, (char **) ¶m, 0);
if( lookDepth == 15)
lookDepth = 16;
if( lookDepth > 32)
lookDepth = 32;
kprintf("%s: Looking %dx%d@%d,%d\n", fb->getName(), lookWidth, lookHeight,
lookRefresh, lookDepth );
} else {
param = 0;
lookWidth = 1024;
lookHeight = 768;
lookRefresh = 75;
lookDepth = 16;
}
bestDepth = bitsToIndex[ lookDepth / 8 ];
for( i = 0; i < numModes; i++) {
thisMode = allModes[ i ];
if( getFlagsForDisplayMode( fb, thisMode, &thisFlags))
continue;
err = fb->getInformationForDisplayMode( thisMode, &info);
if( err)
continue;
if( 0 == info.maxDepthIndex)
continue;
#if 0
kprintf("%d x %d @ %d = %x\n", info.nominalWidth, info.nominalHeight,
info.refreshRate >> 16, thisFlags);
#endif
if( 0 == (thisFlags & kDisplayModeValidFlag))
continue;
foundForced = (param
&& (info.nominalWidth == lookWidth)
&& (info.nominalHeight == lookHeight)
&& (((info.refreshRate + 0x8000) >> 16) == lookRefresh) );
if( (thisFlags & kDisplayModeDefaultFlag)
&& ((info.nominalWidth < kAquaMinWidth)
|| (info.nominalHeight < kAquaMinHeight)) ) {
thisFlags &= ~kDisplayModeDefaultFlag;
killedDefault = true;
haveSubst = false;
} else if( killedDefault
&& (info.nominalWidth >= kAquaMinWidth)
&& (info.nominalHeight >= kAquaMinHeight) ) {
if( thisFlags & kDisplayModeSafeFlag) {
thisFlags |= kDisplayModeDefaultFlag;
killedDefault = false;
} else if( !haveSubst) {
thisFlags |= kDisplayModeDefaultFlag;
haveSubst = true;
}
}
if( foundForced
|| (thisFlags & kDisplayModeDefaultFlag)
|| (((bestFlags & kDisplayModeDefaultFlag) == 0)
&& (thisFlags & kDisplayModeSafeFlag)) ) {
bestMode = thisMode;
bestFlags = thisFlags;
bestDepth = bitsToIndex[ lookDepth / 8 ];
if( bestDepth > info.maxDepthIndex)
bestDepth = info.maxDepthIndex;
if( foundForced)
break;
}
}
IODelete( allModes, IODisplayModeID, numModes );
if( bestMode) {
*mode = bestMode;
*depth = bestDepth;
return( kIOReturnSuccess);
} else
#endif
return( kIOReturnUnsupported);
}
IOReturn IODisplayWrangler::findStartupMode( IOFramebuffer * fb )
{
IODisplayModeID mode;
IOIndex depth;
IODisplayModeID startMode;
IOIndex startDepth;
IOReturn err;
fb->getCurrentDisplayMode( &mode, &depth);
err = fb->getStartupDisplayMode( &startMode, &startDepth );
if( err) {
startMode = mode;
startDepth = depth;
}
#if DODEFAULTMODE
IODisplayModeInformation info;
UInt32 startFlags = 0;
do {
err = getFlagsForDisplayMode( fb, startMode, &startFlags );
if( err)
continue;
err = fb->getInformationForDisplayMode( startMode, &info);
if( err)
continue;
if( (info.nominalWidth < kAquaMinWidth)
|| (info.nominalHeight < kAquaMinHeight)) {
err = kIOReturnNoResources;
continue;
}
if( startDepth == 2)
startDepth = 1;
if( (startDepth == 0) && (info.maxDepthIndex > 0))
startDepth = 1;
} while( false );
if( err
|| (startDepth == 0)
|| ((startFlags & kDisplayModeValidFlag)
!= kDisplayModeValidFlag) ) {
err = getDefaultMode( fb, &startMode, &startDepth );
}
#endif
if( (startMode != mode) || (startDepth != depth))
fb->setDisplayMode( startMode, startDepth );
fb->setupForCurrentConfig();
return( kIOReturnSuccess );
}
#define kNumber_of_power_states 5
static IOPMPowerState ourPowerStates[kNumber_of_power_states] = {
{1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,IOPMPowerOn,0,0,0,0,0,0,0,0},
{1,0,0,IOPMPowerOn,0,0,0,0,0,0,0,0},
{1,0,0,IOPMPowerOn,0,0,0,0,0,0,0,0},
{1,IOPMDeviceUsable,0,IOPMPowerOn,0,0,0,0,0,0,0,0}
};
void IODisplayWrangler::initForPM (void )
{
PMinit();
mins_to_dim = 0;
use_general_aggressiveness = false;
pm_vars->thePlatform->PMRegisterDevice(0,this);
registerPowerDriver(this,ourPowerStates,kNumber_of_power_states);
registerService();
}
IOReturn IODisplayWrangler::setAggressiveness ( unsigned long type, unsigned long newLevel )
{
if ( type == kPMMinutesToDim ) { if( newLevel == 0 ) {
if( pm_vars->myCurrentState < kNumber_of_power_states-1 ) { makeDisplaysUsable(); }
}
mins_to_dim = newLevel;
use_general_aggressiveness = false;
if ( pm_vars->aggressiveness < kIOPowerEmergencyLevel ) { setIdleTimerPeriod(newLevel*60); }
}
if ( type == kPMGeneralAggressiveness ) { if ( newLevel >= kIOPowerEmergencyLevel ) { setIdleTimerPeriod(5); }
else {
if ( pm_vars->aggressiveness >= kIOPowerEmergencyLevel ) { if (use_general_aggressiveness ) { setIdleTimerPeriod(333-(newLevel/3));
}
else {
setIdleTimerPeriod(mins_to_dim*60);
}
}
else {
if (use_general_aggressiveness ) { setIdleTimerPeriod(333-(newLevel/3));
}
}
}
}
super::setAggressiveness(type, newLevel);
return IOPMNoErr;
}
bool IODisplayWrangler::activityTickle ( unsigned long, unsigned long )
{
if ( rootDomain != NULL ) {
rootDomain->activityTickle (kIOPMSubclassPolicy);
}
return super::activityTickle (kIOPMSuperclassPolicy1,kNumber_of_power_states-1 );
}
IOReturn IODisplayWrangler::setPowerState ( unsigned long powerStateOrdinal, IOService* whatDevice )
{
if ( powerStateOrdinal == 0 ) { return IOPMNoErr;
}
if ( powerStateOrdinal < pm_vars->myCurrentState ) { idleDisplays();
return IOPMNoErr;
}
if ( powerStateOrdinal == kNumber_of_power_states-1 ) { makeDisplaysUsable();
return IOPMNoErr;
}
return IOPMNoErr;
}
void IODisplayWrangler::makeDisplaysUsable ( void )
{
OSIterator * iter;
IODisplay * display;
IOTakeLock( fMatchingLock );
iter = OSCollectionIterator::withCollection( fDisplays );
if( iter ) {
while( (display = (IODisplay *) iter->getNextObject()) ) {
display->makeDisplayUsable();
}
iter->release();
}
IOUnlock( fMatchingLock );
}
void IODisplayWrangler::idleDisplays ( void )
{
OSIterator * iter;
IODisplay * display;
IOTakeLock( fMatchingLock );
iter = OSCollectionIterator::withCollection( fDisplays );
if( iter ) {
while( (display = (IODisplay *) iter->getNextObject()) ) {
display->dropOneLevel();
}
iter->release();
}
IOUnlock( fMatchingLock );
}