diff options
-rw-r--r-- | drivers/platform/x86/asus-laptop.c | 235 |
1 files changed, 115 insertions, 120 deletions
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index e1df92a22de9..ae5302646c4e 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c | |||
@@ -193,29 +193,12 @@ ASUS_HANDLE(display_get, | |||
193 | /* | 193 | /* |
194 | * Define a specific led structure to keep the main structure clean | 194 | * Define a specific led structure to keep the main structure clean |
195 | */ | 195 | */ |
196 | #define ASUS_DEFINE_LED(object) \ | 196 | struct asus_led { |
197 | int object##_wk; \ | 197 | int wk; |
198 | struct work_struct object##_work; \ | 198 | struct work_struct work; |
199 | struct led_classdev object; | 199 | struct led_classdev led; |
200 | 200 | struct asus_laptop *asus; | |
201 | 201 | const char *method; | |
202 | #define led_to_asus(led_cdev, led) \ | ||
203 | container_of(container_of(led_cdev, struct asus_laptop_leds, \ | ||
204 | led), \ | ||
205 | struct asus_laptop, leds) | ||
206 | #define work_to_asus(work, led) \ | ||
207 | container_of(container_of(work, struct asus_laptop_leds, \ | ||
208 | led##_work), \ | ||
209 | struct asus_laptop, leds) | ||
210 | |||
211 | struct asus_laptop_leds { | ||
212 | ASUS_DEFINE_LED(mled) | ||
213 | ASUS_DEFINE_LED(tled) | ||
214 | ASUS_DEFINE_LED(rled) | ||
215 | ASUS_DEFINE_LED(pled) | ||
216 | ASUS_DEFINE_LED(gled) | ||
217 | ASUS_DEFINE_LED(kled) | ||
218 | struct workqueue_struct *workqueue; | ||
219 | }; | 202 | }; |
220 | 203 | ||
221 | /* | 204 | /* |
@@ -233,7 +216,13 @@ struct asus_laptop { | |||
233 | struct input_dev *inputdev; | 216 | struct input_dev *inputdev; |
234 | struct key_entry *keymap; | 217 | struct key_entry *keymap; |
235 | 218 | ||
236 | struct asus_laptop_leds leds; | 219 | struct asus_led mled; |
220 | struct asus_led tled; | ||
221 | struct asus_led rled; | ||
222 | struct asus_led pled; | ||
223 | struct asus_led gled; | ||
224 | struct asus_led kled; | ||
225 | struct workqueue_struct *led_workqueue; | ||
237 | 226 | ||
238 | int wireless_status; | 227 | int wireless_status; |
239 | bool have_rsts; | 228 | bool have_rsts; |
@@ -353,7 +342,7 @@ static int acpi_check_handle(acpi_handle handle, const char *method, | |||
353 | } | 342 | } |
354 | 343 | ||
355 | /* Generic LED function */ | 344 | /* Generic LED function */ |
356 | static int asus_led_set(struct asus_laptop *asus, char *method, | 345 | static int asus_led_set(struct asus_laptop *asus, const char *method, |
357 | int value) | 346 | int value) |
358 | { | 347 | { |
359 | if (!strcmp(method, METHOD_MLED)) | 348 | if (!strcmp(method, METHOD_MLED)) |
@@ -369,50 +358,29 @@ static int asus_led_set(struct asus_laptop *asus, char *method, | |||
369 | /* | 358 | /* |
370 | * LEDs | 359 | * LEDs |
371 | */ | 360 | */ |
372 | #define ASUS_LED(object, ledname, max) \ | ||
373 | static void object##_led_set(struct led_classdev *led_cdev, \ | ||
374 | enum led_brightness value); \ | ||
375 | static enum led_brightness object##_led_get( \ | ||
376 | struct led_classdev *led_cdev); \ | ||
377 | static void object##_led_update(struct work_struct *ignored); | ||
378 | |||
379 | ASUS_LED(mled, "mail", 1); | ||
380 | ASUS_LED(tled, "touchpad", 1); | ||
381 | ASUS_LED(rled, "record", 1); | ||
382 | ASUS_LED(pled, "phone", 1); | ||
383 | ASUS_LED(gled, "gaming", 1); | ||
384 | ASUS_LED(kled, "kbd_backlight", 3); | ||
385 | |||
386 | /* /sys/class/led handlers */ | 361 | /* /sys/class/led handlers */ |
387 | #define ASUS_LED_HANDLER(object, method) \ | 362 | static void asus_led_cdev_set(struct led_classdev *led_cdev, |
388 | static void object##_led_set(struct led_classdev *led_cdev, \ | 363 | enum led_brightness value) |
389 | enum led_brightness value) \ | 364 | { |
390 | { \ | 365 | struct asus_led *led = container_of(led_cdev, struct asus_led, led); |
391 | struct asus_laptop *asus = \ | 366 | struct asus_laptop *asus = led->asus; |
392 | led_to_asus(led_cdev, object); \ | 367 | |
393 | \ | 368 | led->wk = !!value; |
394 | asus->leds.object##_wk = (value > 0) ? 1 : 0; \ | 369 | queue_work(asus->led_workqueue, &led->work); |
395 | queue_work(asus->leds.workqueue, \ | 370 | } |
396 | &asus->leds.object##_work); \ | 371 | |
397 | } \ | 372 | static void asus_led_cdev_update(struct work_struct *work) |
398 | static void object##_led_update(struct work_struct *work) \ | 373 | { |
399 | { \ | 374 | struct asus_led *led = container_of(work, struct asus_led, work); |
400 | struct asus_laptop *asus = work_to_asus(work, object); \ | 375 | struct asus_laptop *asus = led->asus; |
401 | \ | ||
402 | int value = asus->leds.object##_wk; \ | ||
403 | asus_led_set(asus, method, value); \ | ||
404 | } \ | ||
405 | static enum led_brightness object##_led_get( \ | ||
406 | struct led_classdev *led_cdev) \ | ||
407 | { \ | ||
408 | return led_cdev->brightness; \ | ||
409 | } | ||
410 | 376 | ||
411 | ASUS_LED_HANDLER(mled, METHOD_MLED); | 377 | asus_led_set(asus, led->method, led->wk); |
412 | ASUS_LED_HANDLER(pled, METHOD_PLED); | 378 | } |
413 | ASUS_LED_HANDLER(rled, METHOD_RLED); | 379 | |
414 | ASUS_LED_HANDLER(tled, METHOD_TLED); | 380 | static enum led_brightness asus_led_cdev_get(struct led_classdev *led_cdev) |
415 | ASUS_LED_HANDLER(gled, METHOD_GLED); | 381 | { |
382 | return led_cdev->brightness; | ||
383 | } | ||
416 | 384 | ||
417 | /* | 385 | /* |
418 | * Keyboard backlight (also a LED) | 386 | * Keyboard backlight (also a LED) |
@@ -452,70 +420,76 @@ static int asus_kled_set(struct asus_laptop *asus, int kblv) | |||
452 | return 0; | 420 | return 0; |
453 | } | 421 | } |
454 | 422 | ||
455 | static void kled_led_set(struct led_classdev *led_cdev, | 423 | static void asus_kled_cdev_set(struct led_classdev *led_cdev, |
456 | enum led_brightness value) | 424 | enum led_brightness value) |
457 | { | 425 | { |
458 | struct asus_laptop *asus = led_to_asus(led_cdev, kled); | 426 | struct asus_led *led = container_of(led_cdev, struct asus_led, led); |
427 | struct asus_laptop *asus = led->asus; | ||
459 | 428 | ||
460 | asus->leds.kled_wk = value; | 429 | led->wk = value; |
461 | queue_work(asus->leds.workqueue, &asus->leds.kled_work); | 430 | queue_work(asus->led_workqueue, &led->work); |
462 | } | 431 | } |
463 | 432 | ||
464 | static void kled_led_update(struct work_struct *work) | 433 | static void asus_kled_cdev_update(struct work_struct *work) |
465 | { | 434 | { |
466 | struct asus_laptop *asus = work_to_asus(work, kled); | 435 | struct asus_led *led = container_of(work, struct asus_led, work); |
436 | struct asus_laptop *asus = led->asus; | ||
467 | 437 | ||
468 | asus_kled_set(asus, asus->leds.kled_wk); | 438 | asus_kled_set(asus, led->wk); |
469 | } | 439 | } |
470 | 440 | ||
471 | static enum led_brightness kled_led_get(struct led_classdev *led_cdev) | 441 | static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev) |
472 | { | 442 | { |
473 | struct asus_laptop *asus = led_to_asus(led_cdev, kled); | 443 | struct asus_led *led = container_of(led_cdev, struct asus_led, led); |
444 | struct asus_laptop *asus = led->asus; | ||
474 | 445 | ||
475 | return asus_kled_lvl(asus); | 446 | return asus_kled_lvl(asus); |
476 | } | 447 | } |
477 | 448 | ||
478 | static void asus_led_exit(struct asus_laptop *asus) | 449 | static void asus_led_exit(struct asus_laptop *asus) |
479 | { | 450 | { |
480 | if (asus->leds.mled.dev) | 451 | if (asus->mled.led.dev) |
481 | led_classdev_unregister(&asus->leds.mled); | 452 | led_classdev_unregister(&asus->mled.led); |
482 | if (asus->leds.tled.dev) | 453 | if (asus->tled.led.dev) |
483 | led_classdev_unregister(&asus->leds.tled); | 454 | led_classdev_unregister(&asus->tled.led); |
484 | if (asus->leds.pled.dev) | 455 | if (asus->pled.led.dev) |
485 | led_classdev_unregister(&asus->leds.pled); | 456 | led_classdev_unregister(&asus->pled.led); |
486 | if (asus->leds.rled.dev) | 457 | if (asus->rled.led.dev) |
487 | led_classdev_unregister(&asus->leds.rled); | 458 | led_classdev_unregister(&asus->rled.led); |
488 | if (asus->leds.gled.dev) | 459 | if (asus->gled.led.dev) |
489 | led_classdev_unregister(&asus->leds.gled); | 460 | led_classdev_unregister(&asus->gled.led); |
490 | if (asus->leds.kled.dev) | 461 | if (asus->kled.led.dev) |
491 | led_classdev_unregister(&asus->leds.kled); | 462 | led_classdev_unregister(&asus->kled.led); |
492 | if (asus->leds.workqueue) { | 463 | if (asus->led_workqueue) { |
493 | destroy_workqueue(asus->leds.workqueue); | 464 | destroy_workqueue(asus->led_workqueue); |
494 | asus->leds.workqueue = NULL; | 465 | asus->led_workqueue = NULL; |
495 | } | 466 | } |
496 | } | 467 | } |
497 | 468 | ||
498 | /* Ugly macro, need to fix that later */ | 469 | /* Ugly macro, need to fix that later */ |
499 | #define ASUS_LED_REGISTER(asus, object, _name, max, method) \ | 470 | static int asus_led_register(struct asus_laptop *asus, |
500 | do { \ | 471 | struct asus_led *led, |
501 | struct led_classdev *ldev = &asus->leds.object; \ | 472 | const char *name, const char *method) |
502 | \ | 473 | { |
503 | if (method && acpi_check_handle(asus->handle, method, NULL)) \ | 474 | struct led_classdev *led_cdev = &led->led; |
504 | break ; \ | 475 | |
505 | \ | 476 | if (!method || acpi_check_handle(asus->handle, method, NULL)) |
506 | INIT_WORK(&asus->leds.object##_work, object##_led_update); \ | 477 | return 0; /* Led not present */ |
507 | ldev->name = "asus::" _name; \ | 478 | |
508 | ldev->brightness_set = object##_led_set; \ | 479 | led->asus = asus; |
509 | ldev->brightness_get = object##_led_get; \ | 480 | led->method = method; |
510 | ldev->max_brightness = max; \ | 481 | |
511 | rv = led_classdev_register(&asus->platform_device->dev, ldev); \ | 482 | INIT_WORK(&led->work, asus_led_cdev_update); |
512 | if (rv) \ | 483 | led_cdev->name = name; |
513 | goto error; \ | 484 | led_cdev->brightness_set = asus_led_cdev_set; |
514 | } while (0) | 485 | led_cdev->brightness_get = asus_led_cdev_get; |
486 | led_cdev->max_brightness = 1; | ||
487 | return led_classdev_register(&asus->platform_device->dev, led_cdev); | ||
488 | } | ||
515 | 489 | ||
516 | static int asus_led_init(struct asus_laptop *asus) | 490 | static int asus_led_init(struct asus_laptop *asus) |
517 | { | 491 | { |
518 | int rv; | 492 | int r; |
519 | 493 | ||
520 | /* | 494 | /* |
521 | * Functions that actually update the LED's are called from a | 495 | * Functions that actually update the LED's are called from a |
@@ -523,22 +497,43 @@ static int asus_led_init(struct asus_laptop *asus) | |||
523 | * subsystem asks, we avoid messing with the Asus ACPI stuff during a | 497 | * subsystem asks, we avoid messing with the Asus ACPI stuff during a |
524 | * potentially bad time, such as a timer interrupt. | 498 | * potentially bad time, such as a timer interrupt. |
525 | */ | 499 | */ |
526 | asus->leds.workqueue = create_singlethread_workqueue("led_workqueue"); | 500 | asus->led_workqueue = create_singlethread_workqueue("led_workqueue"); |
527 | if (!asus->leds.workqueue) | 501 | if (!asus->led_workqueue) |
528 | return -ENOMEM; | 502 | return -ENOMEM; |
529 | 503 | ||
530 | ASUS_LED_REGISTER(asus, mled, "mail", 1, METHOD_MLED); | 504 | r = asus_led_register(asus, &asus->mled, "asus::mail", METHOD_MLED); |
531 | ASUS_LED_REGISTER(asus, tled, "touchpad", 1, METHOD_TLED); | 505 | if (r) |
532 | ASUS_LED_REGISTER(asus, rled, "record", 1, METHOD_RLED); | 506 | goto error; |
533 | ASUS_LED_REGISTER(asus, pled, "phone", 1, METHOD_PLED); | 507 | r = asus_led_register(asus, &asus->tled, "asus::touchpad", METHOD_TLED); |
534 | ASUS_LED_REGISTER(asus, gled, "gaming", 1, METHOD_GLED); | 508 | if (r) |
509 | goto error; | ||
510 | r = asus_led_register(asus, &asus->rled, "asus::record", METHOD_RLED); | ||
511 | if (r) | ||
512 | goto error; | ||
513 | r = asus_led_register(asus, &asus->pled, "asus::phone", METHOD_PLED); | ||
514 | if (r) | ||
515 | goto error; | ||
516 | r = asus_led_register(asus, &asus->gled, "asus::gaming", METHOD_GLED); | ||
517 | if (r) | ||
518 | goto error; | ||
535 | if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL) && | 519 | if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL) && |
536 | !acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_GET, NULL)) | 520 | !acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_GET, NULL)) { |
537 | ASUS_LED_REGISTER(asus, kled, "kbd_backlight", 3, NULL); | 521 | struct asus_led *led = &asus->kled; |
522 | struct led_classdev *cdev = &led->led; | ||
523 | |||
524 | led->asus = asus; | ||
525 | |||
526 | INIT_WORK(&led->work, asus_kled_cdev_update); | ||
527 | cdev->name = "asus::kbd_backlight"; | ||
528 | cdev->brightness_set = asus_kled_cdev_set; | ||
529 | cdev->brightness_get = asus_kled_cdev_get; | ||
530 | cdev->max_brightness = 3; | ||
531 | r = led_classdev_register(&asus->platform_device->dev, cdev); | ||
532 | } | ||
538 | error: | 533 | error: |
539 | if (rv) | 534 | if (r) |
540 | asus_led_exit(asus); | 535 | asus_led_exit(asus); |
541 | return rv; | 536 | return r; |
542 | } | 537 | } |
543 | 538 | ||
544 | /* | 539 | /* |