diff options
author | Henrique de Moraes Holschuh <hmh@hmh.eng.br> | 2007-07-18 22:45:43 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2007-07-21 23:49:03 -0400 |
commit | 24d3b77467b6aaf59e38dce4aa86d05541858195 (patch) | |
tree | 92975c9c2d4b37922d25782bd02d7076d77e817e | |
parent | d5a2f2f1d68e2da538ac28540cddd9ccc733b001 (diff) |
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>
-rw-r--r-- | Documentation/thinkpad-acpi.txt | 6 | ||||
-rw-r--r-- | drivers/misc/Kconfig | 1 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 62 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.h | 7 |
4 files changed, 67 insertions, 9 deletions
diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index c670363cf5a4..c145bcce2339 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt | |||
@@ -860,6 +860,12 @@ cannot be controlled. | |||
860 | The backlight control has eight levels, ranging from 0 to 7. Some of the | 860 | The backlight control has eight levels, ranging from 0 to 7. Some of the |
861 | levels may not be distinct. | 861 | levels may not be distinct. |
862 | 862 | ||
863 | There are two interfaces to the firmware for brightness control, EC and CMOS. | ||
864 | To select which one should be used, use the brightness_mode module parameter: | ||
865 | brightness_mode=1 selects EC mode, brightness_mode=2 selects CMOS mode, | ||
866 | brightness_mode=3 selects both EC and CMOS. The driver tries to autodetect | ||
867 | which interface to use. | ||
868 | |||
863 | Procfs notes: | 869 | Procfs notes: |
864 | 870 | ||
865 | The available commands are: | 871 | The available commands are: |
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 5197f9b9b65d..aaaa61ea4217 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
@@ -150,6 +150,7 @@ config THINKPAD_ACPI | |||
150 | depends on X86 && ACPI | 150 | depends on X86 && ACPI |
151 | select BACKLIGHT_CLASS_DEVICE | 151 | select BACKLIGHT_CLASS_DEVICE |
152 | select HWMON | 152 | select HWMON |
153 | select NVRAM | ||
153 | ---help--- | 154 | ---help--- |
154 | This is a driver for the IBM and Lenovo ThinkPad laptops. It adds | 155 | This is a driver for the IBM and Lenovo ThinkPad laptops. It adds |
155 | support for Fn-Fx key combinations, Bluetooth control, video | 156 | support for Fn-Fx key combinations, Bluetooth control, video |
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 99500af651c1..5318eb272c61 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
@@ -2953,9 +2953,22 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
2953 | 2953 | ||
2954 | vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n"); | 2954 | vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n"); |
2955 | 2955 | ||
2956 | if (!brightness_mode) { | ||
2957 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) | ||
2958 | brightness_mode = 2; | ||
2959 | else | ||
2960 | brightness_mode = 3; | ||
2961 | |||
2962 | dbg_printk(TPACPI_DBG_INIT, "selected brightness_mode=%d\n", | ||
2963 | brightness_mode); | ||
2964 | } | ||
2965 | |||
2966 | if (brightness_mode > 3) | ||
2967 | return -EINVAL; | ||
2968 | |||
2956 | b = brightness_get(NULL); | 2969 | b = brightness_get(NULL); |
2957 | if (b < 0) | 2970 | if (b < 0) |
2958 | return b; | 2971 | return 1; |
2959 | 2972 | ||
2960 | ibm_backlight_device = backlight_device_register( | 2973 | ibm_backlight_device = backlight_device_register( |
2961 | TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, | 2974 | TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, |
@@ -2991,13 +3004,35 @@ static int brightness_update_status(struct backlight_device *bd) | |||
2991 | bd->props.brightness : 0); | 3004 | bd->props.brightness : 0); |
2992 | } | 3005 | } |
2993 | 3006 | ||
3007 | /* | ||
3008 | * ThinkPads can read brightness from two places: EC 0x31, or | ||
3009 | * CMOS NVRAM byte 0x5E, bits 0-3. | ||
3010 | */ | ||
2994 | static int brightness_get(struct backlight_device *bd) | 3011 | static int brightness_get(struct backlight_device *bd) |
2995 | { | 3012 | { |
2996 | u8 level; | 3013 | u8 lec = 0, lcmos = 0, level = 0; |
2997 | if (!acpi_ec_read(brightness_offset, &level)) | ||
2998 | return -EIO; | ||
2999 | 3014 | ||
3000 | level &= 0x7; | 3015 | if (brightness_mode & 1) { |
3016 | if (!acpi_ec_read(brightness_offset, &lec)) | ||
3017 | return -EIO; | ||
3018 | lec &= 7; | ||
3019 | level = lec; | ||
3020 | }; | ||
3021 | if (brightness_mode & 2) { | ||
3022 | lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) | ||
3023 | & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) | ||
3024 | >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; | ||
3025 | level = lcmos; | ||
3026 | } | ||
3027 | |||
3028 | if (brightness_mode == 3 && lec != lcmos) { | ||
3029 | printk(IBM_ERR | ||
3030 | "CMOS NVRAM (%u) and EC (%u) do not agree " | ||
3031 | "on display brightness level\n", | ||
3032 | (unsigned int) lcmos, | ||
3033 | (unsigned int) lec); | ||
3034 | return -EIO; | ||
3035 | } | ||
3001 | 3036 | ||
3002 | return level; | 3037 | return level; |
3003 | } | 3038 | } |
@@ -3007,14 +3042,20 @@ static int brightness_set(int value) | |||
3007 | int cmos_cmd, inc, i; | 3042 | int cmos_cmd, inc, i; |
3008 | int current_value = brightness_get(NULL); | 3043 | int current_value = brightness_get(NULL); |
3009 | 3044 | ||
3010 | value &= 7; | 3045 | if (value > 7) |
3046 | return -EINVAL; | ||
3011 | 3047 | ||
3012 | cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN; | 3048 | cmos_cmd = value > current_value ? |
3049 | TP_CMOS_BRIGHTNESS_UP : | ||
3050 | TP_CMOS_BRIGHTNESS_DOWN; | ||
3013 | inc = value > current_value ? 1 : -1; | 3051 | inc = value > current_value ? 1 : -1; |
3052 | |||
3014 | for (i = current_value; i != value; i += inc) { | 3053 | for (i = current_value; i != value; i += inc) { |
3015 | if (issue_thinkpad_cmos_command(cmos_cmd)) | 3054 | if ((brightness_mode & 2) && |
3055 | issue_thinkpad_cmos_command(cmos_cmd)) | ||
3016 | return -EIO; | 3056 | return -EIO; |
3017 | if (!acpi_ec_write(brightness_offset, i + inc)) | 3057 | if ((brightness_mode & 1) && |
3058 | !acpi_ec_write(brightness_offset, i + inc)) | ||
3018 | return -EIO; | 3059 | return -EIO; |
3019 | } | 3060 | } |
3020 | 3061 | ||
@@ -4485,6 +4526,9 @@ module_param(force_load, bool, 0); | |||
4485 | static int fan_control_allowed; | 4526 | static int fan_control_allowed; |
4486 | module_param_named(fan_control, fan_control_allowed, bool, 0); | 4527 | module_param_named(fan_control, fan_control_allowed, bool, 0); |
4487 | 4528 | ||
4529 | static int brightness_mode; | ||
4530 | module_param_named(brightness_mode, brightness_mode, int, 0); | ||
4531 | |||
4488 | #define IBM_PARAM(feature) \ | 4532 | #define IBM_PARAM(feature) \ |
4489 | module_param_call(feature, set_ibm_param, NULL, NULL, 0) | 4533 | module_param_call(feature, set_ibm_param, NULL, NULL, 0) |
4490 | 4534 | ||
diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index 09b2282fed0b..b7a4a888cc8b 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/list.h> | 32 | #include <linux/list.h> |
33 | #include <linux/mutex.h> | 33 | #include <linux/mutex.h> |
34 | 34 | ||
35 | #include <linux/nvram.h> | ||
35 | #include <linux/proc_fs.h> | 36 | #include <linux/proc_fs.h> |
36 | #include <linux/sysfs.h> | 37 | #include <linux/sysfs.h> |
37 | #include <linux/backlight.h> | 38 | #include <linux/backlight.h> |
@@ -80,6 +81,11 @@ | |||
80 | #define TP_CMOS_BRIGHTNESS_UP 4 | 81 | #define TP_CMOS_BRIGHTNESS_UP 4 |
81 | #define TP_CMOS_BRIGHTNESS_DOWN 5 | 82 | #define TP_CMOS_BRIGHTNESS_DOWN 5 |
82 | 83 | ||
84 | /* ThinkPad CMOS NVRAM constants */ | ||
85 | #define TP_NVRAM_ADDR_BRIGHTNESS 0x5e | ||
86 | #define TP_NVRAM_MASK_LEVEL_BRIGHTNESS 0x07 | ||
87 | #define TP_NVRAM_POS_LEVEL_BRIGHTNESS 0 | ||
88 | |||
83 | #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") | 89 | #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") |
84 | #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") | 90 | #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") |
85 | #define strlencmp(a,b) (strncmp((a), (b), strlen(b))) | 91 | #define strlencmp(a,b) (strncmp((a), (b), strlen(b))) |
@@ -323,6 +329,7 @@ static int bluetooth_write(char *buf); | |||
323 | 329 | ||
324 | static struct backlight_device *ibm_backlight_device; | 330 | static struct backlight_device *ibm_backlight_device; |
325 | static int brightness_offset = 0x31; | 331 | static int brightness_offset = 0x31; |
332 | static int brightness_mode; | ||
326 | 333 | ||
327 | static int brightness_init(struct ibm_init_struct *iibm); | 334 | static int brightness_init(struct ibm_init_struct *iibm); |
328 | static void brightness_exit(void); | 335 | static void brightness_exit(void); |