Add patches accepted for 2.6.30-rc1
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / releases / upstream / 2.6.30-rc1 / 0014-thinkpad-acpi-rework-brightness-support.patch
blob08364c62059fbd58c4cbde1bae0dc194d11c3086
1 From 0e501834f8c2ba7de2a56e332d346dcf4ac0b593 Mon Sep 17 00:00:00 2001
2 From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
3 Date: Sat, 4 Apr 2009 04:25:53 +0000
4 Subject: thinkpad-acpi: rework brightness support
6 Refactor and redesign the brightness control backend...
8 In order to fix bugzilla #11750...
10 Add a new brightness control mode: support direct NVRAM checkpointing
11 of the backlight level (i.e. store directly to NVRAM without the need
12 for UCMS calls), and use that together with the EC-based control.
13 Disallow UCMS+EC, thus avoiding races with the SMM firmware.
15 Switch the models that define HBRV (EC Brightness Value) in the DSDT
16 to the new mode. These are: T40-T43, R50-R52, R50e, R51e, X31-X41.
18 Change the default for all other IBM ThinkPads to UCMS-only. The
19 Lenovo models already default to UCMS-only.
21 Reported-by: Alexey Fisher <bug-track@fisher-privat.net>
22 Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
23 Signed-off-by: Len Brown <len.brown@intel.com>
24 ---
25 Documentation/laptops/thinkpad-acpi.txt | 12 +-
26 drivers/platform/x86/thinkpad_acpi.c | 317 +++++++++++++++++++++----------
27 2 files changed, 227 insertions(+), 102 deletions(-)
29 diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
30 index 25ed43d..3d76507 100644
31 --- a/Documentation/laptops/thinkpad-acpi.txt
32 +++ b/Documentation/laptops/thinkpad-acpi.txt
33 @@ -1157,10 +1157,15 @@ display backlight brightness control methods have 16 levels, ranging
34 from 0 to 15.
36 There are two interfaces to the firmware for direct brightness control,
37 -EC and CMOS. To select which one should be used, use the
38 +EC and UCMS (or CMOS). To select which one should be used, use the
39 brightness_mode module parameter: brightness_mode=1 selects EC mode,
40 -brightness_mode=2 selects CMOS mode, brightness_mode=3 selects both EC
41 -and CMOS. The driver tries to auto-detect which interface to use.
42 +brightness_mode=2 selects UCMS mode, brightness_mode=3 selects EC
43 +mode with NVRAM backing (so that brightness changes are remembered
44 +across shutdown/reboot).
46 +The driver tries to select which interface to use from a table of
47 +defaults for each ThinkPad model. If it makes a wrong choice, please
48 +report this as a bug, so that we can fix it.
50 When display backlight brightness controls are available through the
51 standard ACPI interface, it is best to use it instead of this direct
52 @@ -1498,6 +1503,7 @@ to enable more than one output class, just add their values.
53 (bluetooth, WWAN, UWB...)
54 0x0008 HKEY event interface, hotkeys
55 0x0010 Fan control
56 + 0x0020 Backlight brightness
58 There is also a kernel build option to enable more debugging
59 information, which may be necessary to debug driver problems.
60 diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
61 index 4eec770..ba3682c 100644
62 --- a/drivers/platform/x86/thinkpad_acpi.c
63 +++ b/drivers/platform/x86/thinkpad_acpi.c
64 @@ -192,6 +192,7 @@ enum {
65 #define TPACPI_DBG_RFKILL 0x0004
66 #define TPACPI_DBG_HKEY 0x0008
67 #define TPACPI_DBG_FAN 0x0010
68 +#define TPACPI_DBG_BRGHT 0x0020
70 #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off")
71 #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
72 @@ -274,7 +275,6 @@ static struct {
74 static struct {
75 u16 hotkey_mask_ff:1;
76 - u16 bright_cmos_ec_unsync:1;
77 } tp_warned;
79 struct thinkpad_id_data {
80 @@ -5526,6 +5526,20 @@ static struct ibm_struct ecdump_driver_data = {
82 #define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen"
84 +/*
85 + * ThinkPads can read brightness from two places: EC HBRV (0x31), or
86 + * CMOS NVRAM byte 0x5E, bits 0-3.
87 + *
88 + * EC HBRV (0x31) has the following layout
89 + * Bit 7: unknown function
90 + * Bit 6: unknown function
91 + * Bit 5: Z: honour scale changes, NZ: ignore scale changes
92 + * Bit 4: must be set to zero to avoid problems
93 + * Bit 3-0: backlight brightness level
94 + *
95 + * brightness_get_raw returns status data in the HBRV layout
96 + */
98 enum {
99 TP_EC_BACKLIGHT = 0x31,
101 @@ -5535,108 +5549,164 @@ enum {
102 TP_EC_BACKLIGHT_MAPSW = 0x20,
105 +enum tpacpi_brightness_access_mode {
106 + TPACPI_BRGHT_MODE_AUTO = 0, /* Not implemented yet */
107 + TPACPI_BRGHT_MODE_EC, /* EC control */
108 + TPACPI_BRGHT_MODE_UCMS_STEP, /* UCMS step-based control */
109 + TPACPI_BRGHT_MODE_ECNVRAM, /* EC control w/ NVRAM store */
110 + TPACPI_BRGHT_MODE_MAX
113 static struct backlight_device *ibm_backlight_device;
114 -static int brightness_mode;
116 +static enum tpacpi_brightness_access_mode brightness_mode =
117 + TPACPI_BRGHT_MODE_MAX;
119 static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */
121 static struct mutex brightness_mutex;
124 - * ThinkPads can read brightness from two places: EC 0x31, or
125 - * CMOS NVRAM byte 0x5E, bits 0-3.
127 - * EC 0x31 has the following layout
128 - * Bit 7: unknown function
129 - * Bit 6: unknown function
130 - * Bit 5: Z: honour scale changes, NZ: ignore scale changes
131 - * Bit 4: must be set to zero to avoid problems
132 - * Bit 3-0: backlight brightness level
134 - * brightness_get_raw returns status data in the EC 0x31 layout
135 - */
136 -static int brightness_get_raw(int *status)
137 +/* NVRAM brightness access,
138 + * call with brightness_mutex held! */
139 +static unsigned int tpacpi_brightness_nvram_get(void)
141 - u8 lec = 0, lcmos = 0, level = 0;
142 + u8 lnvram;
144 - if (brightness_mode & 1) {
145 - if (!acpi_ec_read(TP_EC_BACKLIGHT, &lec))
146 - return -EIO;
147 - level = lec & TP_EC_BACKLIGHT_LVLMSK;
148 - };
149 - if (brightness_mode & 2) {
150 - lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
151 - & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
152 - >> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
153 - lcmos &= (tp_features.bright_16levels)? 0x0f : 0x07;
154 - level = lcmos;
157 - if (brightness_mode == 3) {
158 - *status = lec; /* Prefer EC, CMOS is just a backing store */
159 - lec &= TP_EC_BACKLIGHT_LVLMSK;
160 - if (lec == lcmos)
161 - tp_warned.bright_cmos_ec_unsync = 0;
162 - else {
163 - if (!tp_warned.bright_cmos_ec_unsync) {
164 - printk(TPACPI_ERR
165 - "CMOS NVRAM (%u) and EC (%u) do not "
166 - "agree on display brightness level\n",
167 - (unsigned int) lcmos,
168 - (unsigned int) lec);
169 - tp_warned.bright_cmos_ec_unsync = 1;
171 + lnvram = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
172 + & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
173 + >> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
174 + lnvram &= (tp_features.bright_16levels) ? 0x0f : 0x07;
176 + return lnvram;
179 +static void tpacpi_brightness_checkpoint_nvram(void)
181 + u8 lec = 0;
182 + u8 b_nvram;
184 + if (brightness_mode != TPACPI_BRGHT_MODE_ECNVRAM)
185 + return;
187 + vdbg_printk(TPACPI_DBG_BRGHT,
188 + "trying to checkpoint backlight level to NVRAM...\n");
190 + if (mutex_lock_killable(&brightness_mutex) < 0)
191 + return;
193 + if (unlikely(!acpi_ec_read(TP_EC_BACKLIGHT, &lec)))
194 + goto unlock;
195 + lec &= TP_EC_BACKLIGHT_LVLMSK;
196 + b_nvram = nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS);
198 + if (lec != ((b_nvram & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
199 + >> TP_NVRAM_POS_LEVEL_BRIGHTNESS)) {
200 + /* NVRAM needs update */
201 + b_nvram &= ~(TP_NVRAM_MASK_LEVEL_BRIGHTNESS <<
202 + TP_NVRAM_POS_LEVEL_BRIGHTNESS);
203 + b_nvram |= lec;
204 + nvram_write_byte(b_nvram, TP_NVRAM_ADDR_BRIGHTNESS);
205 + dbg_printk(TPACPI_DBG_BRGHT,
206 + "updated NVRAM backlight level to %u (0x%02x)\n",
207 + (unsigned int) lec, (unsigned int) b_nvram);
208 + } else
209 + vdbg_printk(TPACPI_DBG_BRGHT,
210 + "NVRAM backlight level already is %u (0x%02x)\n",
211 + (unsigned int) lec, (unsigned int) b_nvram);
213 +unlock:
214 + mutex_unlock(&brightness_mutex);
218 +/* call with brightness_mutex held! */
219 +static int tpacpi_brightness_get_raw(int *status)
221 + u8 lec = 0;
223 + switch (brightness_mode) {
224 + case TPACPI_BRGHT_MODE_UCMS_STEP:
225 + *status = tpacpi_brightness_nvram_get();
226 + return 0;
227 + case TPACPI_BRGHT_MODE_EC:
228 + case TPACPI_BRGHT_MODE_ECNVRAM:
229 + if (unlikely(!acpi_ec_read(TP_EC_BACKLIGHT, &lec)))
230 return -EIO;
232 - } else {
233 - *status = level;
234 + *status = lec;
235 + return 0;
236 + default:
237 + return -ENXIO;
241 +/* call with brightness_mutex held! */
242 +/* do NOT call with illegal backlight level value */
243 +static int tpacpi_brightness_set_ec(unsigned int value)
245 + u8 lec = 0;
247 + if (unlikely(!acpi_ec_read(TP_EC_BACKLIGHT, &lec)))
248 + return -EIO;
250 + if (unlikely(!acpi_ec_write(TP_EC_BACKLIGHT,
251 + (lec & TP_EC_BACKLIGHT_CMDMSK) |
252 + (value & TP_EC_BACKLIGHT_LVLMSK))))
253 + return -EIO;
255 + return 0;
258 +/* call with brightness_mutex held! */
259 +static int tpacpi_brightness_set_ucmsstep(unsigned int value)
261 + int cmos_cmd, inc;
262 + unsigned int current_value, i;
264 + current_value = tpacpi_brightness_nvram_get();
266 + if (value == current_value)
267 + return 0;
269 + cmos_cmd = (value > current_value) ?
270 + TP_CMOS_BRIGHTNESS_UP :
271 + TP_CMOS_BRIGHTNESS_DOWN;
272 + inc = (value > current_value) ? 1 : -1;
274 + for (i = current_value; i != value; i += inc)
275 + if (issue_thinkpad_cmos_command(cmos_cmd))
276 + return -EIO;
278 return 0;
281 /* May return EINTR which can always be mapped to ERESTARTSYS */
282 -static int brightness_set(int value)
283 +static int brightness_set(unsigned int value)
285 - int cmos_cmd, inc, i, res;
286 - int current_value;
287 - int command_bits;
288 + int res;
290 if (value > ((tp_features.bright_16levels)? 15 : 7) ||
291 value < 0)
292 return -EINVAL;
294 + vdbg_printk(TPACPI_DBG_BRGHT,
295 + "set backlight level to %d\n", value);
297 res = mutex_lock_killable(&brightness_mutex);
298 if (res < 0)
299 return res;
301 - res = brightness_get_raw(&current_value);
302 - if (res < 0)
303 - goto errout;
305 - command_bits = current_value & TP_EC_BACKLIGHT_CMDMSK;
306 - current_value &= TP_EC_BACKLIGHT_LVLMSK;
308 - cmos_cmd = value > current_value ?
309 - TP_CMOS_BRIGHTNESS_UP :
310 - TP_CMOS_BRIGHTNESS_DOWN;
311 - inc = (value > current_value)? 1 : -1;
313 - res = 0;
314 - for (i = current_value; i != value; i += inc) {
315 - if ((brightness_mode & 2) &&
316 - issue_thinkpad_cmos_command(cmos_cmd)) {
317 - res = -EIO;
318 - goto errout;
320 - if ((brightness_mode & 1) &&
321 - !acpi_ec_write(TP_EC_BACKLIGHT,
322 - (i + inc) | command_bits)) {
323 - res = -EIO;
324 - goto errout;;
326 + switch (brightness_mode) {
327 + case TPACPI_BRGHT_MODE_EC:
328 + case TPACPI_BRGHT_MODE_ECNVRAM:
329 + res = tpacpi_brightness_set_ec(value);
330 + break;
331 + case TPACPI_BRGHT_MODE_UCMS_STEP:
332 + res = tpacpi_brightness_set_ucmsstep(value);
333 + break;
334 + default:
335 + res = -ENXIO;
338 -errout:
339 mutex_unlock(&brightness_mutex);
340 return res;
342 @@ -5645,21 +5715,34 @@ errout:
344 static int brightness_update_status(struct backlight_device *bd)
346 - /* it is the backlight class's job (caller) to handle
347 - * EINTR and other errors properly */
348 - return brightness_set(
349 + unsigned int level =
350 (bd->props.fb_blank == FB_BLANK_UNBLANK &&
351 bd->props.power == FB_BLANK_UNBLANK) ?
352 - bd->props.brightness : 0);
353 + bd->props.brightness : 0;
355 + dbg_printk(TPACPI_DBG_BRGHT,
356 + "backlight: attempt to set level to %d\n",
357 + level);
359 + /* it is the backlight class's job (caller) to handle
360 + * EINTR and other errors properly */
361 + return brightness_set(level);
364 static int brightness_get(struct backlight_device *bd)
366 int status, res;
368 - res = brightness_get_raw(&status);
369 + res = mutex_lock_killable(&brightness_mutex);
370 if (res < 0)
371 - return 0; /* FIXME: teach backlight about error handling */
372 + return 0;
374 + res = tpacpi_brightness_get_raw(&status);
376 + mutex_unlock(&brightness_mutex);
378 + if (res < 0)
379 + return 0;
381 return status & TP_EC_BACKLIGHT_LVLMSK;
383 @@ -5709,7 +5792,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
386 if (!brightness_enable) {
387 - dbg_printk(TPACPI_DBG_INIT,
388 + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
389 "brightness support disabled by "
390 "module parameter\n");
391 return 1;
392 @@ -5724,20 +5807,38 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
393 if (b == 16)
394 tp_features.bright_16levels = 1;
396 - if (!brightness_mode) {
397 - if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO)
398 - brightness_mode = 2;
399 - else
400 - brightness_mode = 3;
401 + /*
402 + * Check for module parameter bogosity, note that we
403 + * init brightness_mode to TPACPI_BRGHT_MODE_MAX in order to be
404 + * able to detect "unspecified"
405 + */
406 + if (brightness_mode > TPACPI_BRGHT_MODE_MAX)
407 + return -EINVAL;
409 - dbg_printk(TPACPI_DBG_INIT, "selected brightness_mode=%d\n",
410 - brightness_mode);
412 + /* TPACPI_BRGHT_MODE_AUTO not implemented yet, just use default */
413 + if (brightness_mode == TPACPI_BRGHT_MODE_AUTO ||
414 + brightness_mode == TPACPI_BRGHT_MODE_MAX) {
415 + if (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) {
416 + /*
417 + * IBM models that define HBRV probably have
418 + * EC-based backlight level control
419 + */
420 + if (acpi_evalf(ec_handle, NULL, "HBRV", "qd"))
421 + /* T40-T43, R50-R52, R50e, R51e, X31-X41 */
422 + brightness_mode = TPACPI_BRGHT_MODE_ECNVRAM;
423 + else
424 + /* all other IBM ThinkPads */
425 + brightness_mode = TPACPI_BRGHT_MODE_UCMS_STEP;
426 + } else
427 + /* All Lenovo ThinkPads */
428 + brightness_mode = TPACPI_BRGHT_MODE_UCMS_STEP;
430 - if (brightness_mode > 3)
431 - return -EINVAL;
432 + dbg_printk(TPACPI_DBG_BRGHT,
433 + "selected brightness_mode=%d\n",
434 + brightness_mode);
437 - if (brightness_get_raw(&b) < 0)
438 + if (tpacpi_brightness_get_raw(&b) < 0)
439 return 1;
441 if (tp_features.bright_16levels)
442 @@ -5751,7 +5852,8 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
443 printk(TPACPI_ERR "Could not register backlight device\n");
444 return PTR_ERR(ibm_backlight_device);
446 - vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n");
447 + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
448 + "brightness is supported\n");
450 ibm_backlight_device->props.max_brightness =
451 (tp_features.bright_16levels)? 15 : 7;
452 @@ -5761,13 +5863,25 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
453 return 0;
456 +static void brightness_suspend(pm_message_t state)
458 + tpacpi_brightness_checkpoint_nvram();
461 +static void brightness_shutdown(void)
463 + tpacpi_brightness_checkpoint_nvram();
466 static void brightness_exit(void)
468 if (ibm_backlight_device) {
469 - vdbg_printk(TPACPI_DBG_EXIT,
470 + vdbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_BRGHT,
471 "calling backlight_device_unregister()\n");
472 backlight_device_unregister(ibm_backlight_device);
475 + tpacpi_brightness_checkpoint_nvram();
478 static int brightness_read(char *p)
479 @@ -5814,6 +5928,9 @@ static int brightness_write(char *buf)
480 return -EINVAL;
483 + tpacpi_disclose_usertask("procfs brightness",
484 + "set level to %d\n", level);
487 * Now we know what the final level should be, so we try to set it.
488 * Doing it this way makes the syscall restartable in case of EINTR
489 @@ -5827,6 +5944,8 @@ static struct ibm_struct brightness_driver_data = {
490 .read = brightness_read,
491 .write = brightness_write,
492 .exit = brightness_exit,
493 + .suspend = brightness_suspend,
494 + .shutdown = brightness_shutdown,
497 /*************************************************************************
498 @@ -7465,10 +7584,10 @@ module_param_named(fan_control, fan_control_allowed, bool, 0);
499 MODULE_PARM_DESC(fan_control,
500 "Enables setting fan parameters features when true");
502 -module_param_named(brightness_mode, brightness_mode, int, 0);
503 +module_param_named(brightness_mode, brightness_mode, uint, 0);
504 MODULE_PARM_DESC(brightness_mode,
505 "Selects brightness control strategy: "
506 - "0=auto, 1=EC, 2=CMOS, 3=both");
507 + "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM");
509 module_param(brightness_enable, uint, 0);
510 MODULE_PARM_DESC(brightness_enable,
512 1.6.2.1