diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/misc/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/misc/thinkpad_acpi.c | 191 |
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 */ |
| 90 | enum tp_nvram_addr { | 93 | enum tp_nvram_addr { |
| @@ -269,6 +272,13 @@ static enum { | |||
| 269 | static int experimental; | 272 | static int experimental; |
| 270 | static u32 dbg_level; | 273 | static u32 dbg_level; |
| 271 | 274 | ||
| 275 | /* Special LED class that can defer work */ | ||
| 276 | struct 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 = { | |||
| 3237 | TPACPI_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ | 3247 | TPACPI_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ |
| 3238 | TPACPI_HANDLE(ledb, ec, "LEDB"); /* G4x */ | 3248 | TPACPI_HANDLE(ledb, ec, "LEDB"); /* G4x */ |
| 3239 | 3249 | ||
| 3250 | static 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 | |||
| 3263 | static 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 | |||
| 3240 | static int __init light_init(struct ibm_init_struct *iibm) | 3283 | static 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) | |||
| 3263 | static int light_read(char *p) | 3306 | static 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 | ||
| 3283 | static int light_write(char *buf) | 3327 | static 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 | ||
| 3312 | static struct ibm_struct light_driver_data = { | 3347 | static 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 | ||
| 3748 | enum led_status_t { | ||
| 3749 | TPACPI_LED_OFF = 0, | ||
| 3750 | TPACPI_LED_ON, | ||
| 3751 | TPACPI_LED_BLINK, | ||
| 3752 | }; | ||
| 3753 | |||
| 3713 | static enum led_access_mode led_supported; | 3754 | static enum led_access_mode led_supported; |
| 3714 | 3755 | ||
| 3715 | TPACPI_HANDLE(led, ec, "SLED", /* 570 */ | 3756 | TPACPI_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 | ||
| 3762 | static 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 | |||
| 3783 | static 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 | |||
| 3721 | static int __init led_init(struct ibm_init_struct *iibm) | 3825 | static 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 | ||
| 3748 | static int led_read(char *p) | 3854 | static 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 */ | ||
| 3777 | static const int led_sled_arg1[] = { 0, 1, 3 }; | ||
| 3778 | static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */ | ||
| 3779 | static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */ | ||
| 3780 | static const int led_led_arg1[] = { 0, 0x80, 0xc0 }; | ||
| 3781 | |||
| 3782 | static int led_write(char *buf) | 3882 | static 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; |
