diff options
| -rw-r--r-- | drivers/misc/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/misc/asus-laptop.c | 183 |
2 files changed, 183 insertions, 1 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 87e1db8ffd47..89bba277da5f 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
| @@ -75,6 +75,7 @@ config ASUS_LAPTOP | |||
| 75 | depends on ACPI | 75 | depends on ACPI |
| 76 | depends on EXPERIMENTAL && !ACPI_ASUS | 76 | depends on EXPERIMENTAL && !ACPI_ASUS |
| 77 | depends on LEDS_CLASS | 77 | depends on LEDS_CLASS |
| 78 | depends on BACKLIGHT_CLASS_DEVICE | ||
| 78 | ---help--- | 79 | ---help--- |
| 79 | This is the new Linux driver for Asus laptops. It may also support some | 80 | This is the new Linux driver for Asus laptops. It may also support some |
| 80 | MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate | 81 | MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate |
diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index 222d4fba0ffa..58ab44dc52d8 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c | |||
| @@ -40,6 +40,8 @@ | |||
| 40 | #include <linux/types.h> | 40 | #include <linux/types.h> |
| 41 | #include <linux/err.h> | 41 | #include <linux/err.h> |
| 42 | #include <linux/proc_fs.h> | 42 | #include <linux/proc_fs.h> |
| 43 | #include <linux/backlight.h> | ||
| 44 | #include <linux/fb.h> | ||
| 43 | #include <linux/leds.h> | 45 | #include <linux/leds.h> |
| 44 | #include <linux/platform_device.h> | 46 | #include <linux/platform_device.h> |
| 45 | #include <acpi/acpi_drivers.h> | 47 | #include <acpi/acpi_drivers.h> |
| @@ -56,6 +58,14 @@ | |||
| 56 | #define ASUS_HOTK_PREFIX "\\_SB.ATKD." | 58 | #define ASUS_HOTK_PREFIX "\\_SB.ATKD." |
| 57 | 59 | ||
| 58 | /* | 60 | /* |
| 61 | * Some events we use, same for all Asus | ||
| 62 | */ | ||
| 63 | #define ATKD_BR_UP 0x10 | ||
| 64 | #define ATKD_BR_DOWN 0x20 | ||
| 65 | #define ATKD_LCD_ON 0x33 | ||
| 66 | #define ATKD_LCD_OFF 0x34 | ||
| 67 | |||
| 68 | /* | ||
| 59 | * Known bits returned by \_SB.ATKD.HWRS | 69 | * Known bits returned by \_SB.ATKD.HWRS |
| 60 | */ | 70 | */ |
| 61 | #define WL_HWRS 0x80 | 71 | #define WL_HWRS 0x80 |
| @@ -71,6 +81,7 @@ | |||
| 71 | #define TLED_ON 0x08 //touchpad LED | 81 | #define TLED_ON 0x08 //touchpad LED |
| 72 | #define RLED_ON 0x10 //Record LED | 82 | #define RLED_ON 0x10 //Record LED |
| 73 | #define PLED_ON 0x20 //Phone LED | 83 | #define PLED_ON 0x20 //Phone LED |
| 84 | #define LCD_ON 0x40 //LCD backlight | ||
| 74 | 85 | ||
| 75 | #define ASUS_LOG ASUS_HOTK_FILE ": " | 86 | #define ASUS_LOG ASUS_HOTK_FILE ": " |
| 76 | #define ASUS_ERR KERN_ERR ASUS_LOG | 87 | #define ASUS_ERR KERN_ERR ASUS_LOG |
| @@ -101,6 +112,19 @@ ASUS_HANDLE(wl_switch, ASUS_HOTK_PREFIX "WLED"); | |||
| 101 | ASUS_HANDLE(bt_switch, ASUS_HOTK_PREFIX "BLED"); | 112 | ASUS_HANDLE(bt_switch, ASUS_HOTK_PREFIX "BLED"); |
| 102 | ASUS_HANDLE(wireless_status, ASUS_HOTK_PREFIX "RSTS"); /* All new models */ | 113 | ASUS_HANDLE(wireless_status, ASUS_HOTK_PREFIX "RSTS"); /* All new models */ |
| 103 | 114 | ||
| 115 | /* Brightness */ | ||
| 116 | ASUS_HANDLE(brightness_set, ASUS_HOTK_PREFIX "SPLV"); | ||
| 117 | ASUS_HANDLE(brightness_get, ASUS_HOTK_PREFIX "GPLV"); | ||
| 118 | |||
| 119 | /* Backlight */ | ||
| 120 | ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */ | ||
| 121 | "\\_SB.PCI0.ISA.EC0._Q10", /* A1x */ | ||
| 122 | "\\_SB.PCI0.PX40.ECD0._Q10", /* L3C */ | ||
| 123 | "\\_SB.PCI0.PX40.EC0.Q10", /* M1A */ | ||
| 124 | "\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */ | ||
| 125 | "\\_SB.PCI0.PX40.Q10", /* S1x */ | ||
| 126 | "\\Q10"); /* A2x, L2D, L3D, M2E */ | ||
| 127 | |||
| 104 | /* | 128 | /* |
| 105 | * This is the main structure, we can use it to store anything interesting | 129 | * This is the main structure, we can use it to store anything interesting |
| 106 | * about the hotk device | 130 | * about the hotk device |
| @@ -138,6 +162,21 @@ static struct acpi_driver asus_hotk_driver = { | |||
| 138 | }, | 162 | }, |
| 139 | }; | 163 | }; |
| 140 | 164 | ||
| 165 | /* The backlight device /sys/class/backlight */ | ||
| 166 | static struct backlight_device *asus_backlight_device; | ||
| 167 | |||
| 168 | /* | ||
| 169 | * The backlight class declaration | ||
| 170 | */ | ||
| 171 | static int read_brightness(struct backlight_device *bd); | ||
| 172 | static int update_bl_status(struct backlight_device *bd); | ||
| 173 | static struct backlight_properties asusbl_data = { | ||
| 174 | .owner = THIS_MODULE, | ||
| 175 | .get_brightness = read_brightness, | ||
| 176 | .update_status = update_bl_status, | ||
| 177 | .max_brightness = 15, | ||
| 178 | }; | ||
| 179 | |||
| 141 | /* These functions actually update the LED's, and are called from a | 180 | /* These functions actually update the LED's, and are called from a |
| 142 | * workqueue. By doing this as separate work rather than when the LED | 181 | * workqueue. By doing this as separate work rather than when the LED |
| 143 | * subsystem asks, we avoid messing with the Asus ACPI stuff during a | 182 | * subsystem asks, we avoid messing with the Asus ACPI stuff during a |
| @@ -253,6 +292,86 @@ ASUS_LED_HANDLER(pled, PLED_ON, 0); | |||
| 253 | ASUS_LED_HANDLER(rled, RLED_ON, 0); | 292 | ASUS_LED_HANDLER(rled, RLED_ON, 0); |
| 254 | ASUS_LED_HANDLER(tled, TLED_ON, 0); | 293 | ASUS_LED_HANDLER(tled, TLED_ON, 0); |
| 255 | 294 | ||
| 295 | static int get_lcd_state(void) | ||
| 296 | { | ||
| 297 | return read_status(LCD_ON); | ||
| 298 | } | ||
| 299 | |||
| 300 | static int set_lcd_state(int value) | ||
| 301 | { | ||
| 302 | int lcd = 0; | ||
| 303 | acpi_status status = 0; | ||
| 304 | |||
| 305 | lcd = value ? 1 : 0; | ||
| 306 | |||
| 307 | if (lcd == get_lcd_state()) | ||
| 308 | return 0; | ||
| 309 | |||
| 310 | if(lcd_switch_handle) { | ||
| 311 | status = acpi_evaluate_object(lcd_switch_handle, | ||
| 312 | NULL, NULL, NULL); | ||
| 313 | |||
| 314 | if (ACPI_FAILURE(status)) | ||
| 315 | printk(ASUS_WARNING "Error switching LCD\n"); | ||
| 316 | } | ||
| 317 | |||
| 318 | write_status(NULL, lcd, LCD_ON, 0); | ||
| 319 | return 0; | ||
| 320 | } | ||
| 321 | |||
| 322 | static void lcd_blank(int blank) | ||
| 323 | { | ||
| 324 | struct backlight_device *bd = asus_backlight_device; | ||
| 325 | |||
| 326 | if(bd) { | ||
| 327 | down(&bd->sem); | ||
| 328 | if(likely(bd->props)) { | ||
| 329 | bd->props->power = blank; | ||
| 330 | if(likely(bd->props->update_status)) | ||
| 331 | bd->props->update_status(bd); | ||
| 332 | } | ||
| 333 | up(&bd->sem); | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | static int read_brightness(struct backlight_device *bd) | ||
| 338 | { | ||
| 339 | int value; | ||
| 340 | |||
| 341 | if (!read_acpi_int(brightness_get_handle, NULL, &value, NULL)) | ||
| 342 | printk(ASUS_WARNING "Error reading brightness\n"); | ||
| 343 | |||
| 344 | return value; | ||
| 345 | } | ||
| 346 | |||
| 347 | static int set_brightness(struct backlight_device *bd, int value) | ||
| 348 | { | ||
| 349 | int ret = 0; | ||
| 350 | |||
| 351 | value = (0 < value) ? ((15 < value) ? 15 : value) : 0; | ||
| 352 | /* 0 <= value <= 15 */ | ||
| 353 | |||
| 354 | if (!write_acpi_int(brightness_set_handle, NULL, value, NULL)) { | ||
| 355 | printk(ASUS_WARNING "Error changing brightness\n"); | ||
| 356 | ret = -EIO; | ||
| 357 | } | ||
| 358 | |||
| 359 | return ret; | ||
| 360 | } | ||
| 361 | |||
| 362 | static int update_bl_status(struct backlight_device *bd) | ||
| 363 | { | ||
| 364 | int rv; | ||
| 365 | int value = bd->props->brightness; | ||
| 366 | |||
| 367 | rv = set_brightness(bd, value); | ||
| 368 | if(rv) | ||
| 369 | return rv; | ||
| 370 | |||
| 371 | value = (bd->props->power == FB_BLANK_UNBLANK) ? 1 : 0; | ||
| 372 | return set_lcd_state(value); | ||
| 373 | } | ||
| 374 | |||
| 256 | /* | 375 | /* |
| 257 | * Platform device handlers | 376 | * Platform device handlers |
| 258 | */ | 377 | */ |
| @@ -378,6 +497,18 @@ static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) | |||
| 378 | if (!hotk) | 497 | if (!hotk) |
| 379 | return; | 498 | return; |
| 380 | 499 | ||
| 500 | /* | ||
| 501 | * We need to tell the backlight device when the backlight power is | ||
| 502 | * switched | ||
| 503 | */ | ||
| 504 | if (event == ATKD_LCD_ON) { | ||
| 505 | write_status(NULL, 1, LCD_ON, 0); | ||
| 506 | lcd_blank(FB_BLANK_UNBLANK); | ||
| 507 | } else if(event == ATKD_LCD_OFF) { | ||
| 508 | write_status(NULL, 0, LCD_ON, 0); | ||
| 509 | lcd_blank(FB_BLANK_POWERDOWN); | ||
| 510 | } | ||
| 511 | |||
| 381 | acpi_bus_generate_event(hotk->device, event, | 512 | acpi_bus_generate_event(hotk->device, event, |
| 382 | hotk->event_count[event % 128]++); | 513 | hotk->event_count[event % 128]++); |
| 383 | 514 | ||
| @@ -547,6 +678,11 @@ static int asus_hotk_get_info(void) | |||
| 547 | 678 | ||
| 548 | ASUS_HANDLE_INIT(wireless_status); | 679 | ASUS_HANDLE_INIT(wireless_status); |
| 549 | 680 | ||
| 681 | ASUS_HANDLE_INIT(brightness_set); | ||
| 682 | ASUS_HANDLE_INIT(brightness_get); | ||
| 683 | |||
| 684 | ASUS_HANDLE_INIT(lcd_switch); | ||
| 685 | |||
| 550 | kfree(model); | 686 | kfree(model); |
| 551 | 687 | ||
| 552 | return AE_OK; | 688 | return AE_OK; |
| @@ -611,10 +747,13 @@ static int asus_hotk_add(struct acpi_device *device) | |||
| 611 | 747 | ||
| 612 | asus_hotk_found = 1; | 748 | asus_hotk_found = 1; |
| 613 | 749 | ||
| 614 | /* WLED and BLED are on by default */ | 750 | /* WLED and BLED are on by default */ |
| 615 | write_status(bt_switch_handle, 1, BT_ON, 0); | 751 | write_status(bt_switch_handle, 1, BT_ON, 0); |
| 616 | write_status(wl_switch_handle, 1, WL_ON, 0); | 752 | write_status(wl_switch_handle, 1, WL_ON, 0); |
| 617 | 753 | ||
| 754 | /* LCD Backlight is on by default */ | ||
| 755 | write_status(NULL, 1, LCD_ON, 0); | ||
| 756 | |||
| 618 | end: | 757 | end: |
| 619 | if (result) { | 758 | if (result) { |
| 620 | kfree(hotk->name); | 759 | kfree(hotk->name); |
| @@ -642,6 +781,12 @@ static int asus_hotk_remove(struct acpi_device *device, int type) | |||
| 642 | return 0; | 781 | return 0; |
| 643 | } | 782 | } |
| 644 | 783 | ||
| 784 | static void asus_backlight_exit(void) | ||
| 785 | { | ||
| 786 | if(asus_backlight_device) | ||
| 787 | backlight_device_unregister(asus_backlight_device); | ||
| 788 | } | ||
| 789 | |||
| 645 | #define ASUS_LED_UNREGISTER(object) \ | 790 | #define ASUS_LED_UNREGISTER(object) \ |
| 646 | if(object##_led.class_dev \ | 791 | if(object##_led.class_dev \ |
| 647 | && !IS_ERR(object##_led.class_dev)) \ | 792 | && !IS_ERR(object##_led.class_dev)) \ |
| @@ -659,6 +804,7 @@ static void asus_led_exit(void) | |||
| 659 | 804 | ||
| 660 | static void __exit asus_laptop_exit(void) | 805 | static void __exit asus_laptop_exit(void) |
| 661 | { | 806 | { |
| 807 | asus_backlight_exit(); | ||
| 662 | asus_led_exit(); | 808 | asus_led_exit(); |
| 663 | 809 | ||
| 664 | acpi_bus_unregister_driver(&asus_hotk_driver); | 810 | acpi_bus_unregister_driver(&asus_hotk_driver); |
| @@ -669,6 +815,34 @@ static void __exit asus_laptop_exit(void) | |||
| 669 | kfree(asus_info); | 815 | kfree(asus_info); |
| 670 | } | 816 | } |
| 671 | 817 | ||
| 818 | static int asus_backlight_init(struct device * dev) | ||
| 819 | { | ||
| 820 | struct backlight_device *bd; | ||
| 821 | |||
| 822 | if(brightness_set_handle && lcd_switch_handle) { | ||
| 823 | bd = backlight_device_register (ASUS_HOTK_FILE, dev, | ||
| 824 | NULL, &asusbl_data); | ||
| 825 | if (IS_ERR (bd)) { | ||
| 826 | printk(ASUS_ERR | ||
| 827 | "Could not register asus backlight device\n"); | ||
| 828 | asus_backlight_device = NULL; | ||
| 829 | return PTR_ERR(bd); | ||
| 830 | } | ||
| 831 | |||
| 832 | asus_backlight_device = bd; | ||
| 833 | |||
| 834 | down(&bd->sem); | ||
| 835 | if(likely(bd->props)) { | ||
| 836 | bd->props->brightness = read_brightness(NULL); | ||
| 837 | bd->props->power = FB_BLANK_UNBLANK; | ||
| 838 | if(likely(bd->props->update_status)) | ||
| 839 | bd->props->update_status(bd); | ||
| 840 | } | ||
| 841 | up(&bd->sem); | ||
| 842 | } | ||
| 843 | return 0; | ||
| 844 | } | ||
| 845 | |||
| 672 | static int asus_led_register(acpi_handle handle, | 846 | static int asus_led_register(acpi_handle handle, |
| 673 | struct led_classdev * ldev, | 847 | struct led_classdev * ldev, |
| 674 | struct device * dev) | 848 | struct device * dev) |
| @@ -739,6 +913,10 @@ static int __init asus_laptop_init(void) | |||
| 739 | 913 | ||
| 740 | dev = acpi_get_physical_device(hotk->device->handle); | 914 | dev = acpi_get_physical_device(hotk->device->handle); |
| 741 | 915 | ||
| 916 | result = asus_backlight_init(dev); | ||
| 917 | if(result) | ||
| 918 | goto fail_backlight; | ||
| 919 | |||
| 742 | result = asus_led_init(dev); | 920 | result = asus_led_init(dev); |
| 743 | if(result) | 921 | if(result) |
| 744 | goto fail_led; | 922 | goto fail_led; |
| @@ -778,6 +956,9 @@ fail_platform_driver: | |||
| 778 | asus_led_exit(); | 956 | asus_led_exit(); |
| 779 | 957 | ||
| 780 | fail_led: | 958 | fail_led: |
| 959 | asus_backlight_exit(); | ||
| 960 | |||
| 961 | fail_backlight: | ||
| 781 | 962 | ||
| 782 | return result; | 963 | return result; |
| 783 | } | 964 | } |
