diff options
-rw-r--r-- | drivers/platform/x86/Kconfig | 2 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-wmi.c | 113 |
2 files changed, 115 insertions, 0 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 65ba62086ca8..adcdbbd7d3fd 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -426,6 +426,8 @@ config EEEPC_WMI | |||
426 | depends on EXPERIMENTAL | 426 | depends on EXPERIMENTAL |
427 | depends on BACKLIGHT_CLASS_DEVICE | 427 | depends on BACKLIGHT_CLASS_DEVICE |
428 | select INPUT_SPARSEKMAP | 428 | select INPUT_SPARSEKMAP |
429 | select LEDS_CLASS | ||
430 | select NEW_LEDS | ||
429 | ---help--- | 431 | ---help--- |
430 | Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. | 432 | Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. |
431 | 433 | ||
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index dfbb295326be..f12a25da4055 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/input/sparse-keymap.h> | 34 | #include <linux/input/sparse-keymap.h> |
35 | #include <linux/fb.h> | 35 | #include <linux/fb.h> |
36 | #include <linux/backlight.h> | 36 | #include <linux/backlight.h> |
37 | #include <linux/leds.h> | ||
37 | #include <linux/platform_device.h> | 38 | #include <linux/platform_device.h> |
38 | #include <acpi/acpi_bus.h> | 39 | #include <acpi/acpi_bus.h> |
39 | #include <acpi/acpi_drivers.h> | 40 | #include <acpi/acpi_drivers.h> |
@@ -60,6 +61,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); | |||
60 | #define EEEPC_WMI_METHODID_CFVS 0x53564643 | 61 | #define EEEPC_WMI_METHODID_CFVS 0x53564643 |
61 | 62 | ||
62 | #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 | 63 | #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 |
64 | #define EEEPC_WMI_DEVID_TPDLED 0x00100011 | ||
63 | 65 | ||
64 | static const struct key_entry eeepc_wmi_keymap[] = { | 66 | static const struct key_entry eeepc_wmi_keymap[] = { |
65 | /* Sleep already handled via generic ACPI code */ | 67 | /* Sleep already handled via generic ACPI code */ |
@@ -87,6 +89,11 @@ struct eeepc_wmi { | |||
87 | struct input_dev *inputdev; | 89 | struct input_dev *inputdev; |
88 | struct backlight_device *backlight_device; | 90 | struct backlight_device *backlight_device; |
89 | struct platform_device *platform_device; | 91 | struct platform_device *platform_device; |
92 | |||
93 | struct led_classdev tpd_led; | ||
94 | int tpd_led_wk; | ||
95 | struct workqueue_struct *led_workqueue; | ||
96 | struct work_struct tpd_led_work; | ||
90 | }; | 97 | }; |
91 | 98 | ||
92 | /* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */ | 99 | /* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */ |
@@ -176,6 +183,105 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) | |||
176 | return status; | 183 | return status; |
177 | } | 184 | } |
178 | 185 | ||
186 | /* | ||
187 | * LEDs | ||
188 | */ | ||
189 | /* | ||
190 | * These functions actually update the LED's, and are called from a | ||
191 | * workqueue. By doing this as separate work rather than when the LED | ||
192 | * subsystem asks, we avoid messing with the Eeepc ACPI stuff during a | ||
193 | * potentially bad time, such as a timer interrupt. | ||
194 | */ | ||
195 | static void tpd_led_update(struct work_struct *work) | ||
196 | { | ||
197 | int ctrl_param; | ||
198 | struct eeepc_wmi *eeepc; | ||
199 | |||
200 | eeepc = container_of(work, struct eeepc_wmi, tpd_led_work); | ||
201 | |||
202 | ctrl_param = eeepc->tpd_led_wk; | ||
203 | eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED, ctrl_param); | ||
204 | } | ||
205 | |||
206 | static void tpd_led_set(struct led_classdev *led_cdev, | ||
207 | enum led_brightness value) | ||
208 | { | ||
209 | struct eeepc_wmi *eeepc; | ||
210 | |||
211 | eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); | ||
212 | |||
213 | eeepc->tpd_led_wk = !!value; | ||
214 | queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); | ||
215 | } | ||
216 | |||
217 | static int read_tpd_state(struct eeepc_wmi *eeepc) | ||
218 | { | ||
219 | static u32 ctrl_param; | ||
220 | acpi_status status; | ||
221 | |||
222 | status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &ctrl_param); | ||
223 | |||
224 | if (ACPI_FAILURE(status)) | ||
225 | return -1; | ||
226 | else if (!ctrl_param || ctrl_param == 0x00060000) | ||
227 | /* | ||
228 | * if touchpad led is present, DSTS will set some bits, | ||
229 | * usually 0x00020000. | ||
230 | * 0x00060000 means that the device is not supported | ||
231 | */ | ||
232 | return -ENODEV; | ||
233 | else | ||
234 | /* Status is stored in the first bit */ | ||
235 | return ctrl_param & 0x1; | ||
236 | } | ||
237 | |||
238 | static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) | ||
239 | { | ||
240 | struct eeepc_wmi *eeepc; | ||
241 | |||
242 | eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); | ||
243 | |||
244 | return read_tpd_state(eeepc); | ||
245 | } | ||
246 | |||
247 | static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc) | ||
248 | { | ||
249 | int rv; | ||
250 | |||
251 | if (read_tpd_state(eeepc) < 0) | ||
252 | return 0; | ||
253 | |||
254 | eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); | ||
255 | if (!eeepc->led_workqueue) | ||
256 | return -ENOMEM; | ||
257 | INIT_WORK(&eeepc->tpd_led_work, tpd_led_update); | ||
258 | |||
259 | eeepc->tpd_led.name = "eeepc::touchpad"; | ||
260 | eeepc->tpd_led.brightness_set = tpd_led_set; | ||
261 | eeepc->tpd_led.brightness_get = tpd_led_get; | ||
262 | eeepc->tpd_led.max_brightness = 1; | ||
263 | |||
264 | rv = led_classdev_register(&eeepc->platform_device->dev, | ||
265 | &eeepc->tpd_led); | ||
266 | if (rv) { | ||
267 | destroy_workqueue(eeepc->led_workqueue); | ||
268 | return rv; | ||
269 | } | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc) | ||
275 | { | ||
276 | if (eeepc->tpd_led.dev) | ||
277 | led_classdev_unregister(&eeepc->tpd_led); | ||
278 | if (eeepc->led_workqueue) | ||
279 | destroy_workqueue(eeepc->led_workqueue); | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * Backlight | ||
284 | */ | ||
179 | static int read_brightness(struct backlight_device *bd) | 285 | static int read_brightness(struct backlight_device *bd) |
180 | { | 286 | { |
181 | static u32 ctrl_param; | 287 | static u32 ctrl_param; |
@@ -402,6 +508,10 @@ static struct platform_device * __init eeepc_wmi_add(void) | |||
402 | if (err) | 508 | if (err) |
403 | goto fail_input; | 509 | goto fail_input; |
404 | 510 | ||
511 | err = eeepc_wmi_led_init(eeepc); | ||
512 | if (err) | ||
513 | goto fail_leds; | ||
514 | |||
405 | if (!acpi_video_backlight_support()) { | 515 | if (!acpi_video_backlight_support()) { |
406 | err = eeepc_wmi_backlight_init(eeepc); | 516 | err = eeepc_wmi_backlight_init(eeepc); |
407 | if (err) | 517 | if (err) |
@@ -423,6 +533,8 @@ static struct platform_device * __init eeepc_wmi_add(void) | |||
423 | fail_wmi_handler: | 533 | fail_wmi_handler: |
424 | eeepc_wmi_backlight_exit(eeepc); | 534 | eeepc_wmi_backlight_exit(eeepc); |
425 | fail_backlight: | 535 | fail_backlight: |
536 | eeepc_wmi_led_exit(eeepc); | ||
537 | fail_leds: | ||
426 | eeepc_wmi_input_exit(eeepc); | 538 | eeepc_wmi_input_exit(eeepc); |
427 | fail_input: | 539 | fail_input: |
428 | eeepc_wmi_platform_exit(eeepc); | 540 | eeepc_wmi_platform_exit(eeepc); |
@@ -439,6 +551,7 @@ static int eeepc_wmi_remove(struct platform_device *device) | |||
439 | wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); | 551 | wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); |
440 | eeepc_wmi_backlight_exit(eeepc); | 552 | eeepc_wmi_backlight_exit(eeepc); |
441 | eeepc_wmi_input_exit(eeepc); | 553 | eeepc_wmi_input_exit(eeepc); |
554 | eeepc_wmi_led_exit(eeepc); | ||
442 | eeepc_wmi_platform_exit(eeepc); | 555 | eeepc_wmi_platform_exit(eeepc); |
443 | 556 | ||
444 | kfree(eeepc); | 557 | kfree(eeepc); |