Commit 24d3b774 authored by Henrique de Moraes Holschuh's avatar Henrique de Moraes Holschuh Committed by Len Brown

ACPI: thinkpad-acpi: allow use of CMOS NVRAM for brightness control

It appears that Lenovo decided to break the EC brightness control interface
in a weird way in their latest BIOSes.  Fortunately, the old CMOS NVRAM
interface works just fine in such BIOSes.

Add a module parameter that allows the user to select which strategy to use
for brightness control: EC, NVRAM, or both.  By default, do both (which is
the way thinkpad-acpi used to work until now) on IBM ThinkPads, and use
NVRAM only on Lenovo ThinkPads.
Signed-off-by: default avatarHenrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent d5a2f2f1
...@@ -860,6 +860,12 @@ cannot be controlled. ...@@ -860,6 +860,12 @@ cannot be controlled.
The backlight control has eight levels, ranging from 0 to 7. Some of the The backlight control has eight levels, ranging from 0 to 7. Some of the
levels may not be distinct. levels may not be distinct.
There are two interfaces to the firmware for brightness control, EC and CMOS.
To select which one should be used, use the brightness_mode module parameter:
brightness_mode=1 selects EC mode, brightness_mode=2 selects CMOS mode,
brightness_mode=3 selects both EC and CMOS. The driver tries to autodetect
which interface to use.
Procfs notes: Procfs notes:
The available commands are: The available commands are:
......
...@@ -150,6 +150,7 @@ config THINKPAD_ACPI ...@@ -150,6 +150,7 @@ config THINKPAD_ACPI
depends on X86 && ACPI depends on X86 && ACPI
select BACKLIGHT_CLASS_DEVICE select BACKLIGHT_CLASS_DEVICE
select HWMON select HWMON
select NVRAM
---help--- ---help---
This is a driver for the IBM and Lenovo ThinkPad laptops. It adds This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
support for Fn-Fx key combinations, Bluetooth control, video support for Fn-Fx key combinations, Bluetooth control, video
......
...@@ -2953,9 +2953,22 @@ static int __init brightness_init(struct ibm_init_struct *iibm) ...@@ -2953,9 +2953,22 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n"); vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n");
if (!brightness_mode) {
if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO)
brightness_mode = 2;
else
brightness_mode = 3;
dbg_printk(TPACPI_DBG_INIT, "selected brightness_mode=%d\n",
brightness_mode);
}
if (brightness_mode > 3)
return -EINVAL;
b = brightness_get(NULL); b = brightness_get(NULL);
if (b < 0) if (b < 0)
return b; return 1;
ibm_backlight_device = backlight_device_register( ibm_backlight_device = backlight_device_register(
TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
...@@ -2991,13 +3004,35 @@ static int brightness_update_status(struct backlight_device *bd) ...@@ -2991,13 +3004,35 @@ static int brightness_update_status(struct backlight_device *bd)
bd->props.brightness : 0); bd->props.brightness : 0);
} }
/*
* ThinkPads can read brightness from two places: EC 0x31, or
* CMOS NVRAM byte 0x5E, bits 0-3.
*/
static int brightness_get(struct backlight_device *bd) static int brightness_get(struct backlight_device *bd)
{ {
u8 level; u8 lec = 0, lcmos = 0, level = 0;
if (!acpi_ec_read(brightness_offset, &level))
if (brightness_mode & 1) {
if (!acpi_ec_read(brightness_offset, &lec))
return -EIO; return -EIO;
lec &= 7;
level = lec;
};
if (brightness_mode & 2) {
lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
& TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
>> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
level = lcmos;
}
level &= 0x7; if (brightness_mode == 3 && lec != lcmos) {
printk(IBM_ERR
"CMOS NVRAM (%u) and EC (%u) do not agree "
"on display brightness level\n",
(unsigned int) lcmos,
(unsigned int) lec);
return -EIO;
}
return level; return level;
} }
...@@ -3007,14 +3042,20 @@ static int brightness_set(int value) ...@@ -3007,14 +3042,20 @@ static int brightness_set(int value)
int cmos_cmd, inc, i; int cmos_cmd, inc, i;
int current_value = brightness_get(NULL); int current_value = brightness_get(NULL);
value &= 7; if (value > 7)
return -EINVAL;
cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN; cmos_cmd = value > current_value ?
TP_CMOS_BRIGHTNESS_UP :
TP_CMOS_BRIGHTNESS_DOWN;
inc = value > current_value ? 1 : -1; inc = value > current_value ? 1 : -1;
for (i = current_value; i != value; i += inc) { for (i = current_value; i != value; i += inc) {
if (issue_thinkpad_cmos_command(cmos_cmd)) if ((brightness_mode & 2) &&
issue_thinkpad_cmos_command(cmos_cmd))
return -EIO; return -EIO;
if (!acpi_ec_write(brightness_offset, i + inc)) if ((brightness_mode & 1) &&
!acpi_ec_write(brightness_offset, i + inc))
return -EIO; return -EIO;
} }
...@@ -4485,6 +4526,9 @@ module_param(force_load, bool, 0); ...@@ -4485,6 +4526,9 @@ module_param(force_load, bool, 0);
static int fan_control_allowed; static int fan_control_allowed;
module_param_named(fan_control, fan_control_allowed, bool, 0); module_param_named(fan_control, fan_control_allowed, bool, 0);
static int brightness_mode;
module_param_named(brightness_mode, brightness_mode, int, 0);
#define IBM_PARAM(feature) \ #define IBM_PARAM(feature) \
module_param_call(feature, set_ibm_param, NULL, NULL, 0) module_param_call(feature, set_ibm_param, NULL, NULL, 0)
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/nvram.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/backlight.h> #include <linux/backlight.h>
...@@ -80,6 +81,11 @@ ...@@ -80,6 +81,11 @@
#define TP_CMOS_BRIGHTNESS_UP 4 #define TP_CMOS_BRIGHTNESS_UP 4
#define TP_CMOS_BRIGHTNESS_DOWN 5 #define TP_CMOS_BRIGHTNESS_DOWN 5
/* ThinkPad CMOS NVRAM constants */
#define TP_NVRAM_ADDR_BRIGHTNESS 0x5e
#define TP_NVRAM_MASK_LEVEL_BRIGHTNESS 0x07
#define TP_NVRAM_POS_LEVEL_BRIGHTNESS 0
#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
#define strlencmp(a,b) (strncmp((a), (b), strlen(b))) #define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
...@@ -323,6 +329,7 @@ static int bluetooth_write(char *buf); ...@@ -323,6 +329,7 @@ static int bluetooth_write(char *buf);
static struct backlight_device *ibm_backlight_device; static struct backlight_device *ibm_backlight_device;
static int brightness_offset = 0x31; static int brightness_offset = 0x31;
static int brightness_mode;
static int brightness_init(struct ibm_init_struct *iibm); static int brightness_init(struct ibm_init_struct *iibm);
static void brightness_exit(void); static void brightness_exit(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment