diff options
author | Corentin Chary <corentincj@iksaif.net> | 2007-01-26 08:04:45 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2007-01-30 01:37:04 -0500 |
commit | 6b7091e74fe176da97917ca60524e2b3554305f0 (patch) | |
tree | 384dca7c48096db55fa0a57e6c4fc8e55c86d96e | |
parent | 4564de172dcdce641c0d6c689e79e95b5f6bee2c (diff) |
asus-laptop: add backlight support
Adds backlight support using backlight class. We now
change the brightness *and toggle the backlight !* via
/sys/class/backlight/asus-laptop/.
If the user switchs the backlight using the keyboard,
asus_hotk_notify looks for ATKD_LCD_OFF and ATKD_LCD_ON events,
and stores the right state into hotk->status and bd->props->power .
Signed-off-by: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Len Brown <len.brown@intel.com>
-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 | } |