aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorCorentin Chary <corentincj@iksaif.net>2011-07-01 05:34:31 -0400
committerMatthew Garrett <mjg@redhat.com>2011-08-05 14:45:54 -0400
commite9809c0b9670656855655d8ed6dc33718ec12ba2 (patch)
tree8e7d828d007263006bbb7f4e7b42440f69acb0c5 /drivers
parent57d5c8e742dbb8cdc794d9e0fee769c9533072af (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')
-rw-r--r--drivers/platform/x86/asus-wmi.c130
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
363static int asus_wmi_led_init(struct asus_wmi *asus) 368static 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); 385static 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
412static 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
428static 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
389static void asus_wmi_led_exit(struct asus_wmi *asus) 443static 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
451static 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
485error:
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 */