aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/misc/Kconfig2
-rw-r--r--drivers/misc/thinkpad_acpi.c191
2 files changed, 138 insertions, 55 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 297a48f85446..3a5d7694b878 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -245,6 +245,8 @@ config THINKPAD_ACPI
245 select HWMON 245 select HWMON
246 select NVRAM 246 select NVRAM
247 depends on INPUT 247 depends on INPUT
248 select NEW_LEDS
249 select LEDS_CLASS
248 ---help--- 250 ---help---
249 This is a driver for the IBM and Lenovo ThinkPad laptops. It adds 251 This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
250 support for Fn-Fx key combinations, Bluetooth control, video 252 support for Fn-Fx key combinations, Bluetooth control, video
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index 2b73dfafac9d..5a3fb09f10d3 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -67,6 +67,7 @@
67#include <linux/hwmon.h> 67#include <linux/hwmon.h>
68#include <linux/hwmon-sysfs.h> 68#include <linux/hwmon-sysfs.h>
69#include <linux/input.h> 69#include <linux/input.h>
70#include <linux/leds.h>
70#include <asm/uaccess.h> 71#include <asm/uaccess.h>
71 72
72#include <linux/dmi.h> 73#include <linux/dmi.h>
@@ -85,6 +86,8 @@
85#define TP_CMOS_VOLUME_MUTE 2 86#define TP_CMOS_VOLUME_MUTE 2
86#define TP_CMOS_BRIGHTNESS_UP 4 87#define TP_CMOS_BRIGHTNESS_UP 4
87#define TP_CMOS_BRIGHTNESS_DOWN 5 88#define TP_CMOS_BRIGHTNESS_DOWN 5
89#define TP_CMOS_THINKLIGHT_ON 12
90#define TP_CMOS_THINKLIGHT_OFF 13
88 91
89/* NVRAM Addresses */ 92/* NVRAM Addresses */
90enum tp_nvram_addr { 93enum tp_nvram_addr {
@@ -269,6 +272,13 @@ static enum {
269static int experimental; 272static int experimental;
270static u32 dbg_level; 273static u32 dbg_level;
271 274
275/* Special LED class that can defer work */
276struct tpacpi_led_classdev {
277 struct led_classdev led_classdev;
278 struct work_struct work;
279 enum led_brightness new_brightness;
280};
281
272/**************************************************************************** 282/****************************************************************************
273 **************************************************************************** 283 ****************************************************************************
274 * 284 *
@@ -3237,6 +3247,39 @@ static struct ibm_struct video_driver_data = {
3237TPACPI_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ 3247TPACPI_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */
3238TPACPI_HANDLE(ledb, ec, "LEDB"); /* G4x */ 3248TPACPI_HANDLE(ledb, ec, "LEDB"); /* G4x */
3239 3249
3250static int light_get_status(void)
3251{
3252 int status = 0;
3253
3254 if (tp_features.light_status) {
3255 if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
3256 return -EIO;
3257 return (!!status);
3258 }
3259
3260 return -ENXIO;
3261}
3262
3263static int light_set_status(int status)
3264{
3265 int rc;
3266
3267 if (tp_features.light) {
3268 if (cmos_handle) {
3269 rc = acpi_evalf(cmos_handle, NULL, NULL, "vd",
3270 (status)?
3271 TP_CMOS_THINKLIGHT_ON :
3272 TP_CMOS_THINKLIGHT_OFF);
3273 } else {
3274 rc = acpi_evalf(lght_handle, NULL, NULL, "vd",
3275 (status)? 1 : 0);
3276 }
3277 return (rc)? 0 : -EIO;
3278 }
3279
3280 return -ENXIO;
3281}
3282
3240static int __init light_init(struct ibm_init_struct *iibm) 3283static int __init light_init(struct ibm_init_struct *iibm)
3241{ 3284{
3242 vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); 3285 vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
@@ -3263,7 +3306,7 @@ static int __init light_init(struct ibm_init_struct *iibm)
3263static int light_read(char *p) 3306static int light_read(char *p)
3264{ 3307{
3265 int len = 0; 3308 int len = 0;
3266 int status = 0; 3309 int status;
3267 3310
3268 if (!tp_features.light) { 3311 if (!tp_features.light) {
3269 len += sprintf(p + len, "status:\t\tnot supported\n"); 3312 len += sprintf(p + len, "status:\t\tnot supported\n");
@@ -3271,8 +3314,9 @@ static int light_read(char *p)
3271 len += sprintf(p + len, "status:\t\tunknown\n"); 3314 len += sprintf(p + len, "status:\t\tunknown\n");
3272 len += sprintf(p + len, "commands:\ton, off\n"); 3315 len += sprintf(p + len, "commands:\ton, off\n");
3273 } else { 3316 } else {
3274 if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) 3317 status = light_get_status();
3275 return -EIO; 3318 if (status < 0)
3319 return status;
3276 len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); 3320 len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
3277 len += sprintf(p + len, "commands:\ton, off\n"); 3321 len += sprintf(p + len, "commands:\ton, off\n");
3278 } 3322 }
@@ -3282,31 +3326,22 @@ static int light_read(char *p)
3282 3326
3283static int light_write(char *buf) 3327static int light_write(char *buf)
3284{ 3328{
3285 int cmos_cmd, lght_cmd;
3286 char *cmd; 3329 char *cmd;
3287 int success; 3330 int newstatus = 0;
3288 3331
3289 if (!tp_features.light) 3332 if (!tp_features.light)
3290 return -ENODEV; 3333 return -ENODEV;
3291 3334
3292 while ((cmd = next_cmd(&buf))) { 3335 while ((cmd = next_cmd(&buf))) {
3293 if (strlencmp(cmd, "on") == 0) { 3336 if (strlencmp(cmd, "on") == 0) {
3294 cmos_cmd = 0x0c; 3337 newstatus = 1;
3295 lght_cmd = 1;
3296 } else if (strlencmp(cmd, "off") == 0) { 3338 } else if (strlencmp(cmd, "off") == 0) {
3297 cmos_cmd = 0x0d; 3339 newstatus = 0;
3298 lght_cmd = 0;
3299 } else 3340 } else
3300 return -EINVAL; 3341 return -EINVAL;
3301
3302 success = cmos_handle ?
3303 acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) :
3304 acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd);
3305 if (!success)
3306 return -EIO;
3307 } 3342 }
3308 3343
3309 return 0; 3344 return light_set_status(newstatus);
3310} 3345}
3311 3346
3312static struct ibm_struct light_driver_data = { 3347static struct ibm_struct light_driver_data = {
@@ -3710,6 +3745,12 @@ enum { /* For TPACPI_LED_OLD */
3710 TPACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */ 3745 TPACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */
3711}; 3746};
3712 3747
3748enum led_status_t {
3749 TPACPI_LED_OFF = 0,
3750 TPACPI_LED_ON,
3751 TPACPI_LED_BLINK,
3752};
3753
3713static enum led_access_mode led_supported; 3754static enum led_access_mode led_supported;
3714 3755
3715TPACPI_HANDLE(led, ec, "SLED", /* 570 */ 3756TPACPI_HANDLE(led, ec, "SLED", /* 570 */
@@ -3718,6 +3759,69 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */
3718 "LED", /* all others */ 3759 "LED", /* all others */
3719 ); /* R30, R31 */ 3760 ); /* R30, R31 */
3720 3761
3762static int led_get_status(unsigned int led)
3763{
3764 int status;
3765
3766 switch (led_supported) {
3767 case TPACPI_LED_570:
3768 if (!acpi_evalf(ec_handle,
3769 &status, "GLED", "dd", 1 << led))
3770 return -EIO;
3771 return (status == 0)?
3772 TPACPI_LED_OFF :
3773 ((status == 1)?
3774 TPACPI_LED_ON :
3775 TPACPI_LED_BLINK);
3776 default:
3777 return -ENXIO;
3778 }
3779
3780 /* not reached */
3781}
3782
3783static int led_set_status(unsigned int led, enum led_status_t ledstatus)
3784{
3785 /* off, on, blink. Index is led_status_t */
3786 static const int const led_sled_arg1[] = { 0, 1, 3 };
3787 static const int const led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */
3788 static const int const led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */
3789 static const int const led_led_arg1[] = { 0, 0x80, 0xc0 };
3790
3791 int rc = 0;
3792
3793 switch (led_supported) {
3794 case TPACPI_LED_570:
3795 /* 570 */
3796 led = 1 << led;
3797 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
3798 led, led_sled_arg1[ledstatus]))
3799 rc = -EIO;
3800 break;
3801 case TPACPI_LED_OLD:
3802 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
3803 led = 1 << led;
3804 rc = ec_write(TPACPI_LED_EC_HLMS, led);
3805 if (rc >= 0)
3806 rc = ec_write(TPACPI_LED_EC_HLBL,
3807 led * led_exp_hlbl[ledstatus]);
3808 if (rc >= 0)
3809 rc = ec_write(TPACPI_LED_EC_HLCL,
3810 led * led_exp_hlcl[ledstatus]);
3811 break;
3812 case TPACPI_LED_NEW:
3813 /* all others */
3814 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
3815 led, led_led_arg1[ledstatus]))
3816 rc = -EIO;
3817 break;
3818 default:
3819 rc = -ENXIO;
3820 }
3821
3822 return rc;
3823}
3824
3721static int __init led_init(struct ibm_init_struct *iibm) 3825static int __init led_init(struct ibm_init_struct *iibm)
3722{ 3826{
3723 vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); 3827 vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
@@ -3743,7 +3847,9 @@ static int __init led_init(struct ibm_init_struct *iibm)
3743 return (led_supported != TPACPI_LED_NONE)? 0 : 1; 3847 return (led_supported != TPACPI_LED_NONE)? 0 : 1;
3744} 3848}
3745 3849
3746#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking")) 3850#define str_led_status(s) \
3851 ((s) == TPACPI_LED_OFF ? "off" : \
3852 ((s) == TPACPI_LED_ON ? "on" : "blinking"))
3747 3853
3748static int led_read(char *p) 3854static int led_read(char *p)
3749{ 3855{
@@ -3759,11 +3865,11 @@ static int led_read(char *p)
3759 /* 570 */ 3865 /* 570 */
3760 int i, status; 3866 int i, status;
3761 for (i = 0; i < 8; i++) { 3867 for (i = 0; i < 8; i++) {
3762 if (!acpi_evalf(ec_handle, 3868 status = led_get_status(i);
3763 &status, "GLED", "dd", 1 << i)) 3869 if (status < 0)
3764 return -EIO; 3870 return -EIO;
3765 len += sprintf(p + len, "%d:\t\t%s\n", 3871 len += sprintf(p + len, "%d:\t\t%s\n",
3766 i, led_status(status)); 3872 i, str_led_status(status));
3767 } 3873 }
3768 } 3874 }
3769 3875
@@ -3773,16 +3879,11 @@ static int led_read(char *p)
3773 return len; 3879 return len;
3774} 3880}
3775 3881
3776/* off, on, blink */
3777static const int led_sled_arg1[] = { 0, 1, 3 };
3778static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */
3779static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */
3780static const int led_led_arg1[] = { 0, 0x80, 0xc0 };
3781
3782static int led_write(char *buf) 3882static int led_write(char *buf)
3783{ 3883{
3784 char *cmd; 3884 char *cmd;
3785 int led, ind, ret; 3885 int led, rc;
3886 enum led_status_t s;
3786 3887
3787 if (!led_supported) 3888 if (!led_supported)
3788 return -ENODEV; 3889 return -ENODEV;
@@ -3792,38 +3893,18 @@ static int led_write(char *buf)
3792 return -EINVAL; 3893 return -EINVAL;
3793 3894
3794 if (strstr(cmd, "off")) { 3895 if (strstr(cmd, "off")) {
3795 ind = 0; 3896 s = TPACPI_LED_OFF;
3796 } else if (strstr(cmd, "on")) { 3897 } else if (strstr(cmd, "on")) {
3797 ind = 1; 3898 s = TPACPI_LED_ON;
3798 } else if (strstr(cmd, "blink")) { 3899 } else if (strstr(cmd, "blink")) {
3799 ind = 2; 3900 s = TPACPI_LED_BLINK;
3800 } else
3801 return -EINVAL;
3802
3803 if (led_supported == TPACPI_LED_570) {
3804 /* 570 */
3805 led = 1 << led;
3806 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
3807 led, led_sled_arg1[ind]))
3808 return -EIO;
3809 } else if (led_supported == TPACPI_LED_OLD) {
3810 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
3811 led = 1 << led;
3812 ret = ec_write(TPACPI_LED_EC_HLMS, led);
3813 if (ret >= 0)
3814 ret = ec_write(TPACPI_LED_EC_HLBL,
3815 led * led_exp_hlbl[ind]);
3816 if (ret >= 0)
3817 ret = ec_write(TPACPI_LED_EC_HLCL,
3818 led * led_exp_hlcl[ind]);
3819 if (ret < 0)
3820 return ret;
3821 } else { 3901 } else {
3822 /* all others */ 3902 return -EINVAL;
3823 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
3824 led, led_led_arg1[ind]))
3825 return -EIO;
3826 } 3903 }
3904
3905 rc = led_set_status(led, s);
3906 if (rc < 0)
3907 return rc;
3827 } 3908 }
3828 3909
3829 return 0; 3910 return 0;