|
@@ -138,8 +138,12 @@ MODULE_LICENSE("GPL");
|
|
|
#define HCI_WIRELESS_BT_PRESENT 0x0f
|
|
|
#define HCI_WIRELESS_BT_ATTACH 0x40
|
|
|
#define HCI_WIRELESS_BT_POWER 0x80
|
|
|
+#define SCI_KBD_MODE_MASK 0x1f
|
|
|
#define SCI_KBD_MODE_FNZ 0x1
|
|
|
#define SCI_KBD_MODE_AUTO 0x2
|
|
|
+#define SCI_KBD_MODE_ON 0x8
|
|
|
+#define SCI_KBD_MODE_OFF 0x10
|
|
|
+#define SCI_KBD_TIME_MAX 0x3c001a
|
|
|
|
|
|
struct toshiba_acpi_dev {
|
|
|
struct acpi_device *acpi_dev;
|
|
@@ -155,6 +159,7 @@ struct toshiba_acpi_dev {
|
|
|
int force_fan;
|
|
|
int last_key_event;
|
|
|
int key_event_valid;
|
|
|
+ int kbd_type;
|
|
|
int kbd_mode;
|
|
|
int kbd_time;
|
|
|
|
|
@@ -495,6 +500,42 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
|
|
|
}
|
|
|
|
|
|
/* KBD Illumination */
|
|
|
+static int toshiba_kbd_illum_available(struct toshiba_acpi_dev *dev)
|
|
|
+{
|
|
|
+ u32 in[HCI_WORDS] = { SCI_GET, SCI_KBD_ILLUM_STATUS, 0, 0, 0, 0 };
|
|
|
+ u32 out[HCI_WORDS];
|
|
|
+ acpi_status status;
|
|
|
+
|
|
|
+ if (!sci_open(dev))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ status = hci_raw(dev, in, out);
|
|
|
+ sci_close(dev);
|
|
|
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
|
|
|
+ pr_err("ACPI call to query kbd illumination support failed\n");
|
|
|
+ return 0;
|
|
|
+ } else if (out[0] == HCI_NOT_SUPPORTED) {
|
|
|
+ pr_info("Keyboard illumination not available\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check for keyboard backlight timeout max value,
|
|
|
+ * previous kbd backlight implementation set this to
|
|
|
+ * 0x3c0003, and now the new implementation set this
|
|
|
+ * to 0x3c001a, use this to distinguish between them
|
|
|
+ */
|
|
|
+ if (out[3] == SCI_KBD_TIME_MAX)
|
|
|
+ dev->kbd_type = 2;
|
|
|
+ else
|
|
|
+ dev->kbd_type = 1;
|
|
|
+ /* Get the current keyboard backlight mode */
|
|
|
+ dev->kbd_mode = out[2] & SCI_KBD_MODE_MASK;
|
|
|
+ /* Get the current time (1-60 seconds) */
|
|
|
+ dev->kbd_time = out[2] >> HCI_MISC_SHIFT;
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time)
|
|
|
{
|
|
|
u32 result;
|
|
@@ -1254,6 +1295,62 @@ static const struct backlight_ops toshiba_backlight_data = {
|
|
|
/*
|
|
|
* Sysfs files
|
|
|
*/
|
|
|
+static ssize_t toshiba_kbd_bl_mode_store(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ const char *buf, size_t count);
|
|
|
+static ssize_t toshiba_kbd_bl_mode_show(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ char *buf);
|
|
|
+static ssize_t toshiba_kbd_type_show(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ char *buf);
|
|
|
+static ssize_t toshiba_available_kbd_modes_show(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ char *buf);
|
|
|
+static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ const char *buf, size_t count);
|
|
|
+static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ char *buf);
|
|
|
+static ssize_t toshiba_touchpad_store(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ const char *buf, size_t count);
|
|
|
+static ssize_t toshiba_touchpad_show(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ char *buf);
|
|
|
+static ssize_t toshiba_position_show(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ char *buf);
|
|
|
+
|
|
|
+static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR,
|
|
|
+ toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store);
|
|
|
+static DEVICE_ATTR(kbd_type, S_IRUGO, toshiba_kbd_type_show, NULL);
|
|
|
+static DEVICE_ATTR(available_kbd_modes, S_IRUGO,
|
|
|
+ toshiba_available_kbd_modes_show, NULL);
|
|
|
+static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR,
|
|
|
+ toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store);
|
|
|
+static DEVICE_ATTR(touchpad, S_IRUGO | S_IWUSR,
|
|
|
+ toshiba_touchpad_show, toshiba_touchpad_store);
|
|
|
+static DEVICE_ATTR(position, S_IRUGO, toshiba_position_show, NULL);
|
|
|
+
|
|
|
+static struct attribute *toshiba_attributes[] = {
|
|
|
+ &dev_attr_kbd_backlight_mode.attr,
|
|
|
+ &dev_attr_kbd_type.attr,
|
|
|
+ &dev_attr_available_kbd_modes.attr,
|
|
|
+ &dev_attr_kbd_backlight_timeout.attr,
|
|
|
+ &dev_attr_touchpad.attr,
|
|
|
+ &dev_attr_position.attr,
|
|
|
+ NULL,
|
|
|
+};
|
|
|
+
|
|
|
+static umode_t toshiba_sysfs_is_visible(struct kobject *,
|
|
|
+ struct attribute *, int);
|
|
|
+
|
|
|
+static struct attribute_group toshiba_attr_group = {
|
|
|
+ .is_visible = toshiba_sysfs_is_visible,
|
|
|
+ .attrs = toshiba_attributes,
|
|
|
+};
|
|
|
|
|
|
static ssize_t toshiba_kbd_bl_mode_store(struct device *dev,
|
|
|
struct device_attribute *attr,
|
|
@@ -1268,20 +1365,50 @@ static ssize_t toshiba_kbd_bl_mode_store(struct device *dev,
|
|
|
ret = kstrtoint(buf, 0, &mode);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
- if (mode != SCI_KBD_MODE_FNZ && mode != SCI_KBD_MODE_AUTO)
|
|
|
- return -EINVAL;
|
|
|
+
|
|
|
+ /* Check for supported modes depending on keyboard backlight type */
|
|
|
+ if (toshiba->kbd_type == 1) {
|
|
|
+ /* Type 1 supports SCI_KBD_MODE_FNZ and SCI_KBD_MODE_AUTO */
|
|
|
+ if (mode != SCI_KBD_MODE_FNZ && mode != SCI_KBD_MODE_AUTO)
|
|
|
+ return -EINVAL;
|
|
|
+ } else if (toshiba->kbd_type == 2) {
|
|
|
+ /* Type 2 doesn't support SCI_KBD_MODE_FNZ */
|
|
|
+ if (mode != SCI_KBD_MODE_AUTO && mode != SCI_KBD_MODE_ON &&
|
|
|
+ mode != SCI_KBD_MODE_OFF)
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
|
|
|
/* Set the Keyboard Backlight Mode where:
|
|
|
- * Mode - Auto (2) | FN-Z (1)
|
|
|
* Auto - KBD backlight turns off automatically in given time
|
|
|
* FN-Z - KBD backlight "toggles" when hotkey pressed
|
|
|
+ * ON - KBD backlight is always on
|
|
|
+ * OFF - KBD backlight is always off
|
|
|
*/
|
|
|
+
|
|
|
+ /* Only make a change if the actual mode has changed */
|
|
|
if (toshiba->kbd_mode != mode) {
|
|
|
+ /* Shift the time to "base time" (0x3c0000 == 60 seconds) */
|
|
|
time = toshiba->kbd_time << HCI_MISC_SHIFT;
|
|
|
- time = time + toshiba->kbd_mode;
|
|
|
+
|
|
|
+ /* OR the "base time" to the actual method format */
|
|
|
+ if (toshiba->kbd_type == 1) {
|
|
|
+ /* Type 1 requires the current mode */
|
|
|
+ time |= toshiba->kbd_mode;
|
|
|
+ } else if (toshiba->kbd_type == 2) {
|
|
|
+ /* Type 2 requires the desired mode */
|
|
|
+ time |= mode;
|
|
|
+ }
|
|
|
+
|
|
|
ret = toshiba_kbd_illum_status_set(toshiba, time);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
+
|
|
|
+ /* Update sysfs entries on successful mode change*/
|
|
|
+ ret = sysfs_update_group(&toshiba->acpi_dev->dev.kobj,
|
|
|
+ &toshiba_attr_group);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
toshiba->kbd_mode = mode;
|
|
|
}
|
|
|
|
|
@@ -1298,7 +1425,30 @@ static ssize_t toshiba_kbd_bl_mode_show(struct device *dev,
|
|
|
if (toshiba_kbd_illum_status_get(toshiba, &time) < 0)
|
|
|
return -EIO;
|
|
|
|
|
|
- return sprintf(buf, "%i\n", time & 0x07);
|
|
|
+ return sprintf(buf, "%i\n", time & SCI_KBD_MODE_MASK);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t toshiba_kbd_type_show(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ char *buf)
|
|
|
+{
|
|
|
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
|
|
|
+
|
|
|
+ return sprintf(buf, "%d\n", toshiba->kbd_type);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t toshiba_available_kbd_modes_show(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ char *buf)
|
|
|
+{
|
|
|
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
|
|
|
+
|
|
|
+ if (toshiba->kbd_type == 1)
|
|
|
+ return sprintf(buf, "%x %x\n",
|
|
|
+ SCI_KBD_MODE_FNZ, SCI_KBD_MODE_AUTO);
|
|
|
+
|
|
|
+ return sprintf(buf, "%x %x %x\n",
|
|
|
+ SCI_KBD_MODE_AUTO, SCI_KBD_MODE_ON, SCI_KBD_MODE_OFF);
|
|
|
}
|
|
|
|
|
|
static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev,
|
|
@@ -1394,22 +1544,6 @@ static ssize_t toshiba_position_show(struct device *dev,
|
|
|
return sprintf(buf, "%d %d %d\n", x, y, z);
|
|
|
}
|
|
|
|
|
|
-static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR,
|
|
|
- toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store);
|
|
|
-static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR,
|
|
|
- toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store);
|
|
|
-static DEVICE_ATTR(touchpad, S_IRUGO | S_IWUSR,
|
|
|
- toshiba_touchpad_show, toshiba_touchpad_store);
|
|
|
-static DEVICE_ATTR(position, S_IRUGO, toshiba_position_show, NULL);
|
|
|
-
|
|
|
-static struct attribute *toshiba_attributes[] = {
|
|
|
- &dev_attr_kbd_backlight_mode.attr,
|
|
|
- &dev_attr_kbd_backlight_timeout.attr,
|
|
|
- &dev_attr_touchpad.attr,
|
|
|
- &dev_attr_position.attr,
|
|
|
- NULL,
|
|
|
-};
|
|
|
-
|
|
|
static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
|
|
|
struct attribute *attr, int idx)
|
|
|
{
|
|
@@ -1429,11 +1563,6 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
|
|
|
return exists ? attr->mode : 0;
|
|
|
}
|
|
|
|
|
|
-static struct attribute_group toshiba_attr_group = {
|
|
|
- .is_visible = toshiba_sysfs_is_visible,
|
|
|
- .attrs = toshiba_attributes,
|
|
|
-};
|
|
|
-
|
|
|
static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
|
|
|
struct serio *port)
|
|
|
{
|
|
@@ -1765,12 +1894,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
|
|
|
dev->eco_supported = 1;
|
|
|
}
|
|
|
|
|
|
- ret = toshiba_kbd_illum_status_get(dev, &dummy);
|
|
|
- if (!ret) {
|
|
|
- dev->kbd_time = dummy >> HCI_MISC_SHIFT;
|
|
|
- dev->kbd_mode = dummy & 0x07;
|
|
|
- }
|
|
|
- dev->kbd_illum_supported = !ret;
|
|
|
+ dev->kbd_illum_supported = toshiba_kbd_illum_available(dev);
|
|
|
/*
|
|
|
* Only register the LED if KBD illumination is supported
|
|
|
* and the keyboard backlight operation mode is set to FN-Z
|