diff options
Diffstat (limited to 'drivers/platform/x86/classmate-laptop.c')
| -rw-r--r-- | drivers/platform/x86/classmate-laptop.c | 170 |
1 files changed, 148 insertions, 22 deletions
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 7f9e5ddc9498..3bf399fe2bbc 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <acpi/acpi_drivers.h> | 24 | #include <acpi/acpi_drivers.h> |
| 25 | #include <linux/backlight.h> | 25 | #include <linux/backlight.h> |
| 26 | #include <linux/input.h> | 26 | #include <linux/input.h> |
| 27 | #include <linux/rfkill.h> | ||
| 27 | 28 | ||
| 28 | MODULE_LICENSE("GPL"); | 29 | MODULE_LICENSE("GPL"); |
| 29 | 30 | ||
| @@ -37,7 +38,7 @@ struct cmpc_accel { | |||
| 37 | 38 | ||
| 38 | #define CMPC_ACCEL_HID "ACCE0000" | 39 | #define CMPC_ACCEL_HID "ACCE0000" |
| 39 | #define CMPC_TABLET_HID "TBLT0000" | 40 | #define CMPC_TABLET_HID "TBLT0000" |
| 40 | #define CMPC_BL_HID "IPML200" | 41 | #define CMPC_IPML_HID "IPML200" |
| 41 | #define CMPC_KEYS_HID "FnBT0000" | 42 | #define CMPC_KEYS_HID "FnBT0000" |
| 42 | 43 | ||
| 43 | /* | 44 | /* |
| @@ -461,43 +462,168 @@ static const struct backlight_ops cmpc_bl_ops = { | |||
| 461 | .update_status = cmpc_bl_update_status | 462 | .update_status = cmpc_bl_update_status |
| 462 | }; | 463 | }; |
| 463 | 464 | ||
| 464 | static int cmpc_bl_add(struct acpi_device *acpi) | 465 | /* |
| 466 | * RFKILL code. | ||
| 467 | */ | ||
| 468 | |||
| 469 | static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle, | ||
| 470 | unsigned long long *value) | ||
| 465 | { | 471 | { |
| 466 | struct backlight_properties props; | 472 | union acpi_object param; |
| 473 | struct acpi_object_list input; | ||
| 474 | unsigned long long output; | ||
| 475 | acpi_status status; | ||
| 476 | |||
| 477 | param.type = ACPI_TYPE_INTEGER; | ||
| 478 | param.integer.value = 0xC1; | ||
| 479 | input.count = 1; | ||
| 480 | input.pointer = ¶m; | ||
| 481 | status = acpi_evaluate_integer(handle, "GRDI", &input, &output); | ||
| 482 | if (ACPI_SUCCESS(status)) | ||
| 483 | *value = output; | ||
| 484 | return status; | ||
| 485 | } | ||
| 486 | |||
| 487 | static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle, | ||
| 488 | unsigned long long value) | ||
| 489 | { | ||
| 490 | union acpi_object param[2]; | ||
| 491 | struct acpi_object_list input; | ||
| 492 | acpi_status status; | ||
| 493 | unsigned long long output; | ||
| 494 | |||
| 495 | param[0].type = ACPI_TYPE_INTEGER; | ||
| 496 | param[0].integer.value = 0xC1; | ||
| 497 | param[1].type = ACPI_TYPE_INTEGER; | ||
| 498 | param[1].integer.value = value; | ||
| 499 | input.count = 2; | ||
| 500 | input.pointer = param; | ||
| 501 | status = acpi_evaluate_integer(handle, "GWRI", &input, &output); | ||
| 502 | return status; | ||
| 503 | } | ||
| 504 | |||
| 505 | static void cmpc_rfkill_query(struct rfkill *rfkill, void *data) | ||
| 506 | { | ||
| 507 | acpi_status status; | ||
| 508 | acpi_handle handle; | ||
| 509 | unsigned long long state; | ||
| 510 | bool blocked; | ||
| 511 | |||
| 512 | handle = data; | ||
| 513 | status = cmpc_get_rfkill_wlan(handle, &state); | ||
| 514 | if (ACPI_SUCCESS(status)) { | ||
| 515 | blocked = state & 1 ? false : true; | ||
| 516 | rfkill_set_sw_state(rfkill, blocked); | ||
| 517 | } | ||
| 518 | } | ||
| 519 | |||
| 520 | static int cmpc_rfkill_block(void *data, bool blocked) | ||
| 521 | { | ||
| 522 | acpi_status status; | ||
| 523 | acpi_handle handle; | ||
| 524 | unsigned long long state; | ||
| 525 | |||
| 526 | handle = data; | ||
| 527 | status = cmpc_get_rfkill_wlan(handle, &state); | ||
| 528 | if (ACPI_FAILURE(status)) | ||
| 529 | return -ENODEV; | ||
| 530 | if (blocked) | ||
| 531 | state &= ~1; | ||
| 532 | else | ||
| 533 | state |= 1; | ||
| 534 | status = cmpc_set_rfkill_wlan(handle, state); | ||
| 535 | if (ACPI_FAILURE(status)) | ||
| 536 | return -ENODEV; | ||
| 537 | return 0; | ||
| 538 | } | ||
| 539 | |||
| 540 | static const struct rfkill_ops cmpc_rfkill_ops = { | ||
| 541 | .query = cmpc_rfkill_query, | ||
| 542 | .set_block = cmpc_rfkill_block, | ||
| 543 | }; | ||
| 544 | |||
| 545 | /* | ||
| 546 | * Common backlight and rfkill code. | ||
| 547 | */ | ||
| 548 | |||
| 549 | struct ipml200_dev { | ||
| 467 | struct backlight_device *bd; | 550 | struct backlight_device *bd; |
| 551 | struct rfkill *rf; | ||
| 552 | }; | ||
| 553 | |||
| 554 | static int cmpc_ipml_add(struct acpi_device *acpi) | ||
| 555 | { | ||
| 556 | int retval; | ||
| 557 | struct ipml200_dev *ipml; | ||
| 558 | struct backlight_properties props; | ||
| 559 | |||
| 560 | ipml = kmalloc(sizeof(*ipml), GFP_KERNEL); | ||
| 561 | if (ipml == NULL) | ||
| 562 | return -ENOMEM; | ||
| 468 | 563 | ||
| 469 | memset(&props, 0, sizeof(struct backlight_properties)); | 564 | memset(&props, 0, sizeof(struct backlight_properties)); |
| 470 | props.max_brightness = 7; | 565 | props.max_brightness = 7; |
| 471 | bd = backlight_device_register("cmpc_bl", &acpi->dev, acpi->handle, | 566 | ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev, |
| 472 | &cmpc_bl_ops, &props); | 567 | acpi->handle, &cmpc_bl_ops, |
| 473 | if (IS_ERR(bd)) | 568 | &props); |
| 474 | return PTR_ERR(bd); | 569 | if (IS_ERR(ipml->bd)) { |
| 475 | dev_set_drvdata(&acpi->dev, bd); | 570 | retval = PTR_ERR(ipml->bd); |
| 571 | goto out_bd; | ||
| 572 | } | ||
| 573 | |||
| 574 | ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN, | ||
| 575 | &cmpc_rfkill_ops, acpi->handle); | ||
| 576 | /* rfkill_alloc may fail if RFKILL is disabled. We should still work | ||
| 577 | * anyway. */ | ||
| 578 | if (!IS_ERR(ipml->rf)) { | ||
| 579 | retval = rfkill_register(ipml->rf); | ||
| 580 | if (retval) { | ||
| 581 | rfkill_destroy(ipml->rf); | ||
| 582 | ipml->rf = NULL; | ||
| 583 | } | ||
| 584 | } else { | ||
| 585 | ipml->rf = NULL; | ||
| 586 | } | ||
| 587 | |||
| 588 | dev_set_drvdata(&acpi->dev, ipml); | ||
| 476 | return 0; | 589 | return 0; |
| 590 | |||
| 591 | out_bd: | ||
| 592 | kfree(ipml); | ||
| 593 | return retval; | ||
| 477 | } | 594 | } |
| 478 | 595 | ||
| 479 | static int cmpc_bl_remove(struct acpi_device *acpi, int type) | 596 | static int cmpc_ipml_remove(struct acpi_device *acpi, int type) |
| 480 | { | 597 | { |
| 481 | struct backlight_device *bd; | 598 | struct ipml200_dev *ipml; |
| 599 | |||
| 600 | ipml = dev_get_drvdata(&acpi->dev); | ||
| 601 | |||
| 602 | backlight_device_unregister(ipml->bd); | ||
| 603 | |||
| 604 | if (ipml->rf) { | ||
| 605 | rfkill_unregister(ipml->rf); | ||
| 606 | rfkill_destroy(ipml->rf); | ||
| 607 | } | ||
| 608 | |||
| 609 | kfree(ipml); | ||
| 482 | 610 | ||
| 483 | bd = dev_get_drvdata(&acpi->dev); | ||
| 484 | backlight_device_unregister(bd); | ||
| 485 | return 0; | 611 | return 0; |
| 486 | } | 612 | } |
| 487 | 613 | ||
| 488 | static const struct acpi_device_id cmpc_bl_device_ids[] = { | 614 | static const struct acpi_device_id cmpc_ipml_device_ids[] = { |
| 489 | {CMPC_BL_HID, 0}, | 615 | {CMPC_IPML_HID, 0}, |
| 490 | {"", 0} | 616 | {"", 0} |
| 491 | }; | 617 | }; |
| 492 | 618 | ||
| 493 | static struct acpi_driver cmpc_bl_acpi_driver = { | 619 | static struct acpi_driver cmpc_ipml_acpi_driver = { |
| 494 | .owner = THIS_MODULE, | 620 | .owner = THIS_MODULE, |
| 495 | .name = "cmpc", | 621 | .name = "cmpc", |
| 496 | .class = "cmpc", | 622 | .class = "cmpc", |
| 497 | .ids = cmpc_bl_device_ids, | 623 | .ids = cmpc_ipml_device_ids, |
| 498 | .ops = { | 624 | .ops = { |
| 499 | .add = cmpc_bl_add, | 625 | .add = cmpc_ipml_add, |
| 500 | .remove = cmpc_bl_remove | 626 | .remove = cmpc_ipml_remove |
| 501 | } | 627 | } |
| 502 | }; | 628 | }; |
| 503 | 629 | ||
| @@ -580,7 +706,7 @@ static int cmpc_init(void) | |||
| 580 | if (r) | 706 | if (r) |
| 581 | goto failed_keys; | 707 | goto failed_keys; |
| 582 | 708 | ||
| 583 | r = acpi_bus_register_driver(&cmpc_bl_acpi_driver); | 709 | r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver); |
| 584 | if (r) | 710 | if (r) |
| 585 | goto failed_bl; | 711 | goto failed_bl; |
| 586 | 712 | ||
| @@ -598,7 +724,7 @@ failed_accel: | |||
| 598 | acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); | 724 | acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); |
| 599 | 725 | ||
| 600 | failed_tablet: | 726 | failed_tablet: |
| 601 | acpi_bus_unregister_driver(&cmpc_bl_acpi_driver); | 727 | acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); |
| 602 | 728 | ||
| 603 | failed_bl: | 729 | failed_bl: |
| 604 | acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); | 730 | acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); |
| @@ -611,7 +737,7 @@ static void cmpc_exit(void) | |||
| 611 | { | 737 | { |
| 612 | acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); | 738 | acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); |
| 613 | acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); | 739 | acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); |
| 614 | acpi_bus_unregister_driver(&cmpc_bl_acpi_driver); | 740 | acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); |
| 615 | acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); | 741 | acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); |
| 616 | } | 742 | } |
| 617 | 743 | ||
| @@ -621,7 +747,7 @@ module_exit(cmpc_exit); | |||
| 621 | static const struct acpi_device_id cmpc_device_ids[] = { | 747 | static const struct acpi_device_id cmpc_device_ids[] = { |
| 622 | {CMPC_ACCEL_HID, 0}, | 748 | {CMPC_ACCEL_HID, 0}, |
| 623 | {CMPC_TABLET_HID, 0}, | 749 | {CMPC_TABLET_HID, 0}, |
| 624 | {CMPC_BL_HID, 0}, | 750 | {CMPC_IPML_HID, 0}, |
| 625 | {CMPC_KEYS_HID, 0}, | 751 | {CMPC_KEYS_HID, 0}, |
| 626 | {"", 0} | 752 | {"", 0} |
| 627 | }; | 753 | }; |
