2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 * Copyright (c) 1999 Apple Computer, Inc.
28 * sdouglas 18 Mar 99 - first checked in.
32 #include <IOKit/assert.h>
33 #include <IOKit/IOLib.h>
34 #include <IOKit/IOPlatformExpert.h>
36 #include "IODisplayWrangler.h"
38 bool wranglerHasRoot( OSObject
* us
, void *, IOService
* yourDevice
);
40 #define DODEFAULTMODE 0
49 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
54 #define super IOService
56 OSDefineMetaClassAndStructors(IODisplayConnect
, IOService
)
58 bool IODisplayConnect::initWithConnection( IOIndex _connection
)
65 connection
= _connection
;
67 sprintf( name
, "display%ld", connection
);
74 IOFramebuffer
* IODisplayConnect::getFramebuffer( void )
76 return( (IOFramebuffer
*) getProvider());
79 IOIndex
IODisplayConnect::getConnection( void )
84 IOReturn
IODisplayConnect::getAttributeForConnection( IOIndex connectIndex
, IOSelect selector
, UInt32
* value
)
86 return ((IOFramebuffer
*) getProvider())->getAttributeForConnection( connectIndex
, selector
, value
);
89 IOReturn
IODisplayConnect::setAttributeForConnection( IOIndex connectIndex
, IOSelect selector
, UInt32 info
)
91 return ((IOFramebuffer
*) getProvider())->setAttributeForConnection( connectIndex
, selector
, info
);
95 //*********************************************************************************
98 // The policy-maker in the display driver calls here when initializing.
99 // We attach it into the power management hierarchy as a child of our
101 //*********************************************************************************
102 void IODisplayConnect::joinPMtree ( IOService
* driver
)
104 getProvider()->addPowerChild(driver
);
108 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
110 #define super IOService
111 OSDefineMetaClassAndStructors(IODisplayWrangler
, IOService
);
113 IODisplayWrangler
* gIODisplayWrangler
;
115 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
117 bool IODisplayWrangler::start( IOService
* provider
)
121 if( !super::start( provider
))
124 assert( gIODisplayWrangler
== 0 );
125 gIODisplayWrangler
= this;
127 fMatchingLock
= IOLockAlloc();
128 fFramebuffers
= OSSet::withCapacity( 1 );
129 fDisplays
= OSSet::withCapacity( 1 );
131 assert( fMatchingLock
&& fFramebuffers
&& fDisplays
);
133 notify
= addNotification( gIOPublishNotification
,
134 serviceMatching("IODisplay"), _displayHandler
,
138 notify
= addNotification( gIOPublishNotification
,
139 serviceMatching("IODisplayConnect"), _displayConnectHandler
,
144 addNotification( gIOPublishNotification
,serviceMatching("IOPMrootDomain"), // look for the Root Domain
145 (IOServiceNotificationHandler
)wranglerHasRoot
, this, 0 );
147 // initialize power managment
148 gIODisplayWrangler
->initForPM();
149 // set default screen-dim timeout
150 gIODisplayWrangler
->setAggressiveness ( kPMMinutesToDim
, 30 );
156 bool wranglerHasRoot( OSObject
* us
, void *, IOService
* yourDevice
)
158 if ( yourDevice
!= NULL
) {
159 ((IODisplayWrangler
*)us
)->rootDomain
= yourDevice
;
165 bool IODisplayWrangler::_displayHandler( void * target
, void * ref
,
166 IOService
* newService
)
169 return( ((IODisplayWrangler
*)target
)->displayHandler( (OSSet
*) ref
,
170 (IODisplay
*) newService
));
173 bool IODisplayWrangler::_displayConnectHandler( void * target
, void * ref
,
174 IOService
* newService
)
176 return( ((IODisplayWrangler
*)target
)->displayConnectHandler( ref
,
177 (IODisplayConnect
*) newService
));
180 bool IODisplayWrangler::displayHandler( OSSet
* set
,
181 IODisplay
* newDisplay
)
183 assert( OSDynamicCast( IODisplay
, newDisplay
));
185 IOTakeLock( fMatchingLock
);
187 set
->setObject( newDisplay
);
189 IOUnlock( fMatchingLock
);
194 bool IODisplayWrangler::displayConnectHandler( void * /* ref */,
195 IODisplayConnect
* connect
)
197 SInt32 score
= 50000;
202 assert( OSDynamicCast( IODisplayConnect
, connect
));
204 IOTakeLock( fMatchingLock
);
206 iter
= OSCollectionIterator::withCollection( fDisplays
);
208 while( !found
&& (display
= (IODisplay
*) iter
->getNextObject())) {
209 if( display
->getConnection())
213 if( !display
->attach( connect
))
215 found
= ((display
->probe( connect
, &score
))
216 && (display
->start( connect
)));
218 display
->detach( connect
);
224 IOUnlock( fMatchingLock
);
229 IOReturn
IODisplayWrangler::clientStart( IOFramebuffer
* fb
)
231 IOReturn err
= kIOReturnSuccess
;
233 // IOTakeLock( fFBLock );
235 if( gIODisplayWrangler
&&
236 gIODisplayWrangler
->fFramebuffers
->setObject( fb
)) {
238 // framebuffer not yet done
241 if( kIOReturnSuccess
== err
) {
242 gIODisplayWrangler
->makeDisplayConnects( fb
);
243 gIODisplayWrangler
->findStartupMode( fb
);
245 // try to open it next time
246 // else gIODisplayWrangler->fFramebuffers->removeObject( fb );
249 // IOUnlock( fFBLock );
254 bool IODisplayWrangler::makeDisplayConnects( IOFramebuffer
* fb
)
256 IODisplayConnect
* connect
;
259 for( i
= 0; i
< fb
->getConnectionCount(); i
++) {
261 connect
= new IODisplayConnect
;
265 if( (connect
->initWithConnection( i
))
266 && (connect
->attach( fb
))) {
268 connect
->registerService( kIOServiceSynchronous
);
276 IODisplayConnect
* IODisplayWrangler::getDisplayConnect(
277 IOFramebuffer
* fb
, IOIndex connect
)
281 IODisplayConnect
* connection
= 0;
283 iter
= fb
->getClientIterator();
285 while( (next
= iter
->getNextObject())) {
286 connection
= OSDynamicCast( IODisplayConnect
, next
);
287 if( connection
&& (0 == (connect
--)))
292 return( connection
);
296 IOReturn
IODisplayWrangler::getConnectFlagsForDisplayMode(
297 IODisplayConnect
* connect
,
298 IODisplayModeID mode
, UInt32
* flags
)
300 IOReturn err
= kIOReturnUnsupported
;
303 display
= OSDynamicCast( IODisplay
, connect
->getClient());
305 err
= display
->getConnectFlagsForDisplayMode( mode
, flags
);
307 kprintf("%s: no display\n", connect
->getFramebuffer()->getName());
308 err
= connect
->getFramebuffer()->connectFlags(
309 connect
->getConnection(), mode
, flags
);
315 IOReturn
IODisplayWrangler::getFlagsForDisplayMode(
317 IODisplayModeID mode
, UInt32
* flags
)
319 IODisplayConnect
* connect
;
321 // should look at all connections
322 connect
= gIODisplayWrangler
->getDisplayConnect( fb
, 0 );
324 kprintf("%s: no display connect\n", fb
->getName());
325 return( kIOReturnUnsupported
);
328 return( gIODisplayWrangler
->
329 getConnectFlagsForDisplayMode( connect
, mode
, flags
));
332 IOReturn
IODisplayWrangler::getDefaultMode( IOFramebuffer
* fb
,
333 IODisplayModeID
* mode
, IOIndex
* depth
)
336 UInt32 thisFlags
, bestFlags
= 0;
337 IODisplayModeID thisMode
, bestMode
= 0;
341 IODisplayModeInformation info
;
344 UInt32 lookWidth
, lookHeight
, lookRefresh
, lookDepth
;
345 static const char bitsToIndex
[] = { 0, 0, 1, 1, 2 };
347 IODisplayModeID
* allModes
;
349 bool killedDefault
= false;
350 bool haveSubst
= false;
352 numModes
= fb
->getDisplayModeCount();
353 allModes
= IONew( IODisplayModeID
, numModes
);
355 if( NULL
== allModes
)
356 return( kIOReturnNoMemory
);
357 err
= fb
->getDisplayModes( allModes
);
361 if( PE_parse_boot_arg("dm", arg
)) {
364 lookWidth
= strtol( param
, (char **) ¶m
, 0);
366 lookHeight
= strtol( param
, (char **) ¶m
, 0);
368 lookRefresh
= strtol( param
, (char **) ¶m
, 0);
370 lookDepth
= strtol( param
, (char **) ¶m
, 0);
376 kprintf("%s: Looking %dx%d@%d,%d\n", fb
->getName(), lookWidth
, lookHeight
,
377 lookRefresh
, lookDepth
);
387 bestDepth
= bitsToIndex
[ lookDepth
/ 8 ];
389 for( i
= 0; i
< numModes
; i
++) {
391 thisMode
= allModes
[ i
];
392 if( getFlagsForDisplayMode( fb
, thisMode
, &thisFlags
))
395 // make sure it does 16/32 && requested mode
396 err
= fb
->getInformationForDisplayMode( thisMode
, &info
);
399 if( 0 == info
.maxDepthIndex
)
402 kprintf("%d x %d @ %d = %x\n", info
.nominalWidth
, info
.nominalHeight
,
403 info
.refreshRate
>> 16, thisFlags
);
406 if( 0 == (thisFlags
& kDisplayModeValidFlag
))
410 && (info
.nominalWidth
== lookWidth
)
411 && (info
.nominalHeight
== lookHeight
)
412 && (((info
.refreshRate
+ 0x8000) >> 16) == lookRefresh
) );
414 if( (thisFlags
& kDisplayModeDefaultFlag
)
415 && ((info
.nominalWidth
< kAquaMinWidth
)
416 || (info
.nominalHeight
< kAquaMinHeight
)) ) {
418 thisFlags
&= ~kDisplayModeDefaultFlag
;
419 killedDefault
= true;
422 } else if( killedDefault
423 && (info
.nominalWidth
>= kAquaMinWidth
)
424 && (info
.nominalHeight
>= kAquaMinHeight
) ) {
426 if( thisFlags
& kDisplayModeSafeFlag
) {
427 thisFlags
|= kDisplayModeDefaultFlag
;
428 killedDefault
= false;
429 } else if( !haveSubst
) {
430 thisFlags
|= kDisplayModeDefaultFlag
;
436 || (thisFlags
& kDisplayModeDefaultFlag
)
437 || (((bestFlags
& kDisplayModeDefaultFlag
) == 0)
438 && (thisFlags
& kDisplayModeSafeFlag
)) ) {
441 bestFlags
= thisFlags
;
443 bestDepth
= bitsToIndex
[ lookDepth
/ 8 ];
444 if( bestDepth
> info
.maxDepthIndex
)
445 bestDepth
= info
.maxDepthIndex
;
452 IODelete( allModes
, IODisplayModeID
, numModes
);
457 return( kIOReturnSuccess
);
459 #endif /* DODEFAULTMODE */
460 return( kIOReturnUnsupported
);
463 // Determine a startup mode given the framebuffer & displays
465 IOReturn
IODisplayWrangler::findStartupMode( IOFramebuffer
* fb
)
467 IODisplayModeID mode
;
469 IODisplayModeID startMode
;
473 fb
->getCurrentDisplayMode( &mode
, &depth
);
474 err
= fb
->getStartupDisplayMode( &startMode
, &startDepth
);
481 IODisplayModeInformation info
;
482 UInt32 startFlags
= 0;
485 err
= getFlagsForDisplayMode( fb
, startMode
, &startFlags
);
488 err
= fb
->getInformationForDisplayMode( startMode
, &info
);
492 if( (info
.nominalWidth
< kAquaMinWidth
)
493 || (info
.nominalHeight
< kAquaMinHeight
)) {
494 err
= kIOReturnNoResources
;
501 if( (startDepth
== 0) && (info
.maxDepthIndex
> 0))
508 || ((startFlags
& kDisplayModeValidFlag
)
509 != kDisplayModeValidFlag
) ) {
511 err
= getDefaultMode( fb
, &startMode
, &startDepth
);
513 #endif /* DODEFAULTMODE */
514 if( (startMode
!= mode
) || (startDepth
!= depth
))
515 fb
->setDisplayMode( startMode
, startDepth
);
517 fb
->setupForCurrentConfig();
519 return( kIOReturnSuccess
);
522 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
524 #define kNumber_of_power_states 5
526 static IOPMPowerState ourPowerStates
[kNumber_of_power_states
] = {
527 {1,0,0,0,0,0,0,0,0,0,0,0},
528 {1,0,0,IOPMPowerOn
,0,0,0,0,0,0,0,0},
529 {1,0,0,IOPMPowerOn
,0,0,0,0,0,0,0,0},
530 {1,0,0,IOPMPowerOn
,0,0,0,0,0,0,0,0},
531 {1,IOPMDeviceUsable
,0,IOPMPowerOn
,0,0,0,0,0,0,0,0}
537 This is the Power Management policy-maker for the displays. It senses when the display is idle
538 and lowers power accordingly. It raises power back up when the display becomes un-idle.
540 It senses idleness with a combination of an idle timer and the "activityTickle" method call. "activityTickle"
541 is called by objects which sense keyboard activity, mouse activity, or other button activity (display contrast,
542 display brightness, PCMCIA eject). The method sets a "displayInUse" flag. When the timer expires,
543 this flag is checked. If it is on, the display is judged "in use". The flag is cleared and the timer is restarted.
545 If the flag is off when the timer expires, then there has been no user activity since the last timer
546 expiration, and the display is judged idle and its power is lowered.
548 The period of the timer is a function of the current value of Power Management aggressiveness. As that factor
549 varies from 1 to 999, the timer period varies from 1004 seconds to 6 seconds. Above 1000, the system is in
550 a very aggressive power management condition, and the timer period is 5 seconds. (In this case, the display dims
551 between five and ten seconds after the last user activity).
553 This driver calls the drivers for each display and has them move their display between various power states.
554 When the display is idle, its power is dropped state by state until it is in the lowest state. When it becomes un-idle
555 it is powered back up to the state where it was last being used.
557 In times of very high power management aggressiveness, the display will not be operated above the lowest power
558 state which is marked "usable".
560 When Power Management is turned off (aggressiveness = 0), the display is never judged idle and never dimmed.
562 We register with Power Management only so that we can be informed of changes in the Power Management
563 aggressiveness factor. We don't really have a device with power states so we implement the absolute minimum.
564 The display drivers themselves are part of the Power Management hierarchy under their respective frame buffers.
569 // **********************************************************************************
572 // **********************************************************************************
573 void IODisplayWrangler::initForPM (void )
576 PMinit(); // initialize superclass variables
579 use_general_aggressiveness
= false;
581 pm_vars
->thePlatform
->PMRegisterDevice(0,this); // attach into the power management hierarchy
583 registerPowerDriver(this,ourPowerStates
,kNumber_of_power_states
); // register ourselves with policy-maker (us)
585 registerService(); // HID system is waiting for this
590 //*********************************************************************************
593 // We are informed by our power domain parent of a new level of "power management
594 // aggressiveness" which we use as a factor in our judgement of when we are idle.
595 // This change implies a change in our idle timer period, so restart that timer.
597 //*********************************************************************************
599 IOReturn
IODisplayWrangler::setAggressiveness ( unsigned long type
, unsigned long newLevel
)
601 if ( type
== kPMMinutesToDim
) { // minutes to dim received
602 if( newLevel
== 0 ) {
603 if( pm_vars
->myCurrentState
< kNumber_of_power_states
-1 ) { // pm turned off while idle?
604 makeDisplaysUsable(); // yes, bring displays up again
607 mins_to_dim
= newLevel
;
608 use_general_aggressiveness
= false;
609 if ( pm_vars
->aggressiveness
< kIOPowerEmergencyLevel
) { // no, currently in emergency level?
610 setIdleTimerPeriod(newLevel
*60); // no, set new timeout
613 if ( type
== kPMGeneralAggressiveness
) { // general factor received
614 if ( newLevel
>= kIOPowerEmergencyLevel
) { // emergency level?
615 setIdleTimerPeriod(5); // yes
618 if ( pm_vars
->aggressiveness
>= kIOPowerEmergencyLevel
) { // no, coming out of emergency level?
619 if (use_general_aggressiveness
) { // yes, set new timer period
620 setIdleTimerPeriod(333-(newLevel
/3));
623 setIdleTimerPeriod(mins_to_dim
*60);
627 if (use_general_aggressiveness
) { // no, maybe set period
628 setIdleTimerPeriod(333-(newLevel
/3));
633 super::setAggressiveness(type
, newLevel
);
638 // **********************************************************************************
641 // This is called by the HID system and calls the superclass in turn.
642 // **********************************************************************************
644 bool IODisplayWrangler::activityTickle ( unsigned long, unsigned long )
646 if ( rootDomain
!= NULL
) {
647 rootDomain
->activityTickle (kIOPMSubclassPolicy
);
649 return super::activityTickle (kIOPMSuperclassPolicy1
,kNumber_of_power_states
-1 );
653 // **********************************************************************************
656 // The vanilla policy-maker in the superclass is changing our power state.
657 // If it's down, inform the displays to lower one state, too. If it's up,
658 // the idle displays are made usable.
659 // **********************************************************************************
660 IOReturn
IODisplayWrangler::setPowerState ( unsigned long powerStateOrdinal
, IOService
* whatDevice
)
662 if ( powerStateOrdinal
== 0 ) { // system is going to sleep
665 if ( powerStateOrdinal
< pm_vars
->myCurrentState
) { // HI is idle, drop power
669 if ( powerStateOrdinal
== kNumber_of_power_states
-1 ) { // there is activity, raise power
670 makeDisplaysUsable();
677 // **********************************************************************************
678 // makeDisplaysUsable
680 // **********************************************************************************
681 void IODisplayWrangler::makeDisplaysUsable ( void )
686 IOTakeLock( fMatchingLock
);
688 iter
= OSCollectionIterator::withCollection( fDisplays
);
690 while( (display
= (IODisplay
*) iter
->getNextObject()) ) {
691 display
->makeDisplayUsable();
695 IOUnlock( fMatchingLock
);
699 // **********************************************************************************
702 // **********************************************************************************
703 void IODisplayWrangler::idleDisplays ( void )
708 IOTakeLock( fMatchingLock
);
710 iter
= OSCollectionIterator::withCollection( fDisplays
);
712 while( (display
= (IODisplay
*) iter
->getNextObject()) ) {
713 display
->dropOneLevel();
717 IOUnlock( fMatchingLock
);