diff options
author | Azael Avalos <coproscefalo@gmail.com> | 2014-03-25 22:38:31 -0400 |
---|---|---|
committer | Matthew Garrett <matthew.garrett@nebula.com> | 2014-04-06 12:58:13 -0400 |
commit | 360f0f39d0c58432574655008ec8dd15e52e1e8d (patch) | |
tree | 192136b245ce3b33d8f40abb87b41de9d03c9db4 | |
parent | fdb79081fec4a57e124b34f983aac566e210220f (diff) |
toshiba_acpi: Add keyboard backlight support
Toshiba laptops equiped with an illuminated keyboard
can operate in two different modes: Auto and FN-Z.
The Auto mode turns on the led on keystrokes and
automatically turns it off after some (configurable)
time the last key was pressed.
The FN-Z mode is used to toggle the keyboard led on/off
by userspace.
This patch adds support to set the desired KBD mode and
timeout via sysfs, creates and registers toshiba::kbd_backlight
led device whenever the mode is set to FN-Z.
The acceptable values for mode are: 1 (Auto) and 2 (Fn-Z)
The time values range are: 1-60 seconds
Signed-off-by: Azael Avalos <coproscefalo@gmail.com>
Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com>
-rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 6ed5be030d58..95ae1ffaf409 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c | |||
@@ -111,7 +111,9 @@ MODULE_LICENSE("GPL"); | |||
111 | #define HCI_HOTKEY_EVENT 0x001e | 111 | #define HCI_HOTKEY_EVENT 0x001e |
112 | #define HCI_LCD_BRIGHTNESS 0x002a | 112 | #define HCI_LCD_BRIGHTNESS 0x002a |
113 | #define HCI_WIRELESS 0x0056 | 113 | #define HCI_WIRELESS 0x0056 |
114 | #define HCI_KBD_ILLUMINATION 0x0095 | ||
114 | #define SCI_ILLUMINATION 0x014e | 115 | #define SCI_ILLUMINATION 0x014e |
116 | #define SCI_KBD_ILLUM_STATUS 0x015c | ||
115 | 117 | ||
116 | /* field definitions */ | 118 | /* field definitions */ |
117 | #define HCI_HOTKEY_DISABLE 0x0b | 119 | #define HCI_HOTKEY_DISABLE 0x0b |
@@ -119,6 +121,7 @@ MODULE_LICENSE("GPL"); | |||
119 | #define HCI_LCD_BRIGHTNESS_BITS 3 | 121 | #define HCI_LCD_BRIGHTNESS_BITS 3 |
120 | #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) | 122 | #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) |
121 | #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) | 123 | #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) |
124 | #define HCI_MISC_SHIFT 0x10 | ||
122 | #define HCI_VIDEO_OUT_LCD 0x1 | 125 | #define HCI_VIDEO_OUT_LCD 0x1 |
123 | #define HCI_VIDEO_OUT_CRT 0x2 | 126 | #define HCI_VIDEO_OUT_CRT 0x2 |
124 | #define HCI_VIDEO_OUT_TV 0x4 | 127 | #define HCI_VIDEO_OUT_TV 0x4 |
@@ -126,6 +129,8 @@ MODULE_LICENSE("GPL"); | |||
126 | #define HCI_WIRELESS_BT_PRESENT 0x0f | 129 | #define HCI_WIRELESS_BT_PRESENT 0x0f |
127 | #define HCI_WIRELESS_BT_ATTACH 0x40 | 130 | #define HCI_WIRELESS_BT_ATTACH 0x40 |
128 | #define HCI_WIRELESS_BT_POWER 0x80 | 131 | #define HCI_WIRELESS_BT_POWER 0x80 |
132 | #define SCI_KBD_MODE_FNZ 0x1 | ||
133 | #define SCI_KBD_MODE_AUTO 0x2 | ||
129 | 134 | ||
130 | struct toshiba_acpi_dev { | 135 | struct toshiba_acpi_dev { |
131 | struct acpi_device *acpi_dev; | 136 | struct acpi_device *acpi_dev; |
@@ -135,10 +140,13 @@ struct toshiba_acpi_dev { | |||
135 | struct work_struct hotkey_work; | 140 | struct work_struct hotkey_work; |
136 | struct backlight_device *backlight_dev; | 141 | struct backlight_device *backlight_dev; |
137 | struct led_classdev led_dev; | 142 | struct led_classdev led_dev; |
143 | struct led_classdev kbd_led; | ||
138 | 144 | ||
139 | int force_fan; | 145 | int force_fan; |
140 | int last_key_event; | 146 | int last_key_event; |
141 | int key_event_valid; | 147 | int key_event_valid; |
148 | int kbd_mode; | ||
149 | int kbd_time; | ||
142 | 150 | ||
143 | unsigned int illumination_supported:1; | 151 | unsigned int illumination_supported:1; |
144 | unsigned int video_supported:1; | 152 | unsigned int video_supported:1; |
@@ -147,6 +155,9 @@ struct toshiba_acpi_dev { | |||
147 | unsigned int ntfy_supported:1; | 155 | unsigned int ntfy_supported:1; |
148 | unsigned int info_supported:1; | 156 | unsigned int info_supported:1; |
149 | unsigned int tr_backlight_supported:1; | 157 | unsigned int tr_backlight_supported:1; |
158 | unsigned int kbd_illum_supported:1; | ||
159 | unsigned int kbd_led_registered:1; | ||
160 | unsigned int sysfs_created:1; | ||
150 | 161 | ||
151 | struct mutex mutex; | 162 | struct mutex mutex; |
152 | }; | 163 | }; |
@@ -433,6 +444,89 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) | |||
433 | 444 | ||
434 | return state ? LED_FULL : LED_OFF; | 445 | return state ? LED_FULL : LED_OFF; |
435 | } | 446 | } |
447 | |||
448 | /* KBD Illumination */ | ||
449 | static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time) | ||
450 | { | ||
451 | u32 result; | ||
452 | acpi_status status; | ||
453 | |||
454 | if (!sci_open(dev)) | ||
455 | return -EIO; | ||
456 | |||
457 | status = sci_write(dev, SCI_KBD_ILLUM_STATUS, time, &result); | ||
458 | sci_close(dev); | ||
459 | if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { | ||
460 | pr_err("ACPI call to set KBD backlight status failed\n"); | ||
461 | return -EIO; | ||
462 | } else if (result == HCI_NOT_SUPPORTED) { | ||
463 | pr_info("Keyboard backlight status not supported\n"); | ||
464 | return -ENODEV; | ||
465 | } | ||
466 | |||
467 | return 0; | ||
468 | } | ||
469 | |||
470 | static int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time) | ||
471 | { | ||
472 | u32 result; | ||
473 | acpi_status status; | ||
474 | |||
475 | if (!sci_open(dev)) | ||
476 | return -EIO; | ||
477 | |||
478 | status = sci_read(dev, SCI_KBD_ILLUM_STATUS, time, &result); | ||
479 | sci_close(dev); | ||
480 | if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { | ||
481 | pr_err("ACPI call to get KBD backlight status failed\n"); | ||
482 | return -EIO; | ||
483 | } else if (result == HCI_NOT_SUPPORTED) { | ||
484 | pr_info("Keyboard backlight status not supported\n"); | ||
485 | return -ENODEV; | ||
486 | } | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | static enum led_brightness toshiba_kbd_backlight_get(struct led_classdev *cdev) | ||
492 | { | ||
493 | struct toshiba_acpi_dev *dev = container_of(cdev, | ||
494 | struct toshiba_acpi_dev, kbd_led); | ||
495 | u32 state, result; | ||
496 | acpi_status status; | ||
497 | |||
498 | /* Check the keyboard backlight state */ | ||
499 | status = hci_read1(dev, HCI_KBD_ILLUMINATION, &state, &result); | ||
500 | if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { | ||
501 | pr_err("ACPI call to get the keyboard backlight failed\n"); | ||
502 | return LED_OFF; | ||
503 | } else if (result == HCI_NOT_SUPPORTED) { | ||
504 | pr_info("Keyboard backlight not supported\n"); | ||
505 | return LED_OFF; | ||
506 | } | ||
507 | |||
508 | return state ? LED_FULL : LED_OFF; | ||
509 | } | ||
510 | |||
511 | static void toshiba_kbd_backlight_set(struct led_classdev *cdev, | ||
512 | enum led_brightness brightness) | ||
513 | { | ||
514 | struct toshiba_acpi_dev *dev = container_of(cdev, | ||
515 | struct toshiba_acpi_dev, kbd_led); | ||
516 | u32 state, result; | ||
517 | acpi_status status; | ||
518 | |||
519 | /* Set the keyboard backlight state */ | ||
520 | state = brightness ? 1 : 0; | ||
521 | status = hci_write1(dev, HCI_KBD_ILLUMINATION, state, &result); | ||
522 | if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { | ||
523 | pr_err("ACPI call to set KBD Illumination mode failed\n"); | ||
524 | return; | ||
525 | } else if (result == HCI_NOT_SUPPORTED) { | ||
526 | pr_info("Keyboard backlight not supported\n"); | ||
527 | return; | ||
528 | } | ||
529 | } | ||
436 | 530 | ||
437 | /* Bluetooth rfkill handlers */ | 531 | /* Bluetooth rfkill handlers */ |
438 | 532 | ||
@@ -956,6 +1050,117 @@ static const struct backlight_ops toshiba_backlight_data = { | |||
956 | .get_brightness = get_lcd_brightness, | 1050 | .get_brightness = get_lcd_brightness, |
957 | .update_status = set_lcd_status, | 1051 | .update_status = set_lcd_status, |
958 | }; | 1052 | }; |
1053 | |||
1054 | /* | ||
1055 | * Sysfs files | ||
1056 | */ | ||
1057 | |||
1058 | static ssize_t toshiba_kbd_bl_mode_store(struct device *dev, | ||
1059 | struct device_attribute *attr, | ||
1060 | const char *buf, size_t count) | ||
1061 | { | ||
1062 | struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); | ||
1063 | int mode = -1; | ||
1064 | int time = -1; | ||
1065 | |||
1066 | if (sscanf(buf, "%i", &mode) != 1 && (mode != 2 || mode != 1)) | ||
1067 | return -EINVAL; | ||
1068 | |||
1069 | /* Set the Keyboard Backlight Mode where: | ||
1070 | * Mode - Auto (2) | FN-Z (1) | ||
1071 | * Auto - KBD backlight turns off automatically in given time | ||
1072 | * FN-Z - KBD backlight "toggles" when hotkey pressed | ||
1073 | */ | ||
1074 | if (mode != -1 && toshiba->kbd_mode != mode) { | ||
1075 | time = toshiba->kbd_time << HCI_MISC_SHIFT; | ||
1076 | time = time + toshiba->kbd_mode; | ||
1077 | if (toshiba_kbd_illum_status_set(toshiba, time) < 0) | ||
1078 | return -EIO; | ||
1079 | toshiba->kbd_mode = mode; | ||
1080 | } | ||
1081 | |||
1082 | return count; | ||
1083 | } | ||
1084 | |||
1085 | static ssize_t toshiba_kbd_bl_mode_show(struct device *dev, | ||
1086 | struct device_attribute *attr, | ||
1087 | char *buf) | ||
1088 | { | ||
1089 | struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); | ||
1090 | u32 time; | ||
1091 | |||
1092 | if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) | ||
1093 | return -EIO; | ||
1094 | |||
1095 | return sprintf(buf, "%i\n", time & 0x07); | ||
1096 | } | ||
1097 | |||
1098 | static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev, | ||
1099 | struct device_attribute *attr, | ||
1100 | const char *buf, size_t count) | ||
1101 | { | ||
1102 | struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); | ||
1103 | int time = -1; | ||
1104 | |||
1105 | if (sscanf(buf, "%i", &time) != 1 && (time < 0 || time > 60)) | ||
1106 | return -EINVAL; | ||
1107 | |||
1108 | /* Set the Keyboard Backlight Timeout: 0-60 seconds */ | ||
1109 | if (time != -1 && toshiba->kbd_time != time) { | ||
1110 | time = time << HCI_MISC_SHIFT; | ||
1111 | time = (toshiba->kbd_mode == SCI_KBD_MODE_AUTO) ? | ||
1112 | time + 1 : time + 2; | ||
1113 | if (toshiba_kbd_illum_status_set(toshiba, time) < 0) | ||
1114 | return -EIO; | ||
1115 | toshiba->kbd_time = time >> HCI_MISC_SHIFT; | ||
1116 | } | ||
1117 | |||
1118 | return count; | ||
1119 | } | ||
1120 | |||
1121 | static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev, | ||
1122 | struct device_attribute *attr, | ||
1123 | char *buf) | ||
1124 | { | ||
1125 | struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); | ||
1126 | u32 time; | ||
1127 | |||
1128 | if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) | ||
1129 | return -EIO; | ||
1130 | |||
1131 | return sprintf(buf, "%i\n", time >> HCI_MISC_SHIFT); | ||
1132 | } | ||
1133 | |||
1134 | static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR, | ||
1135 | toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store); | ||
1136 | static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR, | ||
1137 | toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store); | ||
1138 | |||
1139 | static struct attribute *toshiba_attributes[] = { | ||
1140 | &dev_attr_kbd_backlight_mode.attr, | ||
1141 | &dev_attr_kbd_backlight_timeout.attr, | ||
1142 | NULL, | ||
1143 | }; | ||
1144 | |||
1145 | static umode_t toshiba_sysfs_is_visible(struct kobject *kobj, | ||
1146 | struct attribute *attr, int idx) | ||
1147 | { | ||
1148 | struct device *dev = container_of(kobj, struct device, kobj); | ||
1149 | struct toshiba_acpi_dev *drv = dev_get_drvdata(dev); | ||
1150 | bool exists = true; | ||
1151 | |||
1152 | if (attr == &dev_attr_kbd_backlight_mode.attr) | ||
1153 | exists = (drv->kbd_illum_supported) ? true : false; | ||
1154 | else if (attr == &dev_attr_kbd_backlight_timeout.attr) | ||
1155 | exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false; | ||
1156 | |||
1157 | return exists ? attr->mode : 0; | ||
1158 | } | ||
1159 | |||
1160 | static struct attribute_group toshiba_attr_group = { | ||
1161 | .is_visible = toshiba_sysfs_is_visible, | ||
1162 | .attrs = toshiba_attributes, | ||
1163 | }; | ||
959 | 1164 | ||
960 | static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, | 1165 | static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, |
961 | struct serio *port) | 1166 | struct serio *port) |
@@ -1158,6 +1363,10 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) | |||
1158 | struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); | 1363 | struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); |
1159 | 1364 | ||
1160 | remove_toshiba_proc_entries(dev); | 1365 | remove_toshiba_proc_entries(dev); |
1366 | |||
1367 | if (dev->sysfs_created) | ||
1368 | sysfs_remove_group(&dev->acpi_dev->dev.kobj, | ||
1369 | &toshiba_attr_group); | ||
1161 | 1370 | ||
1162 | if (dev->ntfy_supported) { | 1371 | if (dev->ntfy_supported) { |
1163 | i8042_remove_filter(toshiba_acpi_i8042_filter); | 1372 | i8042_remove_filter(toshiba_acpi_i8042_filter); |
@@ -1179,6 +1388,9 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) | |||
1179 | 1388 | ||
1180 | if (dev->illumination_supported) | 1389 | if (dev->illumination_supported) |
1181 | led_classdev_unregister(&dev->led_dev); | 1390 | led_classdev_unregister(&dev->led_dev); |
1391 | |||
1392 | if (dev->kbd_led_registered) | ||
1393 | led_classdev_unregister(&dev->kbd_led); | ||
1182 | 1394 | ||
1183 | if (toshiba_acpi) | 1395 | if (toshiba_acpi) |
1184 | toshiba_acpi = NULL; | 1396 | toshiba_acpi = NULL; |
@@ -1225,6 +1437,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) | |||
1225 | dev->acpi_dev = acpi_dev; | 1437 | dev->acpi_dev = acpi_dev; |
1226 | dev->method_hci = hci_method; | 1438 | dev->method_hci = hci_method; |
1227 | acpi_dev->driver_data = dev; | 1439 | acpi_dev->driver_data = dev; |
1440 | dev_set_drvdata(&acpi_dev->dev, dev); | ||
1228 | 1441 | ||
1229 | if (toshiba_acpi_setup_keyboard(dev)) | 1442 | if (toshiba_acpi_setup_keyboard(dev)) |
1230 | pr_info("Unable to activate hotkeys\n"); | 1443 | pr_info("Unable to activate hotkeys\n"); |
@@ -1264,6 +1477,25 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) | |||
1264 | if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev)) | 1477 | if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev)) |
1265 | dev->illumination_supported = 1; | 1478 | dev->illumination_supported = 1; |
1266 | } | 1479 | } |
1480 | |||
1481 | ret = toshiba_kbd_illum_status_get(dev, &dummy); | ||
1482 | if (!ret) { | ||
1483 | dev->kbd_time = dummy >> HCI_MISC_SHIFT; | ||
1484 | dev->kbd_mode = dummy & 0x07; | ||
1485 | } | ||
1486 | dev->kbd_illum_supported = !ret; | ||
1487 | /* | ||
1488 | * Only register the LED if KBD illumination is supported | ||
1489 | * and the keyboard backlight operation mode is set to FN-Z | ||
1490 | */ | ||
1491 | if (dev->kbd_illum_supported && dev->kbd_mode == SCI_KBD_MODE_FNZ) { | ||
1492 | dev->kbd_led.name = "toshiba::kbd_backlight"; | ||
1493 | dev->kbd_led.max_brightness = 1; | ||
1494 | dev->kbd_led.brightness_set = toshiba_kbd_backlight_set; | ||
1495 | dev->kbd_led.brightness_get = toshiba_kbd_backlight_get; | ||
1496 | if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led)) | ||
1497 | dev->kbd_led_registered = 1; | ||
1498 | } | ||
1267 | 1499 | ||
1268 | /* Determine whether or not BIOS supports fan and video interfaces */ | 1500 | /* Determine whether or not BIOS supports fan and video interfaces */ |
1269 | 1501 | ||
@@ -1273,6 +1505,14 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) | |||
1273 | ret = get_fan_status(dev, &dummy); | 1505 | ret = get_fan_status(dev, &dummy); |
1274 | dev->fan_supported = !ret; | 1506 | dev->fan_supported = !ret; |
1275 | 1507 | ||
1508 | ret = sysfs_create_group(&dev->acpi_dev->dev.kobj, | ||
1509 | &toshiba_attr_group); | ||
1510 | if (ret) { | ||
1511 | dev->sysfs_created = 0; | ||
1512 | goto error; | ||
1513 | } | ||
1514 | dev->sysfs_created = !ret; | ||
1515 | |||
1276 | create_toshiba_proc_entries(dev); | 1516 | create_toshiba_proc_entries(dev); |
1277 | 1517 | ||
1278 | toshiba_acpi = dev; | 1518 | toshiba_acpi = dev; |