]> git.saurik.com Git - apple/xnu.git/blob - iokit/Families/IOGraphics/IODisplayWrangler.cpp
xnu-124.13.tar.gz
[apple/xnu.git] / iokit / Families / IOGraphics / IODisplayWrangler.cpp
1 /*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * Copyright (c) 1999 Apple Computer, Inc.
24 *
25 *
26 * HISTORY
27 *
28 * sdouglas 18 Mar 99 - first checked in.
29 */
30
31
32 #include <IOKit/assert.h>
33 #include <IOKit/IOLib.h>
34 #include <IOKit/IOPlatformExpert.h>
35
36 #include "IODisplayWrangler.h"
37
38 bool wranglerHasRoot( OSObject * us, void *, IOService * yourDevice );
39
40 #define DODEFAULTMODE 0
41
42 #if DODEFAULTMODE
43 enum {
44 kAquaMinWidth = 800,
45 kAquaMinHeight = 600
46 };
47 #endif
48
49 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
50
51 // tiddly nub
52
53 #undef super
54 #define super IOService
55
56 OSDefineMetaClassAndStructors(IODisplayConnect, IOService)
57
58 bool IODisplayConnect::initWithConnection( IOIndex _connection )
59 {
60 char name[ 12 ];
61
62 if( !super::init())
63 return( false);
64
65 connection = _connection;
66
67 sprintf( name, "display%ld", connection);
68
69 setName( name);
70
71 return( true);
72 }
73
74 IOFramebuffer * IODisplayConnect::getFramebuffer( void )
75 {
76 return( (IOFramebuffer *) getProvider());
77 }
78
79 IOIndex IODisplayConnect::getConnection( void )
80 {
81 return( connection);
82 }
83
84 IOReturn IODisplayConnect::getAttributeForConnection( IOIndex connectIndex, IOSelect selector, UInt32 * value )
85 {
86 return ((IOFramebuffer *) getProvider())->getAttributeForConnection( connectIndex, selector, value );
87 }
88
89 IOReturn IODisplayConnect::setAttributeForConnection( IOIndex connectIndex, IOSelect selector, UInt32 info )
90 {
91 return ((IOFramebuffer *) getProvider())->setAttributeForConnection( connectIndex, selector, info );
92 }
93
94
95 //*********************************************************************************
96 // joinPMtree
97 //
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
100 // frame buffer.
101 //*********************************************************************************
102 void IODisplayConnect::joinPMtree ( IOService * driver )
103 {
104 getProvider()->addPowerChild(driver);
105 }
106
107
108 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
109
110 #define super IOService
111 OSDefineMetaClassAndStructors(IODisplayWrangler, IOService);
112
113 IODisplayWrangler * gIODisplayWrangler;
114
115 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
116
117 bool IODisplayWrangler::start( IOService * provider )
118 {
119 OSObject * notify;
120
121 if( !super::start( provider))
122 return( false);
123
124 assert( gIODisplayWrangler == 0 );
125 gIODisplayWrangler = this;
126
127 fMatchingLock = IOLockAlloc();
128 fFramebuffers = OSSet::withCapacity( 1 );
129 fDisplays = OSSet::withCapacity( 1 );
130
131 assert( fMatchingLock && fFramebuffers && fDisplays );
132
133 notify = addNotification( gIOPublishNotification,
134 serviceMatching("IODisplay"), _displayHandler,
135 this, fDisplays );
136 assert( notify );
137
138 notify = addNotification( gIOPublishNotification,
139 serviceMatching("IODisplayConnect"), _displayConnectHandler,
140 this, 0, 50000 );
141 assert( notify );
142
143 rootDomain = NULL;
144 addNotification( gIOPublishNotification,serviceMatching("IOPMrootDomain"), // look for the Root Domain
145 (IOServiceNotificationHandler)wranglerHasRoot, this, 0 );
146
147 // initialize power managment
148 gIODisplayWrangler->initForPM();
149 // set default screen-dim timeout
150 gIODisplayWrangler->setAggressiveness ( kPMMinutesToDim, 30 );
151
152 return( true );
153 }
154
155
156 bool wranglerHasRoot( OSObject * us, void *, IOService * yourDevice )
157 {
158 if ( yourDevice != NULL ) {
159 ((IODisplayWrangler *)us)->rootDomain = yourDevice;
160 }
161 return true;
162 }
163
164
165 bool IODisplayWrangler::_displayHandler( void * target, void * ref,
166 IOService * newService )
167 {
168
169 return( ((IODisplayWrangler *)target)->displayHandler( (OSSet *) ref,
170 (IODisplay *) newService ));
171 }
172
173 bool IODisplayWrangler::_displayConnectHandler( void * target, void * ref,
174 IOService * newService )
175 {
176 return( ((IODisplayWrangler *)target)->displayConnectHandler( ref,
177 (IODisplayConnect *) newService ));
178 }
179
180 bool IODisplayWrangler::displayHandler( OSSet * set,
181 IODisplay * newDisplay )
182 {
183 assert( OSDynamicCast( IODisplay, newDisplay ));
184
185 IOTakeLock( fMatchingLock );
186
187 set->setObject( newDisplay );
188
189 IOUnlock( fMatchingLock );
190
191 return( true );
192 }
193
194 bool IODisplayWrangler::displayConnectHandler( void * /* ref */,
195 IODisplayConnect * connect )
196 {
197 SInt32 score = 50000;
198 OSIterator * iter;
199 IODisplay * display;
200 bool found = false;
201
202 assert( OSDynamicCast( IODisplayConnect, connect ));
203
204 IOTakeLock( fMatchingLock );
205
206 iter = OSCollectionIterator::withCollection( fDisplays );
207 if( iter) {
208 while( !found && (display = (IODisplay *) iter->getNextObject())) {
209 if( display->getConnection())
210 continue;
211
212 do {
213 if( !display->attach( connect ))
214 continue;
215 found = ((display->probe( connect, &score ))
216 && (display->start( connect )));
217 if( !found)
218 display->detach( connect );
219 } while( false);
220 }
221 iter->release();
222 }
223
224 IOUnlock( fMatchingLock );
225
226 return( true);
227 }
228
229 IOReturn IODisplayWrangler::clientStart( IOFramebuffer * fb )
230 {
231 IOReturn err = kIOReturnSuccess;
232
233 // IOTakeLock( fFBLock );
234
235 if( gIODisplayWrangler &&
236 gIODisplayWrangler->fFramebuffers->setObject( fb )) {
237
238 // framebuffer not yet done
239
240 err = fb->open();
241 if( kIOReturnSuccess == err) {
242 gIODisplayWrangler->makeDisplayConnects( fb );
243 gIODisplayWrangler->findStartupMode( fb );
244 }
245 // try to open it next time
246 // else gIODisplayWrangler->fFramebuffers->removeObject( fb );
247 }
248
249 // IOUnlock( fFBLock );
250
251 return( err );
252 }
253
254 bool IODisplayWrangler::makeDisplayConnects( IOFramebuffer * fb )
255 {
256 IODisplayConnect * connect;
257 IOItemCount i;
258
259 for( i = 0; i < fb->getConnectionCount(); i++) {
260
261 connect = new IODisplayConnect;
262 if( 0 == connect)
263 continue;
264
265 if( (connect->initWithConnection( i ))
266 && (connect->attach( fb ))) {
267
268 connect->registerService( kIOServiceSynchronous );
269 }
270 connect->release();
271 }
272
273 return( true );
274 }
275
276 IODisplayConnect * IODisplayWrangler::getDisplayConnect(
277 IOFramebuffer * fb, IOIndex connect )
278 {
279 OSIterator * iter;
280 OSObject * next;
281 IODisplayConnect * connection = 0;
282
283 iter = fb->getClientIterator();
284 if( iter) {
285 while( (next = iter->getNextObject())) {
286 connection = OSDynamicCast( IODisplayConnect, next);
287 if( connection && (0 == (connect--)))
288 break;
289 }
290 iter->release();
291 }
292 return( connection );
293 }
294
295
296 IOReturn IODisplayWrangler::getConnectFlagsForDisplayMode(
297 IODisplayConnect * connect,
298 IODisplayModeID mode, UInt32 * flags )
299 {
300 IOReturn err = kIOReturnUnsupported;
301 IODisplay * display;
302
303 display = OSDynamicCast( IODisplay, connect->getClient());
304 if( display)
305 err = display->getConnectFlagsForDisplayMode( mode, flags );
306 else {
307 kprintf("%s: no display\n", connect->getFramebuffer()->getName());
308 err = connect->getFramebuffer()->connectFlags(
309 connect->getConnection(), mode, flags );
310 }
311
312 return( err );
313 }
314
315 IOReturn IODisplayWrangler::getFlagsForDisplayMode(
316 IOFramebuffer * fb,
317 IODisplayModeID mode, UInt32 * flags )
318 {
319 IODisplayConnect * connect;
320
321 // should look at all connections
322 connect = gIODisplayWrangler->getDisplayConnect( fb, 0 );
323 if( !connect) {
324 kprintf("%s: no display connect\n", fb->getName());
325 return( kIOReturnUnsupported );
326 }
327
328 return( gIODisplayWrangler->
329 getConnectFlagsForDisplayMode( connect, mode, flags ));
330 }
331
332 IOReturn IODisplayWrangler::getDefaultMode( IOFramebuffer * fb,
333 IODisplayModeID * mode, IOIndex * depth )
334 {
335 #if DODEFAULTMODE
336 UInt32 thisFlags, bestFlags = 0;
337 IODisplayModeID thisMode, bestMode = 0;
338 IOIndex bestDepth;
339 UInt32 i;
340 IOReturn err;
341 IODisplayModeInformation info;
342 char arg[ 64 ];
343 const char * param;
344 UInt32 lookWidth, lookHeight, lookRefresh, lookDepth;
345 static const char bitsToIndex[] = { 0, 0, 1, 1, 2 };
346 UInt32 numModes;
347 IODisplayModeID * allModes;
348 bool foundForced;
349 bool killedDefault = false;
350 bool haveSubst = false;
351
352 numModes = fb->getDisplayModeCount();
353 allModes = IONew( IODisplayModeID, numModes );
354
355 if( NULL == allModes)
356 return( kIOReturnNoMemory);
357 err = fb->getDisplayModes( allModes );
358 if( err) // leak
359 return( err );
360
361 if( PE_parse_boot_arg("dm", arg)) {
362
363 param = arg;
364 lookWidth = strtol( param, (char **) &param, 0);
365 param++;
366 lookHeight = strtol( param, (char **) &param, 0);
367 param++;
368 lookRefresh = strtol( param, (char **) &param, 0);
369 param++;
370 lookDepth = strtol( param, (char **) &param, 0);
371 if( lookDepth == 15)
372 lookDepth = 16;
373 if( lookDepth > 32)
374 lookDepth = 32;
375
376 kprintf("%s: Looking %dx%d@%d,%d\n", fb->getName(), lookWidth, lookHeight,
377 lookRefresh, lookDepth );
378
379 } else {
380 param = 0;
381 lookWidth = 1024;
382 lookHeight = 768;
383 lookRefresh = 75;
384 lookDepth = 16;
385 }
386
387 bestDepth = bitsToIndex[ lookDepth / 8 ];
388
389 for( i = 0; i < numModes; i++) {
390
391 thisMode = allModes[ i ];
392 if( getFlagsForDisplayMode( fb, thisMode, &thisFlags))
393 continue;
394
395 // make sure it does 16/32 && requested mode
396 err = fb->getInformationForDisplayMode( thisMode, &info);
397 if( err)
398 continue;
399 if( 0 == info.maxDepthIndex)
400 continue;
401 #if 0
402 kprintf("%d x %d @ %d = %x\n", info.nominalWidth, info.nominalHeight,
403 info.refreshRate >> 16, thisFlags);
404 #endif
405
406 if( 0 == (thisFlags & kDisplayModeValidFlag))
407 continue;
408
409 foundForced = (param
410 && (info.nominalWidth == lookWidth)
411 && (info.nominalHeight == lookHeight)
412 && (((info.refreshRate + 0x8000) >> 16) == lookRefresh) );
413
414 if( (thisFlags & kDisplayModeDefaultFlag)
415 && ((info.nominalWidth < kAquaMinWidth)
416 || (info.nominalHeight < kAquaMinHeight)) ) {
417
418 thisFlags &= ~kDisplayModeDefaultFlag;
419 killedDefault = true;
420 haveSubst = false;
421
422 } else if( killedDefault
423 && (info.nominalWidth >= kAquaMinWidth)
424 && (info.nominalHeight >= kAquaMinHeight) ) {
425
426 if( thisFlags & kDisplayModeSafeFlag) {
427 thisFlags |= kDisplayModeDefaultFlag;
428 killedDefault = false;
429 } else if( !haveSubst) {
430 thisFlags |= kDisplayModeDefaultFlag;
431 haveSubst = true;
432 }
433 }
434
435 if( foundForced
436 || (thisFlags & kDisplayModeDefaultFlag)
437 || (((bestFlags & kDisplayModeDefaultFlag) == 0)
438 && (thisFlags & kDisplayModeSafeFlag)) ) {
439
440 bestMode = thisMode;
441 bestFlags = thisFlags;
442
443 bestDepth = bitsToIndex[ lookDepth / 8 ];
444 if( bestDepth > info.maxDepthIndex)
445 bestDepth = info.maxDepthIndex;
446
447 if( foundForced)
448 break;
449 }
450 }
451
452 IODelete( allModes, IODisplayModeID, numModes );
453
454 if( bestMode) {
455 *mode = bestMode;
456 *depth = bestDepth;
457 return( kIOReturnSuccess);
458 } else
459 #endif /* DODEFAULTMODE */
460 return( kIOReturnUnsupported);
461 }
462
463 // Determine a startup mode given the framebuffer & displays
464
465 IOReturn IODisplayWrangler::findStartupMode( IOFramebuffer * fb )
466 {
467 IODisplayModeID mode;
468 IOIndex depth;
469 IODisplayModeID startMode;
470 IOIndex startDepth;
471 IOReturn err;
472
473 fb->getCurrentDisplayMode( &mode, &depth);
474 err = fb->getStartupDisplayMode( &startMode, &startDepth );
475 if( err) {
476 startMode = mode;
477 startDepth = depth;
478 }
479
480 #if DODEFAULTMODE
481 IODisplayModeInformation info;
482 UInt32 startFlags = 0;
483
484 do {
485 err = getFlagsForDisplayMode( fb, startMode, &startFlags );
486 if( err)
487 continue;
488 err = fb->getInformationForDisplayMode( startMode, &info);
489 if( err)
490 continue;
491
492 if( (info.nominalWidth < kAquaMinWidth)
493 || (info.nominalHeight < kAquaMinHeight)) {
494 err = kIOReturnNoResources;
495 continue;
496 }
497
498 if( startDepth == 2)
499 startDepth = 1;
500
501 if( (startDepth == 0) && (info.maxDepthIndex > 0))
502 startDepth = 1;
503
504 } while( false );
505
506 if( err
507 || (startDepth == 0)
508 || ((startFlags & kDisplayModeValidFlag)
509 != kDisplayModeValidFlag) ) {
510 // look for default
511 err = getDefaultMode( fb, &startMode, &startDepth );
512 }
513 #endif /* DODEFAULTMODE */
514 if( (startMode != mode) || (startDepth != depth))
515 fb->setDisplayMode( startMode, startDepth );
516
517 fb->setupForCurrentConfig();
518
519 return( kIOReturnSuccess );
520 }
521
522 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
523
524 #define kNumber_of_power_states 5
525
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}
532
533 };
534
535
536 /*
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.
539
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.
544
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.
547
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).
552
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.
556
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".
559
560 When Power Management is turned off (aggressiveness = 0), the display is never judged idle and never dimmed.
561
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.
565
566 */
567
568
569 // **********************************************************************************
570 // initForPM
571 //
572 // **********************************************************************************
573 void IODisplayWrangler::initForPM (void )
574 {
575
576 PMinit(); // initialize superclass variables
577
578 mins_to_dim = 0;
579 use_general_aggressiveness = false;
580
581 pm_vars->thePlatform->PMRegisterDevice(0,this); // attach into the power management hierarchy
582
583 registerPowerDriver(this,ourPowerStates,kNumber_of_power_states); // register ourselves with policy-maker (us)
584
585 registerService(); // HID system is waiting for this
586
587 }
588
589
590 //*********************************************************************************
591 // setAggressiveness
592 //
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.
596 // timer.
597 //*********************************************************************************
598
599 IOReturn IODisplayWrangler::setAggressiveness ( unsigned long type, unsigned long newLevel )
600 {
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
605 }
606 }
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
611 }
612 }
613 if ( type == kPMGeneralAggressiveness ) { // general factor received
614 if ( newLevel >= kIOPowerEmergencyLevel ) { // emergency level?
615 setIdleTimerPeriod(5); // yes
616 }
617 else {
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));
621 }
622 else {
623 setIdleTimerPeriod(mins_to_dim*60);
624 }
625 }
626 else {
627 if (use_general_aggressiveness ) { // no, maybe set period
628 setIdleTimerPeriod(333-(newLevel/3));
629 }
630 }
631 }
632 }
633 super::setAggressiveness(type, newLevel);
634 return IOPMNoErr;
635 }
636
637
638 // **********************************************************************************
639 // activityTickle
640 //
641 // This is called by the HID system and calls the superclass in turn.
642 // **********************************************************************************
643
644 bool IODisplayWrangler::activityTickle ( unsigned long, unsigned long )
645 {
646 if ( rootDomain != NULL ) {
647 rootDomain->activityTickle (kIOPMSubclassPolicy);
648 }
649 return super::activityTickle (kIOPMSuperclassPolicy1,kNumber_of_power_states-1 );
650 }
651
652
653 // **********************************************************************************
654 // setPowerState
655 //
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 )
661 {
662 if ( powerStateOrdinal == 0 ) { // system is going to sleep
663 return IOPMNoErr;
664 }
665 if ( powerStateOrdinal < pm_vars->myCurrentState ) { // HI is idle, drop power
666 idleDisplays();
667 return IOPMNoErr;
668 }
669 if ( powerStateOrdinal == kNumber_of_power_states-1 ) { // there is activity, raise power
670 makeDisplaysUsable();
671 return IOPMNoErr;
672 }
673 return IOPMNoErr;
674 }
675
676
677 // **********************************************************************************
678 // makeDisplaysUsable
679 //
680 // **********************************************************************************
681 void IODisplayWrangler::makeDisplaysUsable ( void )
682 {
683 OSIterator * iter;
684 IODisplay * display;
685
686 IOTakeLock( fMatchingLock );
687
688 iter = OSCollectionIterator::withCollection( fDisplays );
689 if( iter ) {
690 while( (display = (IODisplay *) iter->getNextObject()) ) {
691 display->makeDisplayUsable();
692 }
693 iter->release();
694 }
695 IOUnlock( fMatchingLock );
696 }
697
698
699 // **********************************************************************************
700 // idleDisplays
701 //
702 // **********************************************************************************
703 void IODisplayWrangler::idleDisplays ( void )
704 {
705 OSIterator * iter;
706 IODisplay * display;
707
708 IOTakeLock( fMatchingLock );
709
710 iter = OSCollectionIterator::withCollection( fDisplays );
711 if( iter ) {
712 while( (display = (IODisplay *) iter->getNextObject()) ) {
713 display->dropOneLevel();
714 }
715 iter->release();
716 }
717 IOUnlock( fMatchingLock );
718 }
719
720