diff options
author | Pali Rohár <pali.rohar@gmail.com> | 2015-12-30 17:27:41 -0500 |
---|---|---|
committer | Darren Hart <dvhart@linux.intel.com> | 2016-01-19 20:35:49 -0500 |
commit | bb28f3d51ff5e1be541d057708011cc1efe6fae9 (patch) | |
tree | bcfb5bf417333f905fd76a92a6ee6d6f71d2ce27 /drivers/platform/x86 | |
parent | 481fe5be821c3d04f986e4061de42e1209a62374 (diff) |
thinkpad_acpi: Add support for keyboard backlight
This patch adds support for controlling keyboard backlight via standard
linux led class interface (::kbd_backlight). It uses ACPI HKEY device with
MLCG and MLCS methods.
Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
Tested-by: Fabio D'Urso <fabiodurso@hotmail.it>
Acked-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: Darren Hart <dvhart@linux.intel.com>
Diffstat (limited to 'drivers/platform/x86')
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 0bed4733c4f0..a268a7abf8ab 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -303,6 +303,7 @@ static struct { | |||
303 | u32 hotkey_mask:1; | 303 | u32 hotkey_mask:1; |
304 | u32 hotkey_wlsw:1; | 304 | u32 hotkey_wlsw:1; |
305 | u32 hotkey_tablet:1; | 305 | u32 hotkey_tablet:1; |
306 | u32 kbdlight:1; | ||
306 | u32 light:1; | 307 | u32 light:1; |
307 | u32 light_status:1; | 308 | u32 light_status:1; |
308 | u32 bright_acpimode:1; | 309 | u32 bright_acpimode:1; |
@@ -4986,6 +4987,207 @@ static struct ibm_struct video_driver_data = { | |||
4986 | #endif /* CONFIG_THINKPAD_ACPI_VIDEO */ | 4987 | #endif /* CONFIG_THINKPAD_ACPI_VIDEO */ |
4987 | 4988 | ||
4988 | /************************************************************************* | 4989 | /************************************************************************* |
4990 | * Keyboard backlight subdriver | ||
4991 | */ | ||
4992 | |||
4993 | static int kbdlight_set_level(int level) | ||
4994 | { | ||
4995 | if (!hkey_handle) | ||
4996 | return -ENXIO; | ||
4997 | |||
4998 | if (!acpi_evalf(hkey_handle, NULL, "MLCS", "dd", level)) | ||
4999 | return -EIO; | ||
5000 | |||
5001 | return 0; | ||
5002 | } | ||
5003 | |||
5004 | static int kbdlight_get_level(void) | ||
5005 | { | ||
5006 | int status = 0; | ||
5007 | |||
5008 | if (!hkey_handle) | ||
5009 | return -ENXIO; | ||
5010 | |||
5011 | if (!acpi_evalf(hkey_handle, &status, "MLCG", "dd", 0)) | ||
5012 | return -EIO; | ||
5013 | |||
5014 | if (status < 0) | ||
5015 | return status; | ||
5016 | |||
5017 | return status & 0x3; | ||
5018 | } | ||
5019 | |||
5020 | static bool kbdlight_is_supported(void) | ||
5021 | { | ||
5022 | int status = 0; | ||
5023 | |||
5024 | if (!hkey_handle) | ||
5025 | return false; | ||
5026 | |||
5027 | if (!acpi_has_method(hkey_handle, "MLCG")) { | ||
5028 | vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG is unavailable\n"); | ||
5029 | return false; | ||
5030 | } | ||
5031 | |||
5032 | if (!acpi_evalf(hkey_handle, &status, "MLCG", "qdd", 0)) { | ||
5033 | vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG failed\n"); | ||
5034 | return false; | ||
5035 | } | ||
5036 | |||
5037 | if (status < 0) { | ||
5038 | vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG err: %d\n", status); | ||
5039 | return false; | ||
5040 | } | ||
5041 | |||
5042 | vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG returned 0x%x\n", status); | ||
5043 | /* | ||
5044 | * Guessed test for keyboard backlight: | ||
5045 | * | ||
5046 | * Machines with backlight keyboard return: | ||
5047 | * b010100000010000000XX - ThinkPad X1 Carbon 3rd | ||
5048 | * b110100010010000000XX - ThinkPad x230 | ||
5049 | * b010100000010000000XX - ThinkPad x240 | ||
5050 | * b010100000010000000XX - ThinkPad W541 | ||
5051 | * (XX is current backlight level) | ||
5052 | * | ||
5053 | * Machines without backlight keyboard return: | ||
5054 | * b10100001000000000000 - ThinkPad x230 | ||
5055 | * b10110001000000000000 - ThinkPad E430 | ||
5056 | * b00000000000000000000 - ThinkPad E450 | ||
5057 | * | ||
5058 | * Candidate BITs for detection test (XOR): | ||
5059 | * b01000000001000000000 | ||
5060 | * ^ | ||
5061 | */ | ||
5062 | return status & BIT(9); | ||
5063 | } | ||
5064 | |||
5065 | static void kbdlight_set_worker(struct work_struct *work) | ||
5066 | { | ||
5067 | struct tpacpi_led_classdev *data = | ||
5068 | container_of(work, struct tpacpi_led_classdev, work); | ||
5069 | |||
5070 | if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) | ||
5071 | kbdlight_set_level(data->new_state); | ||
5072 | } | ||
5073 | |||
5074 | static void kbdlight_sysfs_set(struct led_classdev *led_cdev, | ||
5075 | enum led_brightness brightness) | ||
5076 | { | ||
5077 | struct tpacpi_led_classdev *data = | ||
5078 | container_of(led_cdev, | ||
5079 | struct tpacpi_led_classdev, | ||
5080 | led_classdev); | ||
5081 | data->new_state = brightness; | ||
5082 | queue_work(tpacpi_wq, &data->work); | ||
5083 | } | ||
5084 | |||
5085 | static enum led_brightness kbdlight_sysfs_get(struct led_classdev *led_cdev) | ||
5086 | { | ||
5087 | int level; | ||
5088 | |||
5089 | level = kbdlight_get_level(); | ||
5090 | if (level < 0) | ||
5091 | return 0; | ||
5092 | |||
5093 | return level; | ||
5094 | } | ||
5095 | |||
5096 | static struct tpacpi_led_classdev tpacpi_led_kbdlight = { | ||
5097 | .led_classdev = { | ||
5098 | .name = "tpacpi::kbd_backlight", | ||
5099 | .max_brightness = 2, | ||
5100 | .brightness_set = &kbdlight_sysfs_set, | ||
5101 | .brightness_get = &kbdlight_sysfs_get, | ||
5102 | .flags = LED_CORE_SUSPENDRESUME, | ||
5103 | } | ||
5104 | }; | ||
5105 | |||
5106 | static int __init kbdlight_init(struct ibm_init_struct *iibm) | ||
5107 | { | ||
5108 | int rc; | ||
5109 | |||
5110 | vdbg_printk(TPACPI_DBG_INIT, "initializing kbdlight subdriver\n"); | ||
5111 | |||
5112 | TPACPI_ACPIHANDLE_INIT(hkey); | ||
5113 | INIT_WORK(&tpacpi_led_kbdlight.work, kbdlight_set_worker); | ||
5114 | |||
5115 | if (!kbdlight_is_supported()) { | ||
5116 | tp_features.kbdlight = 0; | ||
5117 | vdbg_printk(TPACPI_DBG_INIT, "kbdlight is unsupported\n"); | ||
5118 | return 1; | ||
5119 | } | ||
5120 | |||
5121 | tp_features.kbdlight = 1; | ||
5122 | |||
5123 | rc = led_classdev_register(&tpacpi_pdev->dev, | ||
5124 | &tpacpi_led_kbdlight.led_classdev); | ||
5125 | if (rc < 0) { | ||
5126 | tp_features.kbdlight = 0; | ||
5127 | return rc; | ||
5128 | } | ||
5129 | |||
5130 | return 0; | ||
5131 | } | ||
5132 | |||
5133 | static void kbdlight_exit(void) | ||
5134 | { | ||
5135 | if (tp_features.kbdlight) | ||
5136 | led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev); | ||
5137 | flush_workqueue(tpacpi_wq); | ||
5138 | } | ||
5139 | |||
5140 | static int kbdlight_read(struct seq_file *m) | ||
5141 | { | ||
5142 | int level; | ||
5143 | |||
5144 | if (!tp_features.kbdlight) { | ||
5145 | seq_printf(m, "status:\t\tnot supported\n"); | ||
5146 | } else { | ||
5147 | level = kbdlight_get_level(); | ||
5148 | if (level < 0) | ||
5149 | seq_printf(m, "status:\t\terror %d\n", level); | ||
5150 | else | ||
5151 | seq_printf(m, "status:\t\t%d\n", level); | ||
5152 | seq_printf(m, "commands:\t0, 1, 2\n"); | ||
5153 | } | ||
5154 | |||
5155 | return 0; | ||
5156 | } | ||
5157 | |||
5158 | static int kbdlight_write(char *buf) | ||
5159 | { | ||
5160 | char *cmd; | ||
5161 | int level = -1; | ||
5162 | |||
5163 | if (!tp_features.kbdlight) | ||
5164 | return -ENODEV; | ||
5165 | |||
5166 | while ((cmd = next_cmd(&buf))) { | ||
5167 | if (strlencmp(cmd, "0") == 0) | ||
5168 | level = 0; | ||
5169 | else if (strlencmp(cmd, "1") == 0) | ||
5170 | level = 1; | ||
5171 | else if (strlencmp(cmd, "2") == 0) | ||
5172 | level = 2; | ||
5173 | else | ||
5174 | return -EINVAL; | ||
5175 | } | ||
5176 | |||
5177 | if (level == -1) | ||
5178 | return -EINVAL; | ||
5179 | |||
5180 | return kbdlight_set_level(level); | ||
5181 | } | ||
5182 | |||
5183 | static struct ibm_struct kbdlight_driver_data = { | ||
5184 | .name = "kbdlight", | ||
5185 | .read = kbdlight_read, | ||
5186 | .write = kbdlight_write, | ||
5187 | .exit = kbdlight_exit, | ||
5188 | }; | ||
5189 | |||
5190 | /************************************************************************* | ||
4989 | * Light (thinklight) subdriver | 5191 | * Light (thinklight) subdriver |
4990 | */ | 5192 | */ |
4991 | 5193 | ||
@@ -9207,6 +9409,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { | |||
9207 | }, | 9409 | }, |
9208 | #endif | 9410 | #endif |
9209 | { | 9411 | { |
9412 | .init = kbdlight_init, | ||
9413 | .data = &kbdlight_driver_data, | ||
9414 | }, | ||
9415 | { | ||
9210 | .init = light_init, | 9416 | .init = light_init, |
9211 | .data = &light_driver_data, | 9417 | .data = &light_driver_data, |
9212 | }, | 9418 | }, |