]> git.saurik.com Git - apple/xnu.git/blob - iokit/Families/IOGraphics/AppleG3SeriesDisplay.cpp
9e9e451c4ba53820615e16318607ff0266d7d7d8
[apple/xnu.git] / iokit / Families / IOGraphics / AppleG3SeriesDisplay.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 #include <IOKit/IOLib.h>
24 #include <IOKit/graphics/IODisplay.h>
25 #include <IOKit/ndrvsupport/IOMacOSVideo.h>
26
27 #include <Drivers/hidsystem/drvAppleADBDevices/AppleADBButtons.h>
28
29 #define kNumber_of_power_states 4
30 #define kNumber_of_power_levels 32
31
32 #define kScreenBit 0x01
33 #define kPowerOn 0x80
34 #define kPowerOff 0x00
35 #define kDisplayOn kScreenBit | kPowerOn
36 #define kDisplayOff kScreenBit | kPowerOff
37
38 class AppleG3SeriesDisplay : public AppleSenseDisplay
39 {
40 OSDeclareDefaultStructors(AppleG3SeriesDisplay)
41
42 private:
43
44 int current_user_brightness; // 0-31. The brightness level last selected via the brightness buttons.
45 int current_level; // 0-31. The current brightness level
46 IOService * PMUdriver; // points to PMU driver
47 int * rawTable; // points to table of raw brightess levels
48
49 // the constants used to talk with the pmu:
50 enum {
51 kPMUpower1Read = 0x19, // more power status (DBLite)
52 kPMUReadBrightness = 0x49, // read the brightness value
53 kPMUpower1Cntl = 0x11, // more power control (DBLite)
54 kPMUSetBrightness = 0x41 // set screen brightness
55 };
56
57 // We need this to callPlatformFunction when sending to sendMiscCommand
58 typedef struct SendMiscCommandParameterBlock {
59 int command;
60 IOByteCount sLength;
61 UInt8 *sBuffer;
62 IOByteCount *rLength;
63 UInt8 *rBuffer;
64 } SendMiscCommandParameterBlock;
65 typedef SendMiscCommandParameterBlock *SendMiscCommandParameterBlockPtr;
66
67 // A simpler way to interface with the pmu SendMiscCommand
68 IOReturn localSendMiscCommand(int command, IOByteCount sLength, UInt8 *sBuffer, IOByteCount *rLength, UInt8 *rBuffer);
69
70 public:
71 IOService * probe ( IOService *, SInt32 * );
72 virtual void initForPM ( IOService* );
73 virtual IOReturn setPowerState ( unsigned long, IOService* );
74 virtual unsigned long maxCapabilityForDomainState ( IOPMPowerFlags );
75 virtual unsigned long initialPowerStateForDomainState ( IOPMPowerFlags );
76 virtual unsigned long powerStateForDomainState ( IOPMPowerFlags );
77 virtual void ourButtonHandler ( unsigned int );
78 virtual void setBrightness ( long );
79 };
80
81 void upButtonHandler(AppleG3SeriesDisplay *);
82 void downButtonHandler(AppleG3SeriesDisplay *);
83
84
85 /*
86 The actual display panel has 128 power levels. Copying the MacOS, we only implement 32 of them.
87 We further divide the 32 into four IOKit power states which we export to our superclass.
88
89 In the lowest state, the display is off. This state consists of only one of the 32 power levels, the lowest.
90 In the next state it is in the dimmest usable state. This state also consists of only one of the 32 levels, the second lowest.
91 The next state is also dim and consists of seven of the 32 levels.
92 The highest state consists of the highest 23 levels.
93
94 The display has no state or configuration or programming that would be saved/restored over power state changes,
95 and the driver does not register with the superclass as an interested driver.
96
97 This driver doesn't have much to do. It changes between the four power state brightnesses on command
98 from the superclass, and it raises and lowers the display brightness by one of the 32 brightness levels
99 when it receives a brightness-button interrupt from the ADB stack.
100
101 The only smart thing it does is keep track of which of the 32 brightness levels the user has selected by button, and it
102 never exceeds that on command from the display device object. It only raises above that on an brightness-up-button
103 interrupt.
104
105 */
106
107
108 static IOPMPowerState ourPowerStates[kNumber_of_power_states] = {
109 {1,0,0,0,0,0,0,0,0,0,0,0},
110 {1,IOPMDeviceUsable,0,IOPMPowerOn,0,0,0,0,0,0,0,0},
111 {1,IOPMDeviceUsable,0,IOPMPowerOn,0,0,0,0,0,0,0,0},
112 {1,IOPMDeviceUsable+IOPMMaxPerformance,0,IOPMPowerOn,0,0,0,0,0,0,0,0}
113 };
114
115 static int max_brightness_level[kNumber_of_power_states] = {0,1,8,31};
116
117 static int HooperTable[ ] = {127,71,69,67,65,63,61,59,
118 58,56,54,52,50,48,46,44,
119 42,40,38,37,35,33,31,29,
120 27,25,23,21,19,18,16,14 };
121
122 bool ourNotificationHandler( OSObject *, void *, IOService * );
123
124 #define super AppleSenseDisplay
125
126 OSDefineMetaClassAndStructors(AppleG3SeriesDisplay, AppleSenseDisplay)
127
128
129 // **********************************************************************************
130 // probe
131 //
132 // **********************************************************************************
133 IOService * AppleG3SeriesDisplay::probe ( IOService * provider, SInt32 * score )
134 {
135 IOFramebuffer * framebuffer;
136 IOService * ret = 0;
137 UInt32 displayType;
138 IOIndex ourIndex;
139
140 do {
141 if ( 0 == super::probe( provider, score ) ) {
142 continue;
143 }
144
145 framebuffer = (IOFramebuffer *)getConnection()->getFramebuffer(); // point to our framebuffer
146 ourIndex = getConnection()->getConnection(); // get our connection index on this framebuffer
147
148 if ( kIOReturnSuccess != framebuffer->getAppleSense(ourIndex,NULL,NULL,NULL,&displayType) ) {
149 continue;
150 }
151
152 if ( !(displayType == kPanelTFTConnect) ) { // does it have a panel attached?
153 continue; // no
154 }
155 ret = this; // yes, we will control the panel
156
157 } while ( false );
158
159 return ( ret );
160 }
161
162 // **********************************************************************************
163 // localSendMiscCommand
164 //
165 // **********************************************************************************
166 IOReturn AppleG3SeriesDisplay::localSendMiscCommand(int command, IOByteCount sLength, UInt8 *sBuffer, IOByteCount *rLength, UInt8 *rBuffer)
167 {
168 IOReturn returnValue = kIOReturnError;
169
170 // The poupose of this method is to free us from the pain to create a parameter block each time
171 // we wish to talk to the pmu:
172 SendMiscCommandParameterBlock prmBlock = {command, sLength, sBuffer, rLength, rBuffer};
173
174 IOLog("AppleG3SeriesDisplay::localSendMiscCommand 0x%02x %d 0x%08lx 0x%08lx 0x%08lx\n",
175 command, sLength, sBuffer, rLength, rBuffer);
176
177 if (PMUdriver != NULL) {
178 IOLog("AppleG3SeriesDisplay::localSendMiscCommand calling PMUdriver->callPlatformFunction\n");
179 returnValue = PMUdriver->callPlatformFunction("sendMiscCommand", true, (void*)&prmBlock, NULL, NULL, NULL);
180 }
181
182 // If we are here we do not have a dreive to talk to:
183 IOLog("AppleG3SeriesDisplay::localSendMiscCommand end 0x%08lx\n", returnValue);
184
185 return returnValue;
186 }
187
188 // **********************************************************************************
189 // initForPM
190 //
191 // This method overrides the one in IODisplay.h to do PowerBook-only
192 // power management of the display.
193 // **********************************************************************************
194 void AppleG3SeriesDisplay::initForPM ( IOService * provider )
195 {
196 unsigned long i;
197
198 UInt8 PMUreceiveBuffer[10]; // (I think 1 is enough, but it scares me)
199 IOByteCount unused = sizeof(PMUreceiveBuffer);
200
201 displayPMVars->powerControllable = true;
202
203 PMinit(); // initialize superclass variables
204
205 PMUdriver = waitForService(serviceMatching("ApplePMU"));
206
207 rawTable = HooperTable;
208
209 localSendMiscCommand(kPMUpower1Read,0, NULL, &unused,PMUreceiveBuffer);
210
211 if ( PMUreceiveBuffer[0] & kScreenBit ) { // is the screen currently on?
212 unused = sizeof(PMUreceiveBuffer);
213 localSendMiscCommand(kPMUReadBrightness,0, NULL, &unused,PMUreceiveBuffer); // yes, figure out the brightness
214 current_user_brightness = kNumber_of_power_levels - 1; // ( in case the for-loop doesn't break)
215 current_level = kNumber_of_power_levels - 1;
216
217 for ( i = 0; i < kNumber_of_power_levels; i++ ) {
218 if ( PMUreceiveBuffer[0] >= rawTable[i] ) {
219 current_user_brightness = i;
220 current_level = i;
221 break;
222 }
223 }
224 }
225 else { // no
226 current_user_brightness = 0;
227 current_level = 0;
228 }
229
230 addNotification( gIOPublishNotification,serviceMatching("AppleADBButtons"), // look for the button driver
231 (IOServiceNotificationHandler)ourNotificationHandler, this, 0 );
232
233 provider->joinPMtree(this); // attach into the power management hierarchy
234 registerPowerDriver(this,ourPowerStates,kNumber_of_power_states); // register with policy-maker (us)
235 }
236
237
238 // **********************************************************************************
239 // ourNotificationHandler
240 //
241 // The ADB button driver has appeared. Tell it we are interested in the
242 // brightness-up button and the brightness-down button.
243 // **********************************************************************************
244 bool ourNotificationHandler( OSObject * us, void * ref, IOService * yourDevice )
245 {
246 if ( yourDevice != NULL ) {
247 ((AppleADBButtons *)yourDevice)->registerForButton(kBrightness_up,(IOService *)us,(button_handler)upButtonHandler,true);
248 ((AppleADBButtons *)yourDevice)->registerForButton(kBrightness_down,(IOService *)us,(button_handler)downButtonHandler,true);
249 }
250 return true;
251 }
252
253
254 // **********************************************************************************
255 // setPowerState
256 //
257 // All power state changes require a call to the PMU driver, which
258 // blocks the thread till the command completes.
259 // **********************************************************************************
260 IOReturn AppleG3SeriesDisplay::setPowerState ( unsigned long powerStateOrdinal, IOService* whatDevice )
261 {
262 UInt8 displayOn = kDisplayOn;
263 UInt8 displayOff = kDisplayOff;
264 unsigned long i;
265
266 if ( powerStateOrdinal < kNumber_of_power_states ) {
267 if ( powerStateOrdinal > pm_vars->myCurrentState ) { // raising power
268 if ( pm_vars->myCurrentState == 0 ) { // is it currently off?
269 IOByteCount unused = 0;
270 localSendMiscCommand(kPMUpower1Cntl,1, &displayOn, &unused,NULL);
271 }
272 current_level = max_brightness_level[powerStateOrdinal];
273 if ( current_user_brightness < current_level ) {
274 current_level = current_user_brightness; // don't exceed what the user used to have it at
275 }
276 setBrightness(current_level);
277 // If we are still higher than we need to be, request a lower state
278 for ( i = 0; i < kNumber_of_power_states; i++ ) { // figure out what state we should be in
279 if ( current_level <= max_brightness_level[i] ) {
280 break;
281 }
282 }
283 if ( pm_vars->myCurrentState > i ) {
284 changePowerStateToPriv(i);
285 }
286 }
287
288 if ( powerStateOrdinal < pm_vars->myCurrentState ) { // lowering power
289 if (powerStateOrdinal == 0 ) { // going all the way off?
290 IOByteCount unused = 0;
291 localSendMiscCommand(kPMUpower1Cntl,1, &displayOff, &unused,NULL); // yes
292 current_level = max_brightness_level[powerStateOrdinal];
293 }
294 else {
295 if ( current_level > max_brightness_level[powerStateOrdinal] ) { // no
296 current_level = max_brightness_level[powerStateOrdinal];
297 setBrightness(current_level);
298 }
299 }
300 }
301 }
302 return IOPMAckImplied;
303 }
304
305
306 // **********************************************************************************
307 // maxCapabilityForDomainState
308 //
309 // This simple device needs only power. If the power domain is supplying
310 // power, the display can go to its highest state. If there is no power
311 // it can only be in its lowest state, which is off.
312 // **********************************************************************************
313 unsigned long AppleG3SeriesDisplay::maxCapabilityForDomainState ( IOPMPowerFlags domainState )
314 {
315 if ( domainState & IOPMPowerOn ) {
316 return kNumber_of_power_states-1;
317 }
318 return 0;
319 }
320
321
322 // **********************************************************************************
323 // initialPowerStateForDomainState
324 //
325 // The power domain may be changing state. If power is on in the new
326 // state, that will not affect our state at all. If domain power is off,
327 // we can attain only our lowest state, which is off.
328 // **********************************************************************************
329 unsigned long AppleG3SeriesDisplay::initialPowerStateForDomainState ( IOPMPowerFlags domainState )
330 {
331 long unsigned i;
332
333 if ( domainState & IOPMPowerOn ) { // domain has power
334 for ( i = 0; i < kNumber_of_power_states; i++ ) { // find power state that has our current
335 if ( current_level <= max_brightness_level[i] ) { // brightness level
336 return i;
337 break;
338 }
339 }
340 }
341 return 0; // domain is down, so display is off
342 }
343
344
345 // **********************************************************************************
346 // powerStateForDomainState
347 //
348 // The power domain may be changing state. If power is on in the new
349 // state, that will not affect our state at all. If domain power is off,
350 // we can attain only our lowest state, which is off.
351 // **********************************************************************************
352 unsigned long AppleG3SeriesDisplay::powerStateForDomainState ( IOPMPowerFlags domainState )
353 {
354 long unsigned i;
355
356 if ( domainState & IOPMPowerOn ) { // domain has power
357 for ( i = 0; i < kNumber_of_power_states; i++ ) { // find power state that has our current
358 if ( current_level <= max_brightness_level[i] ) { // brightness level
359 return i;
360 }
361 }
362 }
363 return 0; // domain is down, so display is off
364 }
365
366
367 // **********************************************************************************
368 // upButtonHandler
369 //
370 // The display-brightness-up button just went down.
371 // We are running on a new thread made by the ADB Button driver
372 // **********************************************************************************
373 void upButtonHandler(AppleG3SeriesDisplay * us )
374 {
375 ((AppleG3SeriesDisplay *)us)->ourButtonHandler(kBrightness_up);
376 }
377
378
379 // **********************************************************************************
380 // downButtonHandler
381 //
382 // The display-brightness-down button just went down.
383 // We are running on a new thread made by the ADB Button driver
384 // **********************************************************************************
385 void downButtonHandler(AppleG3SeriesDisplay * us )
386 {
387 ((AppleG3SeriesDisplay *)us)->ourButtonHandler(kBrightness_down);
388 }
389
390
391 // **********************************************************************************
392 // ourButtonHandler
393 //
394 // Alter the backlight brightness up or down by one increment.
395 // This involves a call to the PMU driver, which will block the thread.
396 // **********************************************************************************
397 void AppleG3SeriesDisplay::ourButtonHandler ( unsigned int keycode )
398 { // If we are idle, ignore the button.
399 // The display will be made usable
400 if ( ! displayPMVars->displayIdle ) { // by the DisplayWrangler
401 switch (keycode) {
402 case kBrightness_up: // The brightness-up button has just been pressed
403 // We make sure the brightness is not above the maximum
404 // brightness level of our current power state. If it
405 // is too high, we ask the device to raise power.
406 if (current_level < max_brightness_level[pm_vars->myCurrentState] ) {
407 current_level++;
408 current_user_brightness = current_level;
409 setBrightness(current_level);
410 }
411 else {
412 if ( pm_vars->myCurrentState < (kNumber_of_power_states-1) ) {
413 current_user_brightness++; // increment user's desire
414 if ( changePowerStateToPriv(pm_vars->myCurrentState + 1) != IOPMNoErr ) { // request higher power
415 current_user_brightness--; // can't
416 }
417 }
418 }
419 break;
420
421 case kBrightness_down: // The brightness-down button has just been pressed
422 // We lower the brightness, and if that takes us into a
423 // lower power state, we tell our parent about it.
424 if ( pm_vars->myCurrentState > 0 ) { // don't lower if in lowest (off) state
425 if ( current_level > 0 ) {
426 current_level--;
427 current_user_brightness = current_level;
428 setBrightness(current_level);
429 if (current_level <= max_brightness_level[pm_vars->myCurrentState - 1] ) { // if this takes us into the next lower state
430 changePowerStateToPriv(pm_vars->myCurrentState - 1); // request lower power
431 }
432 }
433 }
434 break;
435 }
436 }
437 }
438
439
440 // **********************************************************************************
441 // setBrightness
442 //
443 // Instruct PMU to set the display brightness.
444 // This will block the thread while the command completes.
445 // **********************************************************************************
446 void AppleG3SeriesDisplay::setBrightness ( long brightness )
447 {
448 IOByteCount unused = 0;
449 UInt8 setBrightnessBuffer;
450
451 setBrightnessBuffer = (UInt8)rawTable[brightness];
452 localSendMiscCommand(kPMUSetBrightness,1, &setBrightnessBuffer, &unused,NULL);
453 }