diff options
author | Corentin Chary <corentincj@iksaif.net> | 2011-07-01 05:34:31 -0400 |
---|---|---|
committer | Matthew Garrett <mjg@redhat.com> | 2011-08-05 14:45:54 -0400 |
commit | e9809c0b9670656855655d8ed6dc33718ec12ba2 (patch) | |
tree | 8e7d828d007263006bbb7f4e7b42440f69acb0c5 /drivers/platform | |
parent | 57d5c8e742dbb8cdc794d9e0fee769c9533072af (diff) |
asus-wmi: add keyboard backlight support
Based on a patch from Nate Weibley. <nweibley@gmail.com>.
Cc: Nate Weibley <nweibley@gmail.com>
Signed-off-by: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/asus-wmi.c | 130 |
1 files changed, 113 insertions, 17 deletions
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index fa2b395145a6..83a7a8101fd6 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c | |||
@@ -66,6 +66,8 @@ MODULE_LICENSE("GPL"); | |||
66 | #define NOTIFY_BRNUP_MAX 0x1f | 66 | #define NOTIFY_BRNUP_MAX 0x1f |
67 | #define NOTIFY_BRNDOWN_MIN 0x20 | 67 | #define NOTIFY_BRNDOWN_MIN 0x20 |
68 | #define NOTIFY_BRNDOWN_MAX 0x2e | 68 | #define NOTIFY_BRNDOWN_MAX 0x2e |
69 | #define NOTIFY_KBD_BRTUP 0xc4 | ||
70 | #define NOTIFY_KBD_BRTDWN 0xc5 | ||
69 | 71 | ||
70 | /* WMI Methods */ | 72 | /* WMI Methods */ |
71 | #define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */ | 73 | #define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */ |
@@ -174,8 +176,11 @@ struct asus_wmi { | |||
174 | 176 | ||
175 | struct led_classdev tpd_led; | 177 | struct led_classdev tpd_led; |
176 | int tpd_led_wk; | 178 | int tpd_led_wk; |
179 | struct led_classdev kbd_led; | ||
180 | int kbd_led_wk; | ||
177 | struct workqueue_struct *led_workqueue; | 181 | struct workqueue_struct *led_workqueue; |
178 | struct work_struct tpd_led_work; | 182 | struct work_struct tpd_led_work; |
183 | struct work_struct kbd_led_work; | ||
179 | 184 | ||
180 | struct asus_rfkill wlan; | 185 | struct asus_rfkill wlan; |
181 | struct asus_rfkill bluetooth; | 186 | struct asus_rfkill bluetooth; |
@@ -360,30 +365,79 @@ static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) | |||
360 | return read_tpd_led_state(asus); | 365 | return read_tpd_led_state(asus); |
361 | } | 366 | } |
362 | 367 | ||
363 | static int asus_wmi_led_init(struct asus_wmi *asus) | 368 | static void kbd_led_update(struct work_struct *work) |
364 | { | 369 | { |
365 | int rv; | 370 | int ctrl_param = 0; |
371 | struct asus_wmi *asus; | ||
366 | 372 | ||
367 | if (read_tpd_led_state(asus) < 0) | 373 | asus = container_of(work, struct asus_wmi, kbd_led_work); |
368 | return 0; | ||
369 | 374 | ||
370 | asus->led_workqueue = create_singlethread_workqueue("led_workqueue"); | 375 | /* |
371 | if (!asus->led_workqueue) | 376 | * bits 0-2: level |
372 | return -ENOMEM; | 377 | * bit 7: light on/off |
373 | INIT_WORK(&asus->tpd_led_work, tpd_led_update); | 378 | */ |
379 | if (asus->kbd_led_wk > 0) | ||
380 | ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F); | ||
374 | 381 | ||
375 | asus->tpd_led.name = "asus::touchpad"; | 382 | asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL); |
376 | asus->tpd_led.brightness_set = tpd_led_set; | 383 | } |
377 | asus->tpd_led.brightness_get = tpd_led_get; | ||
378 | asus->tpd_led.max_brightness = 1; | ||
379 | 384 | ||
380 | rv = led_classdev_register(&asus->platform_device->dev, &asus->tpd_led); | 385 | static int kbd_led_read(struct asus_wmi *asus, int *level, int *env) |
381 | if (rv) { | 386 | { |
382 | destroy_workqueue(asus->led_workqueue); | 387 | int retval; |
383 | return rv; | 388 | |
389 | /* | ||
390 | * bits 0-2: level | ||
391 | * bit 7: light on/off | ||
392 | * bit 8-10: environment (0: dark, 1: normal, 2: light) | ||
393 | * bit 17: status unknown | ||
394 | */ | ||
395 | retval = asus_wmi_get_devstate_bits(asus, ASUS_WMI_DEVID_KBD_BACKLIGHT, | ||
396 | 0xFFFF); | ||
397 | |||
398 | if (retval == 0x8000) | ||
399 | retval = -ENODEV; | ||
400 | |||
401 | if (retval >= 0) { | ||
402 | if (level) | ||
403 | *level = retval & 0x80 ? retval & 0x7F : 0; | ||
404 | if (env) | ||
405 | *env = (retval >> 8) & 0x7F; | ||
406 | retval = 0; | ||
384 | } | 407 | } |
385 | 408 | ||
386 | return 0; | 409 | return retval; |
410 | } | ||
411 | |||
412 | static void kbd_led_set(struct led_classdev *led_cdev, | ||
413 | enum led_brightness value) | ||
414 | { | ||
415 | struct asus_wmi *asus; | ||
416 | |||
417 | asus = container_of(led_cdev, struct asus_wmi, kbd_led); | ||
418 | |||
419 | if (value > asus->kbd_led.max_brightness) | ||
420 | value = asus->kbd_led.max_brightness; | ||
421 | else if (value < 0) | ||
422 | value = 0; | ||
423 | |||
424 | asus->kbd_led_wk = value; | ||
425 | queue_work(asus->led_workqueue, &asus->kbd_led_work); | ||
426 | } | ||
427 | |||
428 | static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) | ||
429 | { | ||
430 | struct asus_wmi *asus; | ||
431 | int retval, value; | ||
432 | |||
433 | asus = container_of(led_cdev, struct asus_wmi, kbd_led); | ||
434 | |||
435 | retval = kbd_led_read(asus, &value, NULL); | ||
436 | |||
437 | if (retval < 0) | ||
438 | return retval; | ||
439 | |||
440 | return value; | ||
387 | } | 441 | } |
388 | 442 | ||
389 | static void asus_wmi_led_exit(struct asus_wmi *asus) | 443 | static void asus_wmi_led_exit(struct asus_wmi *asus) |
@@ -394,6 +448,48 @@ static void asus_wmi_led_exit(struct asus_wmi *asus) | |||
394 | destroy_workqueue(asus->led_workqueue); | 448 | destroy_workqueue(asus->led_workqueue); |
395 | } | 449 | } |
396 | 450 | ||
451 | static int asus_wmi_led_init(struct asus_wmi *asus) | ||
452 | { | ||
453 | int rv = 0; | ||
454 | |||
455 | asus->led_workqueue = create_singlethread_workqueue("led_workqueue"); | ||
456 | if (!asus->led_workqueue) | ||
457 | return -ENOMEM; | ||
458 | |||
459 | if (read_tpd_led_state(asus) >= 0) { | ||
460 | INIT_WORK(&asus->tpd_led_work, tpd_led_update); | ||
461 | |||
462 | asus->tpd_led.name = "asus::touchpad"; | ||
463 | asus->tpd_led.brightness_set = tpd_led_set; | ||
464 | asus->tpd_led.brightness_get = tpd_led_get; | ||
465 | asus->tpd_led.max_brightness = 1; | ||
466 | |||
467 | rv = led_classdev_register(&asus->platform_device->dev, | ||
468 | &asus->tpd_led); | ||
469 | if (rv) | ||
470 | goto error; | ||
471 | } | ||
472 | |||
473 | if (kbd_led_read(asus, NULL, NULL) >= 0) { | ||
474 | INIT_WORK(&asus->kbd_led_work, kbd_led_update); | ||
475 | |||
476 | asus->kbd_led.name = "asus::kbd_backlight"; | ||
477 | asus->kbd_led.brightness_set = kbd_led_set; | ||
478 | asus->kbd_led.brightness_get = kbd_led_get; | ||
479 | asus->kbd_led.max_brightness = 3; | ||
480 | |||
481 | rv = led_classdev_register(&asus->platform_device->dev, | ||
482 | &asus->kbd_led); | ||
483 | } | ||
484 | |||
485 | error: | ||
486 | if (rv) | ||
487 | asus_wmi_led_exit(asus); | ||
488 | |||
489 | return rv; | ||
490 | } | ||
491 | |||
492 | |||
397 | /* | 493 | /* |
398 | * PCI hotplug (for wlan rfkill) | 494 | * PCI hotplug (for wlan rfkill) |
399 | */ | 495 | */ |