aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAzael Avalos <coproscefalo@gmail.com>2014-03-25 22:38:31 -0400
committerMatthew Garrett <matthew.garrett@nebula.com>2014-04-06 12:58:13 -0400
commit360f0f39d0c58432574655008ec8dd15e52e1e8d (patch)
tree192136b245ce3b33d8f40abb87b41de9d03c9db4
parentfdb79081fec4a57e124b34f983aac566e210220f (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.c240
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
130struct toshiba_acpi_dev { 135struct 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 */
449static 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
470static 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
491static 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
511static 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
1058static 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
1085static 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
1098static 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
1121static 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
1134static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR,
1135 toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store);
1136static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR,
1137 toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store);
1138
1139static struct attribute *toshiba_attributes[] = {
1140 &dev_attr_kbd_backlight_mode.attr,
1141 &dev_attr_kbd_backlight_timeout.attr,
1142 NULL,
1143};
1144
1145static 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
1160static struct attribute_group toshiba_attr_group = {
1161 .is_visible = toshiba_sysfs_is_visible,
1162 .attrs = toshiba_attributes,
1163};
959 1164
960static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, 1165static 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;