aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/platform/x86/asus-laptop.c440
1 files changed, 223 insertions, 217 deletions
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index 58a4864096d9..940ce3d63229 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -58,36 +58,6 @@
58#define ASUS_LAPTOP_FILE KBUILD_MODNAME 58#define ASUS_LAPTOP_FILE KBUILD_MODNAME
59#define ASUS_LAPTOP_PREFIX "\\_SB.ATKD." 59#define ASUS_LAPTOP_PREFIX "\\_SB.ATKD."
60 60
61
62/*
63 * Some events we use, same for all Asus
64 */
65#define ATKD_BR_UP 0x10
66#define ATKD_BR_DOWN 0x20
67#define ATKD_LCD_ON 0x33
68#define ATKD_LCD_OFF 0x34
69
70/*
71 * Known bits returned by \_SB.ATKD.HWRS
72 */
73#define WL_HWRS 0x80
74#define BT_HWRS 0x100
75
76/*
77 * Flags for hotk status
78 * WL_ON and BT_ON are also used for wireless_status()
79 */
80#define WL_ON 0x01 /* internal Wifi */
81#define BT_ON 0x02 /* internal Bluetooth */
82#define MLED_ON 0x04 /* mail LED */
83#define TLED_ON 0x08 /* touchpad LED */
84#define RLED_ON 0x10 /* Record LED */
85#define PLED_ON 0x20 /* Phone LED */
86#define GLED_ON 0x40 /* Gaming LED */
87#define LCD_ON 0x80 /* LCD backlight */
88#define GPS_ON 0x100 /* GPS */
89#define KEY_ON 0x200 /* Keyboard backlight */
90
91MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary"); 61MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
92MODULE_DESCRIPTION(ASUS_LAPTOP_NAME); 62MODULE_DESCRIPTION(ASUS_LAPTOP_NAME);
93MODULE_LICENSE("GPL"); 63MODULE_LICENSE("GPL");
@@ -119,6 +89,35 @@ MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot "
119 "(0 = disabled, 1 = enabled, -1 = don't do anything). " 89 "(0 = disabled, 1 = enabled, -1 = don't do anything). "
120 "default is 1"); 90 "default is 1");
121 91
92/*
93 * Some events we use, same for all Asus
94 */
95#define ATKD_BR_UP 0x10
96#define ATKD_BR_DOWN 0x20
97#define ATKD_LCD_ON 0x33
98#define ATKD_LCD_OFF 0x34
99
100/*
101 * Known bits returned by \_SB.ATKD.HWRS
102 */
103#define WL_HWRS 0x80
104#define BT_HWRS 0x100
105
106/*
107 * Flags for hotk status
108 * WL_ON and BT_ON are also used for wireless_status()
109 */
110#define WL_ON 0x01 /* internal Wifi */
111#define BT_ON 0x02 /* internal Bluetooth */
112#define MLED_ON 0x04 /* mail LED */
113#define TLED_ON 0x08 /* touchpad LED */
114#define RLED_ON 0x10 /* Record LED */
115#define PLED_ON 0x20 /* Phone LED */
116#define GLED_ON 0x40 /* Gaming LED */
117#define LCD_ON 0x80 /* LCD backlight */
118#define GPS_ON 0x100 /* GPS */
119#define KEY_ON 0x200 /* Keyboard backlight */
120
122#define ASUS_HANDLE(object, paths...) \ 121#define ASUS_HANDLE(object, paths...) \
123 static acpi_handle object##_handle = NULL; \ 122 static acpi_handle object##_handle = NULL; \
124 static char *object##_paths[] = { paths } 123 static char *object##_paths[] = { paths }
@@ -249,36 +248,6 @@ struct asus_laptop {
249 u16 *keycode_map; 248 u16 *keycode_map;
250}; 249};
251 250
252/*
253 * The backlight class declaration
254 */
255static int read_brightness(struct backlight_device *bd);
256static int update_bl_status(struct backlight_device *bd);
257static struct backlight_ops asusbl_ops = {
258 .get_brightness = read_brightness,
259 .update_status = update_bl_status,
260};
261
262#define ASUS_LED(object, ledname, max) \
263 static void object##_led_set(struct led_classdev *led_cdev, \
264 enum led_brightness value); \
265 static enum led_brightness object##_led_get( \
266 struct led_classdev *led_cdev); \
267 static void object##_led_update(struct work_struct *ignored); \
268 static struct led_classdev object##_led = { \
269 .name = "asus::" ledname, \
270 .brightness_set = object##_led_set, \
271 .brightness_get = object##_led_get, \
272 .max_brightness = max \
273 }
274
275ASUS_LED(mled, "mail", 1);
276ASUS_LED(tled, "touchpad", 1);
277ASUS_LED(rled, "record", 1);
278ASUS_LED(pled, "phone", 1);
279ASUS_LED(gled, "gaming", 1);
280ASUS_LED(kled, "kbd_backlight", 3);
281
282struct key_entry { 251struct key_entry {
283 char type; 252 char type;
284 u8 code; 253 u8 code;
@@ -427,6 +396,29 @@ static void write_status(struct asus_laptop *asus, acpi_handle handle,
427 pr_warning(" write failed %x\n", mask); 396 pr_warning(" write failed %x\n", mask);
428} 397}
429 398
399/*
400 * LEDs
401 */
402#define ASUS_LED(object, ledname, max) \
403 static void object##_led_set(struct led_classdev *led_cdev, \
404 enum led_brightness value); \
405 static enum led_brightness object##_led_get( \
406 struct led_classdev *led_cdev); \
407 static void object##_led_update(struct work_struct *ignored); \
408 static struct led_classdev object##_led = { \
409 .name = "asus::" ledname, \
410 .brightness_set = object##_led_set, \
411 .brightness_get = object##_led_get, \
412 .max_brightness = max \
413 }
414
415ASUS_LED(mled, "mail", 1);
416ASUS_LED(tled, "touchpad", 1);
417ASUS_LED(rled, "record", 1);
418ASUS_LED(pled, "phone", 1);
419ASUS_LED(gled, "gaming", 1);
420ASUS_LED(kled, "kbd_backlight", 3);
421
430/* /sys/class/led handlers */ 422/* /sys/class/led handlers */
431#define ASUS_LED_HANDLER(object, mask) \ 423#define ASUS_LED_HANDLER(object, mask) \
432 static void object##_led_set(struct led_classdev *led_cdev, \ 424 static void object##_led_set(struct led_classdev *led_cdev, \
@@ -459,7 +451,7 @@ ASUS_LED_HANDLER(tled, TLED_ON);
459ASUS_LED_HANDLER(gled, GLED_ON); 451ASUS_LED_HANDLER(gled, GLED_ON);
460 452
461/* 453/*
462 * Keyboard backlight 454 * Keyboard backlight (also a LED)
463 */ 455 */
464static int get_kled_lvl(void) 456static int get_kled_lvl(void)
465{ 457{
@@ -516,6 +508,70 @@ static enum led_brightness kled_led_get(struct led_classdev *led_cdev)
516 return get_kled_lvl(); 508 return get_kled_lvl();
517} 509}
518 510
511#define ASUS_LED_UNREGISTER(object) \
512 if (object##_led.dev) \
513 led_classdev_unregister(&object##_led)
514
515static void asus_led_exit(struct asus_laptop *asus)
516{
517 ASUS_LED_UNREGISTER(mled);
518 ASUS_LED_UNREGISTER(tled);
519 ASUS_LED_UNREGISTER(pled);
520 ASUS_LED_UNREGISTER(rled);
521 ASUS_LED_UNREGISTER(gled);
522 ASUS_LED_UNREGISTER(kled);
523 if (asus->leds.workqueue) {
524 destroy_workqueue(asus->leds.workqueue);
525 asus->leds.workqueue = NULL;
526 }
527}
528
529/* Ugly macro, need to fix that later */
530#define ASUS_LED_REGISTER(asus, object, _name, max) \
531 do { \
532 struct led_classdev *ldev = &asus->leds.object; \
533 if (!object##_set_handle) \
534 break ; \
535 \
536 INIT_WORK(&asus->leds.object##_work, object##_led_update); \
537 ldev->name = "asus::" _name; \
538 ldev->brightness_set = object##_led_set; \
539 ldev->max_brightness = max; \
540 rv = led_classdev_register(&asus->platform_device->dev, ldev); \
541 if (rv) \
542 goto error; \
543 } while (0)
544
545static int asus_led_init(struct asus_laptop *asus)
546{
547 int rv;
548
549 /*
550 * Functions that actually update the LED's are called from a
551 * workqueue. By doing this as separate work rather than when the LED
552 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
553 * potentially bad time, such as a timer interrupt.
554 */
555 asus->leds.workqueue = create_singlethread_workqueue("led_workqueue");
556 if (!asus->leds.workqueue)
557 return -ENOMEM;
558
559 ASUS_LED_REGISTER(asus, mled, "mail", 1);
560 ASUS_LED_REGISTER(asus, tled, "touchpad", 1);
561 ASUS_LED_REGISTER(asus, rled, "record", 1);
562 ASUS_LED_REGISTER(asus, pled, "phone", 1);
563 ASUS_LED_REGISTER(asus, gled, "gaming", 1);
564 if (kled_set_handle && kled_get_handle)
565 ASUS_LED_REGISTER(asus, kled, "kbd_backlight", 3);
566error:
567 if (rv)
568 asus_led_exit(asus);
569 return rv;
570}
571
572/*
573 * Backlight device
574 */
519static int get_lcd_state(struct asus_laptop *asus) 575static int get_lcd_state(struct asus_laptop *asus)
520{ 576{
521 return read_status(asus, LCD_ON); 577 return read_status(asus, LCD_ON);
@@ -588,6 +644,41 @@ static int update_bl_status(struct backlight_device *bd)
588 return set_lcd_state(asus, value); 644 return set_lcd_state(asus, value);
589} 645}
590 646
647static struct backlight_ops asusbl_ops = {
648 .get_brightness = read_brightness,
649 .update_status = update_bl_status,
650};
651
652static int asus_backlight_init(struct asus_laptop *asus)
653{
654 struct backlight_device *bd;
655 struct device *dev = &asus->platform_device->dev;
656
657 if (brightness_set_handle && lcd_switch_handle) {
658 bd = backlight_device_register(ASUS_LAPTOP_FILE, dev,
659 asus, &asusbl_ops);
660 if (IS_ERR(bd)) {
661 pr_err("Could not register asus backlight device\n");
662 asus->backlight_device = NULL;
663 return PTR_ERR(bd);
664 }
665
666 asus->backlight_device = bd;
667
668 bd->props.max_brightness = 15;
669 bd->props.brightness = read_brightness(NULL);
670 bd->props.power = FB_BLANK_UNBLANK;
671 backlight_update_status(bd);
672 }
673 return 0;
674}
675
676static void asus_backlight_exit(struct asus_laptop *asus)
677{
678 if (asus->backlight_device)
679 backlight_device_unregister(asus->backlight_device);
680}
681
591/* 682/*
592 * Platform device handlers 683 * Platform device handlers
593 */ 684 */
@@ -904,7 +995,7 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
904} 995}
905 996
906/* 997/*
907 * Hotkey functions 998 * Input device (i.e. hotkeys)
908 */ 999 */
909static struct key_entry *asus_get_entry_by_scancode(struct asus_laptop *asus, 1000static struct key_entry *asus_get_entry_by_scancode(struct asus_laptop *asus,
910 int code) 1001 int code)
@@ -965,10 +1056,72 @@ static int asus_setkeycode(struct input_dev *dev, int scancode, int keycode)
965 return -EINVAL; 1056 return -EINVAL;
966} 1057}
967 1058
1059static void asus_input_notify(struct asus_laptop *asus, int event)
1060{
1061 struct key_entry *key;
1062
1063 key = asus_get_entry_by_scancode(asus, event);
1064 if (!key)
1065 return ;
1066
1067 switch (key->type) {
1068 case KE_KEY:
1069 input_report_key(asus->inputdev, key->keycode, 1);
1070 input_sync(asus->inputdev);
1071 input_report_key(asus->inputdev, key->keycode, 0);
1072 input_sync(asus->inputdev);
1073 break;
1074 }
1075}
1076
1077static int asus_input_init(struct asus_laptop *asus)
1078{
1079 const struct key_entry *key;
1080 int result;
1081
1082 asus->inputdev = input_allocate_device();
1083 if (!asus->inputdev) {
1084 pr_info("Unable to allocate input device\n");
1085 return 0;
1086 }
1087 asus->inputdev->name = "Asus Laptop extra buttons";
1088 asus->inputdev->dev.parent = &asus->platform_device->dev;
1089 asus->inputdev->phys = ASUS_LAPTOP_FILE "/input0";
1090 asus->inputdev->id.bustype = BUS_HOST;
1091 asus->inputdev->getkeycode = asus_getkeycode;
1092 asus->inputdev->setkeycode = asus_setkeycode;
1093 input_set_drvdata(asus->inputdev, asus);
1094
1095 asus->keymap = kmemdup(asus_keymap, sizeof(asus_keymap),
1096 GFP_KERNEL);
1097 for (key = asus->keymap; key->type != KE_END; key++) {
1098 switch (key->type) {
1099 case KE_KEY:
1100 set_bit(EV_KEY, asus->inputdev->evbit);
1101 set_bit(key->keycode, asus->inputdev->keybit);
1102 break;
1103 }
1104 }
1105 result = input_register_device(asus->inputdev);
1106 if (result) {
1107 pr_info("Unable to register input device\n");
1108 input_free_device(asus->inputdev);
1109 }
1110 return result;
1111}
1112
1113static void asus_input_exit(struct asus_laptop *asus)
1114{
1115 if (asus->inputdev)
1116 input_unregister_device(asus->inputdev);
1117}
1118
1119/*
1120 * ACPI driver
1121 */
968static void asus_acpi_notify(struct acpi_device *device, u32 event) 1122static void asus_acpi_notify(struct acpi_device *device, u32 event)
969{ 1123{
970 struct asus_laptop *asus = acpi_driver_data(device); 1124 struct asus_laptop *asus = acpi_driver_data(device);
971 static struct key_entry *key;
972 u16 count; 1125 u16 count;
973 1126
974 /* 1127 /*
@@ -990,20 +1143,7 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)
990 dev_name(&asus->device->dev), event, 1143 dev_name(&asus->device->dev), event,
991 count); 1144 count);
992 1145
993 if (asus->inputdev) { 1146 asus_input_notify(asus, event);
994 key = asus_get_entry_by_scancode(asus, event);
995 if (!key)
996 return ;
997
998 switch (key->type) {
999 case KE_KEY:
1000 input_report_key(asus->inputdev, key->keycode, 1);
1001 input_sync(asus->inputdev);
1002 input_report_key(asus->inputdev, key->keycode, 0);
1003 input_sync(asus->inputdev);
1004 break;
1005 }
1006 }
1007} 1147}
1008 1148
1009#define ASUS_CREATE_DEVICE_ATTR(_name) \ 1149#define ASUS_CREATE_DEVICE_ATTR(_name) \
@@ -1257,142 +1397,6 @@ static int asus_laptop_get_info(struct asus_laptop *asus)
1257 return AE_OK; 1397 return AE_OK;
1258} 1398}
1259 1399
1260static int asus_input_init(struct asus_laptop *asus)
1261{
1262 const struct key_entry *key;
1263 int result;
1264
1265 asus->inputdev = input_allocate_device();
1266 if (!asus->inputdev) {
1267 pr_info("Unable to allocate input device\n");
1268 return 0;
1269 }
1270 asus->inputdev->name = "Asus Laptop extra buttons";
1271 asus->inputdev->dev.parent = &asus->platform_device->dev;
1272 asus->inputdev->phys = ASUS_LAPTOP_FILE "/input0";
1273 asus->inputdev->id.bustype = BUS_HOST;
1274 asus->inputdev->getkeycode = asus_getkeycode;
1275 asus->inputdev->setkeycode = asus_setkeycode;
1276 input_set_drvdata(asus->inputdev, asus);
1277
1278 asus->keymap = kmemdup(asus_keymap, sizeof(asus_keymap),
1279 GFP_KERNEL);
1280 for (key = asus->keymap; key->type != KE_END; key++) {
1281 switch (key->type) {
1282 case KE_KEY:
1283 set_bit(EV_KEY, asus->inputdev->evbit);
1284 set_bit(key->keycode, asus->inputdev->keybit);
1285 break;
1286 }
1287 }
1288 result = input_register_device(asus->inputdev);
1289 if (result) {
1290 pr_info("Unable to register input device\n");
1291 input_free_device(asus->inputdev);
1292 }
1293 return result;
1294}
1295
1296static void asus_backlight_exit(struct asus_laptop *asus)
1297{
1298 if (asus->backlight_device)
1299 backlight_device_unregister(asus->backlight_device);
1300}
1301
1302#define ASUS_LED_UNREGISTER(object) \
1303 if (object##_led.dev) \
1304 led_classdev_unregister(&object##_led)
1305
1306static void asus_led_exit(struct asus_laptop *asus)
1307{
1308 ASUS_LED_UNREGISTER(mled);
1309 ASUS_LED_UNREGISTER(tled);
1310 ASUS_LED_UNREGISTER(pled);
1311 ASUS_LED_UNREGISTER(rled);
1312 ASUS_LED_UNREGISTER(gled);
1313 ASUS_LED_UNREGISTER(kled);
1314 if (asus->leds.workqueue) {
1315 destroy_workqueue(asus->leds.workqueue);
1316 asus->leds.workqueue = NULL;
1317 }
1318}
1319
1320static void asus_input_exit(struct asus_laptop *asus)
1321{
1322 if (asus->inputdev)
1323 input_unregister_device(asus->inputdev);
1324}
1325
1326static int asus_backlight_init(struct asus_laptop *asus)
1327{
1328 struct backlight_device *bd;
1329 struct device *dev = &asus->platform_device->dev;
1330
1331 if (brightness_set_handle && lcd_switch_handle) {
1332 bd = backlight_device_register(ASUS_LAPTOP_FILE, dev,
1333 asus, &asusbl_ops);
1334 if (IS_ERR(bd)) {
1335 pr_err("Could not register asus backlight device\n");
1336 asus->backlight_device = NULL;
1337 return PTR_ERR(bd);
1338 }
1339
1340 asus->backlight_device = bd;
1341
1342 bd->props.max_brightness = 15;
1343 bd->props.brightness = read_brightness(NULL);
1344 bd->props.power = FB_BLANK_UNBLANK;
1345 backlight_update_status(bd);
1346 }
1347 return 0;
1348}
1349
1350/*
1351 * Ugly macro, need to fix that later
1352 */
1353#define ASUS_LED_REGISTER(asus, object, _name, max) \
1354 do { \
1355 struct led_classdev *ldev = &asus->leds.object; \
1356 if (!object##_set_handle) \
1357 break ; \
1358 \
1359 INIT_WORK(&asus->leds.object##_work, object##_led_update); \
1360 ldev->name = "asus::" _name; \
1361 ldev->brightness_set = object##_led_set; \
1362 ldev->max_brightness = max; \
1363 rv = led_classdev_register(&asus->platform_device->dev, ldev); \
1364 if (rv) \
1365 goto error; \
1366 } while (0)
1367
1368static int asus_led_init(struct asus_laptop *asus)
1369{
1370 int rv;
1371
1372 /*
1373 * Functions that actually update the LED's are called from a
1374 * workqueue. By doing this as separate work rather than when the LED
1375 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
1376 * potentially bad time, such as a timer interrupt.
1377 */
1378 asus->leds.workqueue = create_singlethread_workqueue("led_workqueue");
1379 if (!asus->leds.workqueue)
1380 return -ENOMEM;
1381
1382 ASUS_LED_REGISTER(asus, mled, "mail", 1);
1383 ASUS_LED_REGISTER(asus, tled, "touchpad", 1);
1384 ASUS_LED_REGISTER(asus, rled, "record", 1);
1385 ASUS_LED_REGISTER(asus, pled, "phone", 1);
1386 ASUS_LED_REGISTER(asus, gled, "gaming", 1);
1387 if (kled_set_handle && kled_get_handle)
1388 ASUS_LED_REGISTER(asus, kled, "kbd_backlight", 3);
1389error:
1390 if (rv)
1391 asus_led_exit(asus);
1392 return rv;
1393}
1394
1395
1396static bool asus_device_present; 1400static bool asus_device_present;
1397 1401
1398static int __devinit asus_acpi_init(struct asus_laptop *asus) 1402static int __devinit asus_acpi_init(struct asus_laptop *asus)
@@ -1414,8 +1418,10 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)
1414 asus_laptop_add_fs(asus); 1418 asus_laptop_add_fs(asus);
1415 1419
1416 /* WLED and BLED are on by default */ 1420 /* WLED and BLED are on by default */
1417 write_status(asus, bt_switch_handle, 1, BT_ON); 1421 if (bluetooth_status >= 0)
1418 write_status(asus, wl_switch_handle, 1, WL_ON); 1422 write_status(asus, bt_switch_handle, !!bluetooth_status, BT_ON);
1423 if (wireless_status >= 0)
1424 write_status(asus, wl_switch_handle, !!wireless_status, WL_ON);
1419 1425
1420 /* If the h/w switch is off, we need to check the real status */ 1426 /* If the h/w switch is off, we need to check the real status */
1421 write_status(asus, NULL, read_status(asus, BT_ON), BT_ON); 1427 write_status(asus, NULL, read_status(asus, BT_ON), BT_ON);
@@ -1432,8 +1438,8 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)
1432 asus->ledd_status = 0xFFF; 1438 asus->ledd_status = 0xFFF;
1433 1439
1434 /* Set initial values of light sensor and level */ 1440 /* Set initial values of light sensor and level */
1435 hotk->light_switch = 0; /* Default to light sensor disabled */ 1441 asus->light_switch = 0; /* Default to light sensor disabled */
1436 hotk->light_level = 5; /* level 5 for sensor sensitivity */ 1442 asus->light_level = 5; /* level 5 for sensor sensitivity */
1437 1443
1438 if (ls_switch_handle) 1444 if (ls_switch_handle)
1439 set_light_sens_switch(asus, asus->light_switch); 1445 set_light_sens_switch(asus, asus->light_switch);