aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCorentin Chary <corentincj@iksaif.net>2007-01-26 08:04:45 -0500
committerLen Brown <len.brown@intel.com>2007-01-30 01:37:04 -0500
commit6b7091e74fe176da97917ca60524e2b3554305f0 (patch)
tree384dca7c48096db55fa0a57e6c4fc8e55c86d96e
parent4564de172dcdce641c0d6c689e79e95b5f6bee2c (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/Kconfig1
-rw-r--r--drivers/misc/asus-laptop.c183
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");
101ASUS_HANDLE(bt_switch, ASUS_HOTK_PREFIX "BLED"); 112ASUS_HANDLE(bt_switch, ASUS_HOTK_PREFIX "BLED");
102ASUS_HANDLE(wireless_status, ASUS_HOTK_PREFIX "RSTS"); /* All new models */ 113ASUS_HANDLE(wireless_status, ASUS_HOTK_PREFIX "RSTS"); /* All new models */
103 114
115/* Brightness */
116ASUS_HANDLE(brightness_set, ASUS_HOTK_PREFIX "SPLV");
117ASUS_HANDLE(brightness_get, ASUS_HOTK_PREFIX "GPLV");
118
119/* Backlight */
120ASUS_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 */
166static struct backlight_device *asus_backlight_device;
167
168/*
169 * The backlight class declaration
170 */
171static int read_brightness(struct backlight_device *bd);
172static int update_bl_status(struct backlight_device *bd);
173static 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);
253ASUS_LED_HANDLER(rled, RLED_ON, 0); 292ASUS_LED_HANDLER(rled, RLED_ON, 0);
254ASUS_LED_HANDLER(tled, TLED_ON, 0); 293ASUS_LED_HANDLER(tled, TLED_ON, 0);
255 294
295static int get_lcd_state(void)
296{
297 return read_status(LCD_ON);
298}
299
300static 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
322static 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
337static 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
347static 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
362static 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
784static 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
660static void __exit asus_laptop_exit(void) 805static 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
818static 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
672static int asus_led_register(acpi_handle handle, 846static 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
780fail_led: 958fail_led:
959 asus_backlight_exit();
960
961fail_backlight:
781 962
782 return result; 963 return result;
783} 964}