mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-11-20 08:38:24 +08:00
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: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Signed-off-by: Len Brown <len.brown@intel.com>
This commit is contained in:
parent
d5a2f2f1d6
commit
24d3b77467
@ -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
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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))
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
level &= 0x7;
|
if (brightness_mode & 1) {
|
||||||
|
if (!acpi_ec_read(brightness_offset, &lec))
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
|||||||
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);
|
|||||||
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 @@
|
|||||||
#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 @@
|
|||||||
#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);
|
|||||||
|
|
||||||
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user