aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/laptops/thinkpad-acpi.txt12
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c317
2 files changed, 227 insertions, 102 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
index 25ed43d0a21f..3d7650768bb5 100644
--- a/Documentation/laptops/thinkpad-acpi.txt
+++ b/Documentation/laptops/thinkpad-acpi.txt
@@ -1157,10 +1157,15 @@ display backlight brightness control methods have 16 levels, ranging
1157from 0 to 15. 1157from 0 to 15.
1158 1158
1159There are two interfaces to the firmware for direct brightness control, 1159There are two interfaces to the firmware for direct brightness control,
1160EC and CMOS. To select which one should be used, use the 1160EC and UCMS (or CMOS). To select which one should be used, use the
1161brightness_mode module parameter: brightness_mode=1 selects EC mode, 1161brightness_mode module parameter: brightness_mode=1 selects EC mode,
1162brightness_mode=2 selects CMOS mode, brightness_mode=3 selects both EC 1162brightness_mode=2 selects UCMS mode, brightness_mode=3 selects EC
1163and CMOS. The driver tries to auto-detect which interface to use. 1163mode with NVRAM backing (so that brightness changes are remembered
1164across shutdown/reboot).
1165
1166The driver tries to select which interface to use from a table of
1167defaults for each ThinkPad model. If it makes a wrong choice, please
1168report this as a bug, so that we can fix it.
1164 1169
1165When display backlight brightness controls are available through the 1170When display backlight brightness controls are available through the
1166standard ACPI interface, it is best to use it instead of this direct 1171standard ACPI interface, it is best to use it instead of this direct
@@ -1498,6 +1503,7 @@ to enable more than one output class, just add their values.
1498 (bluetooth, WWAN, UWB...) 1503 (bluetooth, WWAN, UWB...)
1499 0x0008 HKEY event interface, hotkeys 1504 0x0008 HKEY event interface, hotkeys
1500 0x0010 Fan control 1505 0x0010 Fan control
1506 0x0020 Backlight brightness
1501 1507
1502There is also a kernel build option to enable more debugging 1508There is also a kernel build option to enable more debugging
1503information, which may be necessary to debug driver problems. 1509information, which may be necessary to debug driver problems.
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,