aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86
diff options
context:
space:
mode:
authorHenrique de Moraes Holschuh <hmh@hmh.eng.br>2009-04-04 00:25:53 -0400
committerLen Brown <len.brown@intel.com>2009-04-04 03:14:53 -0400
commit0e501834f8c2ba7de2a56e332d346dcf4ac0b593 (patch)
tree97aa4223015bbb43ffeced0812b243e42f8ba872 /drivers/platform/x86
parent74a60c0f828016456fc635feae388ffd12bb3bb9 (diff)
thinkpad-acpi: rework brightness support
Refactor and redesign the brightness control backend... In order to fix bugzilla #11750... Add a new brightness control mode: support direct NVRAM checkpointing of the backlight level (i.e. store directly to NVRAM without the need for UCMS calls), and use that together with the EC-based control. Disallow UCMS+EC, thus avoiding races with the SMM firmware. Switch the models that define HBRV (EC Brightness Value) in the DSDT to the new mode. These are: T40-T43, R50-R52, R50e, R51e, X31-X41. Change the default for all other IBM ThinkPads to UCMS-only. The Lenovo models already default to UCMS-only. Reported-by: Alexey Fisher <bug-track@fisher-privat.net> Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/platform/x86')
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c317
1 files changed, 218 insertions, 99 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 4eec77032a71..ba3682c5cde0 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -192,6 +192,7 @@ enum {
192#define TPACPI_DBG_RFKILL 0x0004 192#define TPACPI_DBG_RFKILL 0x0004
193#define TPACPI_DBG_HKEY 0x0008 193#define TPACPI_DBG_HKEY 0x0008
194#define TPACPI_DBG_FAN 0x0010 194#define TPACPI_DBG_FAN 0x0010
195#define TPACPI_DBG_BRGHT 0x0020
195 196
196#define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") 197#define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off")
197#define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") 198#define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
@@ -274,7 +275,6 @@ static struct {
274 275
275static struct { 276static struct {
276 u16 hotkey_mask_ff:1; 277 u16 hotkey_mask_ff:1;
277 u16 bright_cmos_ec_unsync:1;
278} tp_warned; 278} tp_warned;
279 279
280struct thinkpad_id_data { 280struct thinkpad_id_data {
@@ -5526,6 +5526,20 @@ static struct ibm_struct ecdump_driver_data = {
5526 5526
5527#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen" 5527#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen"
5528 5528
5529/*
5530 * ThinkPads can read brightness from two places: EC HBRV (0x31), or
5531 * CMOS NVRAM byte 0x5E, bits 0-3.
5532 *
5533 * EC HBRV (0x31) has the following layout
5534 * Bit 7: unknown function
5535 * Bit 6: unknown function
5536 * Bit 5: Z: honour scale changes, NZ: ignore scale changes
5537 * Bit 4: must be set to zero to avoid problems
5538 * Bit 3-0: backlight brightness level
5539 *
5540 * brightness_get_raw returns status data in the HBRV layout
5541 */
5542
5529enum { 5543enum {
5530 TP_EC_BACKLIGHT = 0x31, 5544 TP_EC_BACKLIGHT = 0x31,
5531 5545
@@ -5535,108 +5549,164 @@ enum {
5535 TP_EC_BACKLIGHT_MAPSW = 0x20, 5549 TP_EC_BACKLIGHT_MAPSW = 0x20,
5536}; 5550};
5537 5551
5552enum tpacpi_brightness_access_mode {
5553 TPACPI_BRGHT_MODE_AUTO = 0, /* Not implemented yet */
5554 TPACPI_BRGHT_MODE_EC, /* EC control */
5555 TPACPI_BRGHT_MODE_UCMS_STEP, /* UCMS step-based control */
5556 TPACPI_BRGHT_MODE_ECNVRAM, /* EC control w/ NVRAM store */
5557 TPACPI_BRGHT_MODE_MAX
5558};
5559
5538static struct backlight_device *ibm_backlight_device; 5560static struct backlight_device *ibm_backlight_device;
5539static int brightness_mode; 5561
5562static enum tpacpi_brightness_access_mode brightness_mode =
5563 TPACPI_BRGHT_MODE_MAX;
5564
5540static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */ 5565static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */
5541 5566
5542static struct mutex brightness_mutex; 5567static struct mutex brightness_mutex;
5543 5568
5544/* 5569/* NVRAM brightness access,
5545 * ThinkPads can read brightness from two places: EC 0x31, or 5570 * call with brightness_mutex held! */
5546 * CMOS NVRAM byte 0x5E, bits 0-3. 5571static unsigned int tpacpi_brightness_nvram_get(void)
5547 *
5548 * EC 0x31 has the following layout
5549 * Bit 7: unknown function
5550 * Bit 6: unknown function
5551 * Bit 5: Z: honour scale changes, NZ: ignore scale changes
5552 * Bit 4: must be set to zero to avoid problems
5553 * Bit 3-0: backlight brightness level
5554 *
5555 * brightness_get_raw returns status data in the EC 0x31 layout
5556 */
5557static int brightness_get_raw(int *status)
5558{ 5572{
5559 u8 lec = 0, lcmos = 0, level = 0; 5573 u8 lnvram;
5560 5574
5561 if (brightness_mode & 1) { 5575 lnvram = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
5562 if (!acpi_ec_read(TP_EC_BACKLIGHT, &lec)) 5576 & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
5563 return -EIO; 5577 >> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
5564 level = lec & TP_EC_BACKLIGHT_LVLMSK; 5578 lnvram &= (tp_features.bright_16levels) ? 0x0f : 0x07;
5565 }; 5579
5566 if (brightness_mode & 2) { 5580 return lnvram;
5567 lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) 5581}
5568 & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) 5582
5569 >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; 5583static void tpacpi_brightness_checkpoint_nvram(void)
5570 lcmos &= (tp_features.bright_16levels)? 0x0f : 0x07; 5584{
5571 level = lcmos; 5585 u8 lec = 0;
5572 } 5586 u8 b_nvram;
5573 5587
5574 if (brightness_mode == 3) { 5588 if (brightness_mode != TPACPI_BRGHT_MODE_ECNVRAM)
5575 *status = lec; /* Prefer EC, CMOS is just a backing store */ 5589 return;
5576 lec &= TP_EC_BACKLIGHT_LVLMSK; 5590
5577 if (lec == lcmos) 5591 vdbg_printk(TPACPI_DBG_BRGHT,
5578 tp_warned.bright_cmos_ec_unsync = 0; 5592 "trying to checkpoint backlight level to NVRAM...\n");
5579 else { 5593
5580 if (!tp_warned.bright_cmos_ec_unsync) { 5594 if (mutex_lock_killable(&brightness_mutex) < 0)
5581 printk(TPACPI_ERR 5595 return;
5582 "CMOS NVRAM (%u) and EC (%u) do not " 5596
5583 "agree on display brightness level\n", 5597 if (unlikely(!acpi_ec_read(TP_EC_BACKLIGHT, &lec)))
5584 (unsigned int) lcmos, 5598 goto unlock;
5585 (unsigned int) lec); 5599 lec &= TP_EC_BACKLIGHT_LVLMSK;
5586 tp_warned.bright_cmos_ec_unsync = 1; 5600 b_nvram = nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS);
5587 } 5601
5602 if (lec != ((b_nvram & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
5603 >> TP_NVRAM_POS_LEVEL_BRIGHTNESS)) {
5604 /* NVRAM needs update */
5605 b_nvram &= ~(TP_NVRAM_MASK_LEVEL_BRIGHTNESS <<
5606 TP_NVRAM_POS_LEVEL_BRIGHTNESS);
5607 b_nvram |= lec;
5608 nvram_write_byte(b_nvram, TP_NVRAM_ADDR_BRIGHTNESS);
5609 dbg_printk(TPACPI_DBG_BRGHT,
5610 "updated NVRAM backlight level to %u (0x%02x)\n",
5611 (unsigned int) lec, (unsigned int) b_nvram);
5612 } else
5613 vdbg_printk(TPACPI_DBG_BRGHT,
5614 "NVRAM backlight level already is %u (0x%02x)\n",
5615 (unsigned int) lec, (unsigned int) b_nvram);
5616
5617unlock:
5618 mutex_unlock(&brightness_mutex);
5619}
5620
5621
5622/* call with brightness_mutex held! */
5623static int tpacpi_brightness_get_raw(int *status)
5624{
5625 u8 lec = 0;
5626
5627 switch (brightness_mode) {
5628 case TPACPI_BRGHT_MODE_UCMS_STEP:
5629 *status = tpacpi_brightness_nvram_get();
5630 return 0;
5631 case TPACPI_BRGHT_MODE_EC:
5632 case TPACPI_BRGHT_MODE_ECNVRAM:
5633 if (unlikely(!acpi_ec_read(TP_EC_BACKLIGHT, &lec)))
5588 return -EIO; 5634 return -EIO;
5589 } 5635 *status = lec;
5590 } else { 5636 return 0;
5591 *status = level; 5637 default:
5638 return -ENXIO;
5592 } 5639 }
5640}
5641
5642/* call with brightness_mutex held! */
5643/* do NOT call with illegal backlight level value */
5644static int tpacpi_brightness_set_ec(unsigned int value)
5645{
5646 u8 lec = 0;
5647
5648 if (unlikely(!acpi_ec_read(TP_EC_BACKLIGHT, &lec)))
5649 return -EIO;
5650
5651 if (unlikely(!acpi_ec_write(TP_EC_BACKLIGHT,
5652 (lec & TP_EC_BACKLIGHT_CMDMSK) |
5653 (value & TP_EC_BACKLIGHT_LVLMSK))))
5654 return -EIO;
5655
5656 return 0;
5657}
5658
5659/* call with brightness_mutex held! */
5660static int tpacpi_brightness_set_ucmsstep(unsigned int value)
5661{
5662 int cmos_cmd, inc;
5663 unsigned int current_value, i;
5664
5665 current_value = tpacpi_brightness_nvram_get();
5666
5667 if (value == current_value)
5668 return 0;
5669
5670 cmos_cmd = (value > current_value) ?
5671 TP_CMOS_BRIGHTNESS_UP :
5672 TP_CMOS_BRIGHTNESS_DOWN;
5673 inc = (value > current_value) ? 1 : -1;
5674
5675 for (i = current_value; i != value; i += inc)
5676 if (issue_thinkpad_cmos_command(cmos_cmd))
5677 return -EIO;
5593 5678
5594 return 0; 5679 return 0;
5595} 5680}
5596 5681
5597/* May return EINTR which can always be mapped to ERESTARTSYS */ 5682/* May return EINTR which can always be mapped to ERESTARTSYS */
5598static int brightness_set(int value) 5683static int brightness_set(unsigned int value)
5599{ 5684{
5600 int cmos_cmd, inc, i, res; 5685 int res;
5601 int current_value;
5602 int command_bits;
5603 5686
5604 if (value > ((tp_features.bright_16levels)? 15 : 7) || 5687 if (value > ((tp_features.bright_16levels)? 15 : 7) ||
5605 value < 0) 5688 value < 0)
5606 return -EINVAL; 5689 return -EINVAL;
5607 5690
5691 vdbg_printk(TPACPI_DBG_BRGHT,
5692 "set backlight level to %d\n", value);
5693
5608 res = mutex_lock_killable(&brightness_mutex); 5694 res = mutex_lock_killable(&brightness_mutex);
5609 if (res < 0) 5695 if (res < 0)
5610 return res; 5696 return res;
5611 5697
5612 res = brightness_get_raw(&current_value); 5698 switch (brightness_mode) {
5613 if (res < 0) 5699 case TPACPI_BRGHT_MODE_EC:
5614 goto errout; 5700 case TPACPI_BRGHT_MODE_ECNVRAM:
5615 5701 res = tpacpi_brightness_set_ec(value);
5616 command_bits = current_value & TP_EC_BACKLIGHT_CMDMSK; 5702 break;
5617 current_value &= TP_EC_BACKLIGHT_LVLMSK; 5703 case TPACPI_BRGHT_MODE_UCMS_STEP:
5618 5704 res = tpacpi_brightness_set_ucmsstep(value);
5619 cmos_cmd = value > current_value ? 5705 break;
5620 TP_CMOS_BRIGHTNESS_UP : 5706 default:
5621 TP_CMOS_BRIGHTNESS_DOWN; 5707 res = -ENXIO;
5622 inc = (value > current_value)? 1 : -1;
5623
5624 res = 0;
5625 for (i = current_value; i != value; i += inc) {
5626 if ((brightness_mode & 2) &&
5627 issue_thinkpad_cmos_command(cmos_cmd)) {
5628 res = -EIO;
5629 goto errout;
5630 }
5631 if ((brightness_mode & 1) &&
5632 !acpi_ec_write(TP_EC_BACKLIGHT,
5633 (i + inc) | command_bits)) {
5634 res = -EIO;
5635 goto errout;;
5636 }
5637 } 5708 }
5638 5709
5639errout:
5640 mutex_unlock(&brightness_mutex); 5710 mutex_unlock(&brightness_mutex);
5641 return res; 5711 return res;
5642} 5712}
@@ -5645,21 +5715,34 @@ errout:
5645 5715
5646static int brightness_update_status(struct backlight_device *bd) 5716static int brightness_update_status(struct backlight_device *bd)
5647{ 5717{
5648 /* it is the backlight class's job (caller) to handle 5718 unsigned int level =
5649 * EINTR and other errors properly */
5650 return brightness_set(
5651 (bd->props.fb_blank == FB_BLANK_UNBLANK && 5719 (bd->props.fb_blank == FB_BLANK_UNBLANK &&
5652 bd->props.power == FB_BLANK_UNBLANK) ? 5720 bd->props.power == FB_BLANK_UNBLANK) ?
5653 bd->props.brightness : 0); 5721 bd->props.brightness : 0;
5722
5723 dbg_printk(TPACPI_DBG_BRGHT,
5724 "backlight: attempt to set level to %d\n",
5725 level);
5726
5727 /* it is the backlight class's job (caller) to handle
5728 * EINTR and other errors properly */
5729 return brightness_set(level);
5654} 5730}
5655 5731
5656static int brightness_get(struct backlight_device *bd) 5732static int brightness_get(struct backlight_device *bd)
5657{ 5733{
5658 int status, res; 5734 int status, res;
5659 5735
5660 res = brightness_get_raw(&status); 5736 res = mutex_lock_killable(&brightness_mutex);
5661 if (res < 0) 5737 if (res < 0)
5662 return 0; /* FIXME: teach backlight about error handling */ 5738 return 0;
5739
5740 res = tpacpi_brightness_get_raw(&status);
5741
5742 mutex_unlock(&brightness_mutex);
5743
5744 if (res < 0)
5745 return 0;
5663 5746
5664 return status & TP_EC_BACKLIGHT_LVLMSK; 5747 return status & TP_EC_BACKLIGHT_LVLMSK;
5665} 5748}
@@ -5709,7 +5792,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
5709 } 5792 }
5710 5793
5711 if (!brightness_enable) { 5794 if (!brightness_enable) {
5712 dbg_printk(TPACPI_DBG_INIT, 5795 dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
5713 "brightness support disabled by " 5796 "brightness support disabled by "
5714 "module parameter\n"); 5797 "module parameter\n");
5715 return 1; 5798 return 1;
@@ -5724,20 +5807,38 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
5724 if (b == 16) 5807 if (b == 16)
5725 tp_features.bright_16levels = 1; 5808 tp_features.bright_16levels = 1;
5726 5809
5727 if (!brightness_mode) { 5810 /*
5728 if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) 5811 * Check for module parameter bogosity, note that we
5729 brightness_mode = 2; 5812 * init brightness_mode to TPACPI_BRGHT_MODE_MAX in order to be
5730 else 5813 * able to detect "unspecified"
5731 brightness_mode = 3; 5814 */
5815 if (brightness_mode > TPACPI_BRGHT_MODE_MAX)
5816 return -EINVAL;
5732 5817
5733 dbg_printk(TPACPI_DBG_INIT, "selected brightness_mode=%d\n", 5818 /* TPACPI_BRGHT_MODE_AUTO not implemented yet, just use default */
5734 brightness_mode); 5819 if (brightness_mode == TPACPI_BRGHT_MODE_AUTO ||
5735 } 5820 brightness_mode == TPACPI_BRGHT_MODE_MAX) {
5821 if (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) {
5822 /*
5823 * IBM models that define HBRV probably have
5824 * EC-based backlight level control
5825 */
5826 if (acpi_evalf(ec_handle, NULL, "HBRV", "qd"))
5827 /* T40-T43, R50-R52, R50e, R51e, X31-X41 */
5828 brightness_mode = TPACPI_BRGHT_MODE_ECNVRAM;
5829 else
5830 /* all other IBM ThinkPads */
5831 brightness_mode = TPACPI_BRGHT_MODE_UCMS_STEP;
5832 } else
5833 /* All Lenovo ThinkPads */
5834 brightness_mode = TPACPI_BRGHT_MODE_UCMS_STEP;
5736 5835
5737 if (brightness_mode > 3) 5836 dbg_printk(TPACPI_DBG_BRGHT,
5738 return -EINVAL; 5837 "selected brightness_mode=%d\n",
5838 brightness_mode);
5839 }
5739 5840
5740 if (brightness_get_raw(&b) < 0) 5841 if (tpacpi_brightness_get_raw(&b) < 0)
5741 return 1; 5842 return 1;
5742 5843
5743 if (tp_features.bright_16levels) 5844 if (tp_features.bright_16levels)
@@ -5751,7 +5852,8 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
5751 printk(TPACPI_ERR "Could not register backlight device\n"); 5852 printk(TPACPI_ERR "Could not register backlight device\n");
5752 return PTR_ERR(ibm_backlight_device); 5853 return PTR_ERR(ibm_backlight_device);
5753 } 5854 }
5754 vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n"); 5855 vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
5856 "brightness is supported\n");
5755 5857
5756 ibm_backlight_device->props.max_brightness = 5858 ibm_backlight_device->props.max_brightness =
5757 (tp_features.bright_16levels)? 15 : 7; 5859 (tp_features.bright_16levels)? 15 : 7;
@@ -5761,13 +5863,25 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
5761 return 0; 5863 return 0;
5762} 5864}
5763 5865
5866static void brightness_suspend(pm_message_t state)
5867{
5868 tpacpi_brightness_checkpoint_nvram();
5869}
5870
5871static void brightness_shutdown(void)
5872{
5873 tpacpi_brightness_checkpoint_nvram();
5874}
5875
5764static void brightness_exit(void) 5876static void brightness_exit(void)
5765{ 5877{
5766 if (ibm_backlight_device) { 5878 if (ibm_backlight_device) {
5767 vdbg_printk(TPACPI_DBG_EXIT, 5879 vdbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_BRGHT,
5768 "calling backlight_device_unregister()\n"); 5880 "calling backlight_device_unregister()\n");
5769 backlight_device_unregister(ibm_backlight_device); 5881 backlight_device_unregister(ibm_backlight_device);
5770 } 5882 }
5883
5884 tpacpi_brightness_checkpoint_nvram();
5771} 5885}
5772 5886
5773static int brightness_read(char *p) 5887static int brightness_read(char *p)
@@ -5814,6 +5928,9 @@ static int brightness_write(char *buf)
5814 return -EINVAL; 5928 return -EINVAL;
5815 } 5929 }
5816 5930
5931 tpacpi_disclose_usertask("procfs brightness",
5932 "set level to %d\n", level);
5933
5817 /* 5934 /*
5818 * Now we know what the final level should be, so we try to set it. 5935 * Now we know what the final level should be, so we try to set it.
5819 * Doing it this way makes the syscall restartable in case of EINTR 5936 * Doing it this way makes the syscall restartable in case of EINTR
@@ -5827,6 +5944,8 @@ static struct ibm_struct brightness_driver_data = {
5827 .read = brightness_read, 5944 .read = brightness_read,
5828 .write = brightness_write, 5945 .write = brightness_write,
5829 .exit = brightness_exit, 5946 .exit = brightness_exit,
5947 .suspend = brightness_suspend,
5948 .shutdown = brightness_shutdown,
5830}; 5949};
5831 5950
5832/************************************************************************* 5951/*************************************************************************
@@ -7465,10 +7584,10 @@ module_param_named(fan_control, fan_control_allowed, bool, 0);
7465MODULE_PARM_DESC(fan_control, 7584MODULE_PARM_DESC(fan_control,
7466 "Enables setting fan parameters features when true"); 7585 "Enables setting fan parameters features when true");
7467 7586
7468module_param_named(brightness_mode, brightness_mode, int, 0); 7587module_param_named(brightness_mode, brightness_mode, uint, 0);
7469MODULE_PARM_DESC(brightness_mode, 7588MODULE_PARM_DESC(brightness_mode,
7470 "Selects brightness control strategy: " 7589 "Selects brightness control strategy: "
7471 "0=auto, 1=EC, 2=CMOS, 3=both"); 7590 "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM");
7472 7591
7473module_param(brightness_enable, uint, 0); 7592module_param(brightness_enable, uint, 0);
7474MODULE_PARM_DESC(brightness_enable, 7593MODULE_PARM_DESC(brightness_enable,