summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/char/Kconfig2
-rw-r--r--drivers/platform/x86/Kconfig22
-rw-r--r--drivers/platform/x86/Makefile2
-rw-r--r--drivers/platform/x86/alienware-wmi.c565
-rw-r--r--drivers/platform/x86/fujitsu-tablet.c65
-rw-r--r--drivers/platform/x86/intel_baytrail.c224
-rw-r--r--drivers/platform/x86/intel_baytrail.h90
-rw-r--r--drivers/platform/x86/panasonic-laptop.c11
-rw-r--r--drivers/platform/x86/sony-laptop.c539
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c227
-rw-r--r--drivers/platform/x86/toshiba_acpi.c635
11 files changed, 1901 insertions, 481 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 1386749b48ff..fbae63e3d304 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -408,7 +408,7 @@ config APPLICOM
config SONYPI
tristate "Sony Vaio Programmable I/O Control Device support"
- depends on X86 && PCI && INPUT && !64BIT
+ depends on X86_32 && PCI && INPUT
---help---
This driver enables access to the Sony Programmable I/O Control
Device which can be found in many (all ?) Sony Vaio laptops.
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 5f67843c7fb7..27df2c533b09 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -53,6 +53,18 @@ config ACERHDF
If you have an Acer Aspire One netbook, say Y or M
here.
+config ALIENWARE_WMI
+ tristate "Alienware Special feature control"
+ depends on ACPI
+ depends on LEDS_CLASS
+ depends on NEW_LEDS
+ depends on ACPI_WMI
+ ---help---
+ This is a driver for controlling Alienware BIOS driven
+ features. It exposes an interface for controlling the AlienFX
+ zones on Alienware machines that don't contain a dedicated AlienFX
+ USB MCU such as the X51 and X51-R2.
+
config ASUS_LAPTOP
tristate "Asus Laptop Extras"
depends on ACPI
@@ -196,7 +208,7 @@ config HP_ACCEL
be called hp_accel.
config HP_WIRELESS
- tristate "HP WIRELESS"
+ tristate "HP wireless button"
depends on ACPI
depends on INPUT
help
@@ -817,12 +829,4 @@ config PVPANIC
a paravirtualized device provided by QEMU; it lets a virtual machine
(guest) communicate panic events to the host.
-config INTEL_BAYTRAIL_MBI
- tristate
- depends on PCI
- ---help---
- Needed on Baytrail platforms for access to the IOSF Sideband Mailbox
- Interface. This is a requirement for systems that need to configure
- the PUNIT for power management features such as RAPL.
-
endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 9b87cfc42b84..1a2eafc9d48e 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -55,4 +55,4 @@ obj-$(CONFIG_INTEL_RST) += intel-rst.o
obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
-obj-$(CONFIG_INTEL_BAYTRAIL_MBI) += intel_baytrail.o
+obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c
new file mode 100644
index 000000000000..541f9514f76f
--- /dev/null
+++ b/drivers/platform/x86/alienware-wmi.c
@@ -0,0 +1,565 @@
+/*
+ * Alienware AlienFX control
+ *
+ * Copyright (C) 2014 Dell Inc <mario_limonciello@dell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include <linux/leds.h>
+
+#define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492"
+#define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
+#define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492"
+
+#define WMAX_METHOD_HDMI_SOURCE 0x1
+#define WMAX_METHOD_HDMI_STATUS 0x2
+#define WMAX_METHOD_BRIGHTNESS 0x3
+#define WMAX_METHOD_ZONE_CONTROL 0x4
+
+MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>");
+MODULE_DESCRIPTION("Alienware special feature control");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID);
+MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID);
+
+enum INTERFACE_FLAGS {
+ LEGACY,
+ WMAX,
+};
+
+enum LEGACY_CONTROL_STATES {
+ LEGACY_RUNNING = 1,
+ LEGACY_BOOTING = 0,
+ LEGACY_SUSPEND = 3,
+};
+
+enum WMAX_CONTROL_STATES {
+ WMAX_RUNNING = 0xFF,
+ WMAX_BOOTING = 0,
+ WMAX_SUSPEND = 3,
+};
+
+struct quirk_entry {
+ u8 num_zones;
+};
+
+static struct quirk_entry *quirks;
+
+static struct quirk_entry quirk_unknown = {
+ .num_zones = 2,
+};
+
+static struct quirk_entry quirk_x51_family = {
+ .num_zones = 3,
+};
+
+static int dmi_matched(const struct dmi_system_id *dmi)
+{
+ quirks = dmi->driver_data;
+ return 1;
+}
+
+static struct dmi_system_id alienware_quirks[] = {
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
+ },
+ .driver_data = &quirk_x51_family,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
+ },
+ .driver_data = &quirk_x51_family,
+ },
+ {}
+};
+
+struct color_platform {
+ u8 blue;
+ u8 green;
+ u8 red;
+} __packed;
+
+struct platform_zone {
+ u8 location;
+ struct device_attribute *attr;
+ struct color_platform colors;
+};
+
+struct wmax_brightness_args {
+ u32 led_mask;
+ u32 percentage;
+};
+
+struct hdmi_args {
+ u8 arg;
+};
+
+struct legacy_led_args {
+ struct color_platform colors;
+ u8 brightness;
+ u8 state;
+} __packed;
+
+struct wmax_led_args {
+ u32 led_mask;
+ struct color_platform colors;
+ u8 state;
+} __packed;
+
+static struct platform_device *platform_device;
+static struct device_attribute *zone_dev_attrs;
+static struct attribute **zone_attrs;
+static struct platform_zone *zone_data;
+
+static struct platform_driver platform_driver = {
+ .driver = {
+ .name = "alienware-wmi",
+ .owner = THIS_MODULE,
+ }
+};
+
+static struct attribute_group zone_attribute_group = {
+ .name = "rgb_zones",
+};
+
+static u8 interface;
+static u8 lighting_control_state;
+static u8 global_brightness;
+
+/*
+ * Helpers used for zone control
+*/
+static int parse_rgb(const char *buf, struct platform_zone *zone)
+{
+ long unsigned int rgb;
+ int ret;
+ union color_union {
+ struct color_platform cp;
+ int package;
+ } repackager;
+
+ ret = kstrtoul(buf, 16, &rgb);
+ if (ret)
+ return ret;
+
+ /* RGB triplet notation is 24-bit hexadecimal */
+ if (rgb > 0xFFFFFF)
+ return -EINVAL;
+
+ repackager.package = rgb & 0x0f0f0f0f;
+ pr_debug("alienware-wmi: r: %d g:%d b: %d\n",
+ repackager.cp.red, repackager.cp.green, repackager.cp.blue);
+ zone->colors = repackager.cp;
+ return 0;
+}
+
+static struct platform_zone *match_zone(struct device_attribute *attr)
+{
+ int i;
+ for (i = 0; i < quirks->num_zones; i++) {
+ if ((struct device_attribute *)zone_data[i].attr == attr) {
+ pr_debug("alienware-wmi: matched zone location: %d\n",
+ zone_data[i].location);
+ return &zone_data[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Individual RGB zone control
+*/
+static int alienware_update_led(struct platform_zone *zone)
+{
+ int method_id;
+ acpi_status status;
+ char *guid;
+ struct acpi_buffer input;
+ struct legacy_led_args legacy_args;
+ struct wmax_led_args wmax_args;
+ if (interface == WMAX) {
+ wmax_args.led_mask = 1 << zone->location;
+ wmax_args.colors = zone->colors;
+ wmax_args.state = lighting_control_state;
+ guid = WMAX_CONTROL_GUID;
+ method_id = WMAX_METHOD_ZONE_CONTROL;
+
+ input.length = (acpi_size) sizeof(wmax_args);
+ input.pointer = &wmax_args;
+ } else {
+ legacy_args.colors = zone->colors;
+ legacy_args.brightness = global_brightness;
+ legacy_args.state = 0;
+ if (lighting_control_state == LEGACY_BOOTING ||
+ lighting_control_state == LEGACY_SUSPEND) {
+ guid = LEGACY_POWER_CONTROL_GUID;
+ legacy_args.state = lighting_control_state;
+ } else
+ guid = LEGACY_CONTROL_GUID;
+ method_id = zone->location + 1;
+
+ input.length = (acpi_size) sizeof(legacy_args);
+ input.pointer = &legacy_args;
+ }
+ pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
+
+ status = wmi_evaluate_method(guid, 1, method_id, &input, NULL);
+ if (ACPI_FAILURE(status))
+ pr_err("alienware-wmi: zone set failure: %u\n", status);
+ return ACPI_FAILURE(status);
+}
+
+static ssize_t zone_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_zone *target_zone;
+ target_zone = match_zone(attr);
+ if (target_zone == NULL)
+ return sprintf(buf, "red: -1, green: -1, blue: -1\n");
+ return sprintf(buf, "red: %d, green: %d, blue: %d\n",
+ target_zone->colors.red,
+ target_zone->colors.green, target_zone->colors.blue);
+
+}
+
+static ssize_t zone_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_zone *target_zone;
+ int ret;
+ target_zone = match_zone(attr);
+ if (target_zone == NULL) {
+ pr_err("alienware-wmi: invalid target zone\n");
+ return 1;
+ }
+ ret = parse_rgb(buf, target_zone);
+ if (ret)
+ return ret;
+ ret = alienware_update_led(target_zone);
+ return ret ? ret : count;
+}
+
+/*
+ * LED Brightness (Global)
+*/
+static int wmax_brightness(int brightness)
+{
+ acpi_status status;
+ struct acpi_buffer input;
+ struct wmax_brightness_args args = {
+ .led_mask = 0xFF,
+ .percentage = brightness,
+ };
+ input.length = (acpi_size) sizeof(args);
+ input.pointer = &args;
+ status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
+ WMAX_METHOD_BRIGHTNESS, &input, NULL);
+ if (ACPI_FAILURE(status))
+ pr_err("alienware-wmi: brightness set failure: %u\n", status);
+ return ACPI_FAILURE(status);
+}
+
+static void global_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ int ret;
+ global_brightness = brightness;
+ if (interface == WMAX)
+ ret = wmax_brightness(brightness);
+ else
+ ret = alienware_update_led(&zone_data[0]);
+ if (ret)
+ pr_err("LED brightness update failed\n");
+}
+
+static enum led_brightness global_led_get(struct led_classdev *led_cdev)
+{
+ return global_brightness;
+}
+
+static struct led_classdev global_led = {
+ .brightness_set = global_led_set,
+ .brightness_get = global_led_get,
+ .name = "alienware::global_brightness",
+};
+
+/*
+ * Lighting control state device attribute (Global)
+*/
+static ssize_t show_control_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (lighting_control_state == LEGACY_BOOTING)
+ return scnprintf(buf, PAGE_SIZE, "[booting] running suspend\n");
+ else if (lighting_control_state == LEGACY_SUSPEND)
+ return scnprintf(buf, PAGE_SIZE, "booting running [suspend]\n");
+ return scnprintf(buf, PAGE_SIZE, "booting [running] suspend\n");
+}
+
+static ssize_t store_control_state(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ long unsigned int val;
+ if (strcmp(buf, "booting\n") == 0)
+ val = LEGACY_BOOTING;
+ else if (strcmp(buf, "suspend\n") == 0)
+ val = LEGACY_SUSPEND;
+ else if (interface == LEGACY)
+ val = LEGACY_RUNNING;
+ else
+ val = WMAX_RUNNING;
+ lighting_control_state = val;
+ pr_debug("alienware-wmi: updated control state to %d\n",
+ lighting_control_state);
+ return count;
+}
+
+static DEVICE_ATTR(lighting_control_state, 0644, show_control_state,
+ store_control_state);
+
+static int alienware_zone_init(struct platform_device *dev)
+{
+ int i;
+ char buffer[10];
+ char *name;
+
+ if (interface == WMAX) {
+ global_led.max_brightness = 100;
+ lighting_control_state = WMAX_RUNNING;
+ } else if (interface == LEGACY) {
+ global_led.max_brightness = 0x0F;
+ lighting_control_state = LEGACY_RUNNING;
+ }
+ global_brightness = global_led.max_brightness;
+
+ /*
+ * - zone_dev_attrs num_zones + 1 is for individual zones and then
+ * null terminated
+ * - zone_attrs num_zones + 2 is for all attrs in zone_dev_attrs +
+ * the lighting control + null terminated
+ * - zone_data num_zones is for the distinct zones
+ */
+ zone_dev_attrs =
+ kzalloc(sizeof(struct device_attribute) * (quirks->num_zones + 1),
+ GFP_KERNEL);
+ if (!zone_dev_attrs)
+ return -ENOMEM;
+
+ zone_attrs =
+ kzalloc(sizeof(struct attribute *) * (quirks->num_zones + 2),
+ GFP_KERNEL);
+ if (!zone_attrs)
+ return -ENOMEM;
+
+ zone_data =
+ kzalloc(sizeof(struct platform_zone) * (quirks->num_zones),
+ GFP_KERNEL);
+ if (!zone_data)
+ return -ENOMEM;
+
+ for (i = 0; i < quirks->num_zones; i++) {
+ sprintf(buffer, "zone%02X", i);
+ name = kstrdup(buffer, GFP_KERNEL);
+ if (name == NULL)
+ return 1;
+ sysfs_attr_init(&zone_dev_attrs[i].attr);
+ zone_dev_attrs[i].attr.name = name;
+ zone_dev_attrs[i].attr.mode = 0644;
+ zone_dev_attrs[i].show = zone_show;
+ zone_dev_attrs[i].store = zone_set;
+ zone_data[i].location = i;
+ zone_attrs[i] = &zone_dev_attrs[i].attr;
+ zone_data[i].attr = &zone_dev_attrs[i];
+ }
+ zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr;
+ zone_attribute_group.attrs = zone_attrs;
+
+ led_classdev_register(&dev->dev, &global_led);
+
+ return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group);
+}
+
+static void alienware_zone_exit(struct platform_device *dev)
+{
+ sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group);
+ led_classdev_unregister(&global_led);
+ if (zone_dev_attrs) {
+ int i;
+ for (i = 0; i < quirks->num_zones; i++)
+ kfree(zone_dev_attrs[i].attr.name);
+ }
+ kfree(zone_dev_attrs);
+ kfree(zone_data);
+ kfree(zone_attrs);
+}
+
+/*
+ The HDMI mux sysfs node indicates the status of the HDMI input mux.
+ It can toggle between standard system GPU output and HDMI input.
+*/
+static ssize_t show_hdmi(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ acpi_status status;
+ struct acpi_buffer input;
+ union acpi_object *obj;
+ u32 tmp = 0;
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct hdmi_args in_args = {
+ .arg = 0,
+ };
+ input.length = (acpi_size) sizeof(in_args);
+ input.pointer = &in_args;
+ status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
+ WMAX_METHOD_HDMI_STATUS, &input, &output);
+
+ if (ACPI_SUCCESS(status)) {
+ obj = (union acpi_object *)output.pointer;
+ if (obj && obj->type == ACPI_TYPE_INTEGER)
+ tmp = (u32) obj->integer.value;
+ if (tmp == 1)
+ return scnprintf(buf, PAGE_SIZE,
+ "[input] gpu unknown\n");
+ else if (tmp == 2)
+ return scnprintf(buf, PAGE_SIZE,
+ "input [gpu] unknown\n");
+ }
+ pr_err("alienware-wmi: unknown HDMI status: %d\n", status);
+ return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n");
+}
+
+static ssize_t toggle_hdmi(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_buffer input;
+ acpi_status status;
+ struct hdmi_args args;
+ if (strcmp(buf, "gpu\n") == 0)
+ args.arg = 1;
+ else if (strcmp(buf, "input\n") == 0)
+ args.arg = 2;
+ else
+ args.arg = 3;
+ pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
+ input.length = (acpi_size) sizeof(args);
+ input.pointer = &args;
+ status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
+ WMAX_METHOD_HDMI_SOURCE, &input, NULL);
+ if (ACPI_FAILURE(status))
+ pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
+ status);
+ return count;
+}
+
+static DEVICE_ATTR(hdmi, S_IRUGO | S_IWUSR, show_hdmi, toggle_hdmi);
+
+static void remove_hdmi(struct platform_device *device)
+{
+ device_remove_file(&device->dev, &dev_attr_hdmi);
+}
+
+static int create_hdmi(void)
+{
+ int ret = -ENOMEM;
+ ret = device_create_file(&platform_device->dev, &dev_attr_hdmi);
+ if (ret)
+ goto error_create_hdmi;
+ return 0;
+
+error_create_hdmi:
+ remove_hdmi(platform_device);
+ return ret;
+}
+
+static int __init alienware_wmi_init(void)
+{
+ int ret;
+
+ if (wmi_has_guid(LEGACY_CONTROL_GUID))
+ interface = LEGACY;
+ else if (wmi_has_guid(WMAX_CONTROL_GUID))
+ interface = WMAX;
+ else {
+ pr_warn("alienware-wmi: No known WMI GUID found\n");
+ return -ENODEV;
+ }
+
+ dmi_check_system(alienware_quirks);
+ if (quirks == NULL)
+ quirks = &quirk_unknown;
+
+ ret = platform_driver_register(&platform_driver);
+ if (ret)
+ goto fail_platform_driver;
+ platform_device = platform_device_alloc("alienware-wmi", -1);
+ if (!platform_device) {
+ ret = -ENOMEM;
+ goto fail_platform_device1;
+ }
+ ret = platform_device_add(platform_device);
+ if (ret)
+ goto fail_platform_device2;
+
+ if (interface == WMAX) {
+ ret = create_hdmi();
+ if (ret)
+ goto fail_prep_hdmi;
+ }
+
+ ret = alienware_zone_init(platform_device);
+ if (ret)
+ goto fail_prep_zones;
+
+ return 0;
+
+fail_prep_zones:
+ alienware_zone_exit(platform_device);
+fail_prep_hdmi:
+ platform_device_del(platform_device);
+fail_platform_device2:
+ platform_device_put(platform_device);
+fail_platform_device1:
+ platform_driver_unregister(&platform_driver);
+fail_platform_driver:
+ return ret;
+}
+
+module_init(alienware_wmi_init);
+
+static void __exit alienware_wmi_exit(void)
+{
+ if (platform_device) {
+ alienware_zone_exit(platform_device);
+ remove_hdmi(platform_device);
+ platform_device_unregister(platform_device);
+ platform_driver_unregister(&platform_driver);
+ }
+}
+
+module_exit(alienware_wmi_exit);
diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c
index 570926c10014..c3784baceae3 100644
--- a/drivers/platform/x86/fujitsu-tablet.c
+++ b/drivers/platform/x86/fujitsu-tablet.c
@@ -71,6 +71,44 @@ static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = {
KEY_LEFTALT
};
+static unsigned short keymap_Lifebook_T901[KEYMAP_LEN] __initdata = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_SCROLLDOWN,
+ KEY_SCROLLUP,
+ KEY_CYCLEWINDOWS,
+ KEY_LEFTCTRL,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_LEFTMETA
+};
+
+static unsigned short keymap_Lifebook_T902[KEYMAP_LEN] __initdata = {
+ KEY_RESERVED,
+ KEY_VOLUMEDOWN,
+ KEY_VOLUMEUP,
+ KEY_CYCLEWINDOWS,
+ KEY_PROG1,
+ KEY_PROG2,
+ KEY_LEFTMETA,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+};
+
static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = {
KEY_RESERVED,
KEY_RESERVED,
@@ -302,6 +340,33 @@ static int fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)
static const struct dmi_system_id dmi_ids[] __initconst = {
{
.callback = fujitsu_dmi_lifebook,
+ .ident = "Fujitsu Lifebook T901",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T901")
+ },
+ .driver_data = keymap_Lifebook_T901
+ },
+ {
+ .callback = fujitsu_dmi_lifebook,
+ .ident = "Fujitsu Lifebook T901",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T901")
+ },
+ .driver_data = keymap_Lifebook_T901
+ },
+ {
+ .callback = fujitsu_dmi_lifebook,
+ .ident = "Fujitsu Lifebook T902",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T902")
+ },
+ .driver_data = keymap_Lifebook_T902
+ },
+ {
+ .callback = fujitsu_dmi_lifebook,
.ident = "Fujitsu Siemens P/T Series",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
diff --git a/drivers/platform/x86/intel_baytrail.c b/drivers/platform/x86/intel_baytrail.c
deleted file mode 100644
index f96626b17260..000000000000
--- a/drivers/platform/x86/intel_baytrail.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Baytrail IOSF-SB MailBox Interface Driver
- * Copyright (c) 2013, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- *
- * The IOSF-SB is a fabric bus available on Atom based SOC's that uses a
- * mailbox interface (MBI) to communicate with mutiple devices. This
- * driver implements BayTrail-specific access to this interface.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/pci.h>
-
-#include "intel_baytrail.h"
-
-static DEFINE_SPINLOCK(iosf_mbi_lock);
-
-static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset)
-{
- return (op << 24) | (port << 16) | (offset << 8) | BT_MBI_ENABLE;
-}
-
-static struct pci_dev *mbi_pdev; /* one mbi device */
-
-/* Hold lock before calling */
-static int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr)
-{
- int result;
-
- if (!mbi_pdev)
- return -ENODEV;
-
- if (mcrx) {
- result = pci_write_config_dword(mbi_pdev,
- BT_MBI_MCRX_OFFSET, mcrx);
- if (result < 0)
- goto iosf_mbi_read_err;
- }
-
- result = pci_write_config_dword(mbi_pdev,
- BT_MBI_MCR_OFFSET, mcr);
- if (result < 0)
- goto iosf_mbi_read_err;
-
- result = pci_read_config_dword(mbi_pdev,
- BT_MBI_MDR_OFFSET, mdr);
- if (result < 0)
- goto iosf_mbi_read_err;
-
- return 0;
-
-iosf_mbi_read_err:
- dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n",
- result);
- return result;
-}
-
-/* Hold lock before calling */
-static int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr)
-{
- int result;
-
- if (!mbi_pdev)
- return -ENODEV;
-
- result = pci_write_config_dword(mbi_pdev,
- BT_MBI_MDR_OFFSET, mdr);
- if (result < 0)
- goto iosf_mbi_write_err;
-
- if (mcrx) {
- result = pci_write_config_dword(mbi_pdev,
- BT_MBI_MCRX_OFFSET, mcrx);
- if (result < 0)
- goto iosf_mbi_write_err;
- }
-
- result = pci_write_config_dword(mbi_pdev,
- BT_MBI_MCR_OFFSET, mcr);
- if (result < 0)
- goto iosf_mbi_write_err;
-
- return 0;
-
-iosf_mbi_write_err:
- dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n",
- result);
- return result;
-}
-
-int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr)
-{
- u32 mcr, mcrx;
- unsigned long flags;
- int ret;
-
- /*Access to the GFX unit is handled by GPU code */
- BUG_ON(port == BT_MBI_UNIT_GFX);
-
- mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
- mcrx = offset & BT_MBI_MASK_HI;
-
- spin_lock_irqsave(&iosf_mbi_lock, flags);
- ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr);
- spin_unlock_irqrestore(&iosf_mbi_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(bt_mbi_read);
-
-int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr)
-{
- u32 mcr, mcrx;
- unsigned long flags;
- int ret;
-
- /*Access to the GFX unit is handled by GPU code */
- BUG_ON(port == BT_MBI_UNIT_GFX);
-
- mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
- mcrx = offset & BT_MBI_MASK_HI;
-
- spin_lock_irqsave(&iosf_mbi_lock, flags);
- ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr);
- spin_unlock_irqrestore(&iosf_mbi_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(bt_mbi_write);
-
-int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask)
-{
- u32 mcr, mcrx;
- u32 value;
- unsigned long flags;
- int ret;
-
- /*Access to the GFX unit is handled by GPU code */
- BUG_ON(port == BT_MBI_UNIT_GFX);
-
- mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
- mcrx = offset & BT_MBI_MASK_HI;
-
- spin_lock_irqsave(&iosf_mbi_lock, flags);
-
- /* Read current mdr value */
- ret = iosf_mbi_pci_read_mdr(mcrx, mcr & BT_MBI_RD_MASK, &value);
- if (ret < 0) {
- spin_unlock_irqrestore(&iosf_mbi_lock, flags);
- return ret;
- }
-
- /* Apply mask */
- value &= ~mask;
- mdr &= mask;
- value |= mdr;
-
- /* Write back */
- ret = iosf_mbi_pci_write_mdr(mcrx, mcr | BT_MBI_WR_MASK, value);
-
- spin_unlock_irqrestore(&iosf_mbi_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(bt_mbi_modify);
-
-static int iosf_mbi_probe(struct pci_dev *pdev,
- const struct pci_device_id *unused)
-{
- int ret;
-
- ret = pci_enable_device(pdev);
- if (ret < 0) {
- dev_err(&pdev->dev, "error: could not enable device\n");
- return ret;
- }
-
- mbi_pdev = pci_dev_get(pdev);
- return 0;
-}
-
-static DEFINE_PCI_DEVICE_TABLE(iosf_mbi_pci_ids) = {
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F00) },
- { 0, },
-};
-MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids);
-
-static struct pci_driver iosf_mbi_pci_driver = {
- .name = "iosf_mbi_pci",
- .probe = iosf_mbi_probe,
- .id_table = iosf_mbi_pci_ids,
-};
-
-static int __init bt_mbi_init(void)
-{
- return pci_register_driver(&iosf_mbi_pci_driver);
-}
-
-static void __exit bt_mbi_exit(void)
-{
- pci_unregister_driver(&iosf_mbi_pci_driver);
- if (mbi_pdev) {
- pci_dev_put(mbi_pdev);
- mbi_pdev = NULL;
- }
-}
-
-module_init(bt_mbi_init);
-module_exit(bt_mbi_exit);
-
-MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
-MODULE_DESCRIPTION("BayTrail Mailbox Interface accessor");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/intel_baytrail.h b/drivers/platform/x86/intel_baytrail.h
deleted file mode 100644
index 8bcc311262e9..000000000000
--- a/drivers/platform/x86/intel_baytrail.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * intel_baytrail.h: MailBox access support for Intel BayTrail platforms
- */
-
-#ifndef INTEL_BAYTRAIL_MBI_SYMS_H
-#define INTEL_BAYTRAIL_MBI_SYMS_H
-
-#define BT_MBI_MCR_OFFSET 0xD0
-#define BT_MBI_MDR_OFFSET 0xD4
-#define BT_MBI_MCRX_OFFSET 0xD8
-
-#define BT_MBI_RD_MASK 0xFEFFFFFF
-#define BT_MBI_WR_MASK 0X01000000
-
-#define BT_MBI_MASK_HI 0xFFFFFF00
-#define BT_MBI_MASK_LO 0x000000FF
-#define BT_MBI_ENABLE 0xF0
-
-/* BT-SB unit access methods */
-#define BT_MBI_UNIT_AUNIT 0x00
-#define BT_MBI_UNIT_SMC 0x01
-#define BT_MBI_UNIT_CPU 0x02
-#define BT_MBI_UNIT_BUNIT 0x03
-#define BT_MBI_UNIT_PMC 0x04
-#define BT_MBI_UNIT_GFX 0x06
-#define BT_MBI_UNIT_SMI 0x0C
-#define BT_MBI_UNIT_USB 0x43
-#define BT_MBI_UNIT_SATA 0xA3
-#define BT_MBI_UNIT_PCIE 0xA6
-
-/* Read/write opcodes */
-#define BT_MBI_AUNIT_READ 0x10
-#define BT_MBI_AUNIT_WRITE 0x11
-#define BT_MBI_SMC_READ 0x10
-#define BT_MBI_SMC_WRITE 0x11
-#define BT_MBI_CPU_READ 0x10
-#define BT_MBI_CPU_WRITE 0x11
-#define BT_MBI_BUNIT_READ 0x10
-#define BT_MBI_BUNIT_WRITE 0x11
-#define BT_MBI_PMC_READ 0x06
-#define BT_MBI_PMC_WRITE 0x07
-#define BT_MBI_GFX_READ 0x00
-#define BT_MBI_GFX_WRITE 0x01
-#define BT_MBI_SMIO_READ 0x06
-#define BT_MBI_SMIO_WRITE 0x07
-#define BT_MBI_USB_READ 0x06
-#define BT_MBI_USB_WRITE 0x07
-#define BT_MBI_SATA_READ 0x00
-#define BT_MBI_SATA_WRITE 0x01
-#define BT_MBI_PCIE_READ 0x00
-#define BT_MBI_PCIE_WRITE 0x01
-
-/**
- * bt_mbi_read() - MailBox Interface read command
- * @port: port indicating subunit being accessed
- * @opcode: port specific read or write opcode
- * @offset: register address offset
- * @mdr: register data to be read
- *
- * Locking is handled by spinlock - cannot sleep.
- * Return: Nonzero on error
- */
-int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr);
-
-/**
- * bt_mbi_write() - MailBox unmasked write command
- * @port: port indicating subunit being accessed
- * @opcode: port specific read or write opcode
- * @offset: register address offset
- * @mdr: register data to be written
- *
- * Locking is handled by spinlock - cannot sleep.
- * Return: Nonzero on error
- */
-int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr);
-
-/**
- * bt_mbi_modify() - MailBox masked write command
- * @port: port indicating subunit being accessed
- * @opcode: port specific read or write opcode
- * @offset: register address offset
- * @mdr: register data being modified
- * @mask: mask indicating bits in mdr to be modified
- *
- * Locking is handled by spinlock - cannot sleep.
- * Return: Nonzero on error
- */
-int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask);
-
-#endif /* INTEL_BAYTRAIL_MBI_SYMS_H */
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index 609d38779b26..3f870972247c 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -449,6 +449,7 @@ static struct attribute_group pcc_attr_group = {
/* hotkey input device driver */
+static int sleep_keydown_seen;
static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
{
struct input_dev *hotk_input_dev = pcc->input_dev;
@@ -462,6 +463,16 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
"error getting hotkey status\n"));
return;
}
+
+ /* hack: some firmware sends no key down for sleep / hibernate */
+ if ((result & 0xf) == 0x7 || (result & 0xf) == 0xa) {
+ if (result & 0x80)
+ sleep_keydown_seen = 1;
+ if (!sleep_keydown_seen)
+ sparse_keymap_report_event(hotk_input_dev,
+ result & 0xf, 0x80, false);
+ }
+
if (!sparse_keymap_report_event(hotk_input_dev,
result & 0xf, result & 0x80, false))
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index 8f8551a63cc0..9c5a07417b2b 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -76,8 +76,6 @@ do { \
pr_warn(fmt, ##__VA_ARGS__); \
} while (0)
-#define SONY_LAPTOP_DRIVER_VERSION "0.6"
-
#define SONY_NC_CLASS "sony-nc"
#define SONY_NC_HID "SNY5001"
#define SONY_NC_DRIVER_NAME "Sony Notebook Control Driver"
@@ -89,7 +87,6 @@ do { \
MODULE_AUTHOR("Stelian Pop, Mattia Dongili");
MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)");
MODULE_LICENSE("GPL");
-MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION);
static int debug;
module_param(debug, int, 0);
@@ -129,7 +126,8 @@ static int kbd_backlight = -1;
module_param(kbd_backlight, int, 0444);
MODULE_PARM_DESC(kbd_backlight,
"set this to 0 to disable keyboard backlight, "
- "1 to enable it (default: no change from current value)");
+ "1 to enable it with automatic control and 2 to have it always "
+ "on (default: no change from current value)");
static int kbd_backlight_timeout = -1;
module_param(kbd_backlight_timeout, int, 0444);
@@ -152,7 +150,8 @@ static void sony_nc_battery_care_cleanup(struct platform_device *pd);
static int sony_nc_thermal_setup(struct platform_device *pd);
static void sony_nc_thermal_cleanup(struct platform_device *pd);
-static int sony_nc_lid_resume_setup(struct platform_device *pd);
+static int sony_nc_lid_resume_setup(struct platform_device *pd,
+ unsigned int handle);
static void sony_nc_lid_resume_cleanup(struct platform_device *pd);
static int sony_nc_gfx_switch_setup(struct platform_device *pd,
@@ -163,6 +162,21 @@ static int __sony_nc_gfx_switch_status_get(void);
static int sony_nc_highspeed_charging_setup(struct platform_device *pd);
static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd);
+static int sony_nc_lowbatt_setup(struct platform_device *pd);
+static void sony_nc_lowbatt_cleanup(struct platform_device *pd);
+
+static int sony_nc_fanspeed_setup(struct platform_device *pd);
+static void sony_nc_fanspeed_cleanup(struct platform_device *pd);
+
+static int sony_nc_usb_charge_setup(struct platform_device *pd);
+static void sony_nc_usb_charge_cleanup(struct platform_device *pd);
+
+static int sony_nc_panelid_setup(struct platform_device *pd);
+static void sony_nc_panelid_cleanup(struct platform_device *pd);
+
+static int sony_nc_smart_conn_setup(struct platform_device *pd);
+static void sony_nc_smart_conn_cleanup(struct platform_device *pd);
+
static int sony_nc_touchpad_setup(struct platform_device *pd,
unsigned int handle);
static void sony_nc_touchpad_cleanup(struct platform_device *pd);
@@ -1122,6 +1136,8 @@ static struct sony_nc_event sony_100_events[] = {
{ 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0xa6, SONYPI_EVENT_HELP_PRESSED },
{ 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED },
+ { 0xa8, SONYPI_EVENT_FNKEY_1 },
+ { 0x28, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0, 0 },
};
@@ -1339,7 +1355,8 @@ static void sony_nc_function_setup(struct acpi_device *device,
result);
break;
case 0x0119:
- result = sony_nc_lid_resume_setup(pf_device);
+ case 0x015D:
+ result = sony_nc_lid_resume_setup(pf_device, handle);
if (result)
pr_err("couldn't set up lid resume function (%d)\n",
result);
@@ -1381,6 +1398,36 @@ static void sony_nc_function_setup(struct acpi_device *device,
pr_err("couldn't set up keyboard backlight function (%d)\n",
result);
break;
+ case 0x0121:
+ result = sony_nc_lowbatt_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up low battery function (%d)\n",
+ result);
+ break;
+ case 0x0149:
+ result = sony_nc_fanspeed_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up fan speed function (%d)\n",
+ result);
+ break;
+ case 0x0155:
+ result = sony_nc_usb_charge_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up USB charge support (%d)\n",
+ result);
+ break;
+ case 0x011D:
+ result = sony_nc_panelid_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up panel ID function (%d)\n",
+ result);
+ break;
+ case 0x0168:
+ result = sony_nc_smart_conn_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up smart connect support (%d)\n",
+ result);
+ break;
default:
continue;
}
@@ -1420,6 +1467,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
sony_nc_battery_care_cleanup(pd);
break;
case 0x0119:
+ case 0x015D:
sony_nc_lid_resume_cleanup(pd);
break;
case 0x0122:
@@ -1444,6 +1492,21 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
case 0x0163:
sony_nc_kbd_backlight_cleanup(pd, handle);
break;
+ case 0x0121:
+ sony_nc_lowbatt_cleanup(pd);
+ break;
+ case 0x0149:
+ sony_nc_fanspeed_cleanup(pd);
+ break;
+ case 0x0155:
+ sony_nc_usb_charge_cleanup(pd);
+ break;
+ case 0x011D:
+ sony_nc_panelid_cleanup(pd);
+ break;
+ case 0x0168:
+ sony_nc_smart_conn_cleanup(pd);
+ break;
default:
continue;
}
@@ -1719,7 +1782,7 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)
{
int result;
- if (value > 1)
+ if (value > 2)
return -EINVAL;
if (sony_call_snc_handle(kbdbl_ctl->handle,
@@ -1727,8 +1790,10 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)
return -EIO;
/* Try to turn the light on/off immediately */
- sony_call_snc_handle(kbdbl_ctl->handle,
- (value << 0x10) | (kbdbl_ctl->base + 0x100), &result);
+ if (value != 1)
+ sony_call_snc_handle(kbdbl_ctl->handle,
+ (value << 0x0f) | (kbdbl_ctl->base + 0x100),
+ &result);
kbdbl_ctl->mode = value;
@@ -2221,9 +2286,14 @@ static void sony_nc_thermal_resume(void)
#endif
/* resume on LID open */
+#define LID_RESUME_S5 0
+#define LID_RESUME_S4 1
+#define LID_RESUME_S3 2
+#define LID_RESUME_MAX 3
struct snc_lid_resume_control {
- struct device_attribute attrs[3];
+ struct device_attribute attrs[LID_RESUME_MAX];
unsigned int status;
+ int handle;
};
static struct snc_lid_resume_control *lid_ctl;
@@ -2231,8 +2301,9 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{
- unsigned int result, pos;
+ unsigned int result;
unsigned long value;
+ unsigned int pos = LID_RESUME_S5;
if (count > 31)
return -EINVAL;
@@ -2245,21 +2316,21 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev,
* +--------------+
* 2 1 0
*/
- if (strcmp(attr->attr.name, "lid_resume_S3") == 0)
- pos = 2;
- else if (strcmp(attr->attr.name, "lid_resume_S4") == 0)
- pos = 1;
- else if (strcmp(attr->attr.name, "lid_resume_S5") == 0)
- pos = 0;
- else
- return -EINVAL;
+ while (pos < LID_RESUME_MAX) {
+ if (&lid_ctl->attrs[pos].attr == &attr->attr)
+ break;
+ pos++;
+ }
+ if (pos == LID_RESUME_MAX)
+ return -EINVAL;
if (value)
value = lid_ctl->status | (1 << pos);
else
value = lid_ctl->status & ~(1 << pos);
- if (sony_call_snc_handle(0x0119, value << 0x10 | 0x0100, &result))
+ if (sony_call_snc_handle(lid_ctl->handle, value << 0x10 | 0x0100,
+ &result))
return -EIO;
lid_ctl->status = value;
@@ -2268,29 +2339,27 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev,
}
static ssize_t sony_nc_lid_resume_show(struct device *dev,
- struct device_attribute *attr, char *buffer)
+ struct device_attribute *attr,
+ char *buffer)
{
- unsigned int pos;
+ unsigned int pos = LID_RESUME_S5;
- if (strcmp(attr->attr.name, "lid_resume_S3") == 0)
- pos = 2;
- else if (strcmp(attr->attr.name, "lid_resume_S4") == 0)
- pos = 1;
- else if (strcmp(attr->attr.name, "lid_resume_S5") == 0)
- pos = 0;
- else
- return -EINVAL;
-
- return snprintf(buffer, PAGE_SIZE, "%d\n",
- (lid_ctl->status >> pos) & 0x01);
+ while (pos < LID_RESUME_MAX) {
+ if (&lid_ctl->attrs[pos].attr == &attr->attr)
+ return snprintf(buffer, PAGE_SIZE, "%d\n",
+ (lid_ctl->status >> pos) & 0x01);
+ pos++;
+ }
+ return -EINVAL;
}
-static int sony_nc_lid_resume_setup(struct platform_device *pd)
+static int sony_nc_lid_resume_setup(struct platform_device *pd,
+ unsigned int handle)
{
unsigned int result;
int i;
- if (sony_call_snc_handle(0x0119, 0x0000, &result))
+ if (sony_call_snc_handle(handle, 0x0000, &result))
return -EIO;
lid_ctl = kzalloc(sizeof(struct snc_lid_resume_control), GFP_KERNEL);
@@ -2298,26 +2367,29 @@ static int sony_nc_lid_resume_setup(struct platform_device *pd)
return -ENOMEM;
lid_ctl->status = result & 0x7;
+ lid_ctl->handle = handle;
sysfs_attr_init(&lid_ctl->attrs[0].attr);
- lid_ctl->attrs[0].attr.name = "lid_resume_S3";
- lid_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR;
- lid_ctl->attrs[0].show = sony_nc_lid_resume_show;
- lid_ctl->attrs[0].store = sony_nc_lid_resume_store;
-
- sysfs_attr_init(&lid_ctl->attrs[1].attr);
- lid_ctl->attrs[1].attr.name = "lid_resume_S4";
- lid_ctl->attrs[1].attr.mode = S_IRUGO | S_IWUSR;
- lid_ctl->attrs[1].show = sony_nc_lid_resume_show;
- lid_ctl->attrs[1].store = sony_nc_lid_resume_store;
-
- sysfs_attr_init(&lid_ctl->attrs[2].attr);
- lid_ctl->attrs[2].attr.name = "lid_resume_S5";
- lid_ctl->attrs[2].attr.mode = S_IRUGO | S_IWUSR;
- lid_ctl->attrs[2].show = sony_nc_lid_resume_show;
- lid_ctl->attrs[2].store = sony_nc_lid_resume_store;
-
- for (i = 0; i < 3; i++) {
+ lid_ctl->attrs[LID_RESUME_S5].attr.name = "lid_resume_S5";
+ lid_ctl->attrs[LID_RESUME_S5].attr.mode = S_IRUGO | S_IWUSR;
+ lid_ctl->attrs[LID_RESUME_S5].show = sony_nc_lid_resume_show;
+ lid_ctl->attrs[LID_RESUME_S5].store = sony_nc_lid_resume_store;
+
+ if (handle == 0x0119) {
+ sysfs_attr_init(&lid_ctl->attrs[1].attr);
+ lid_ctl->attrs[LID_RESUME_S4].attr.name = "lid_resume_S4";
+ lid_ctl->attrs[LID_RESUME_S4].attr.mode = S_IRUGO | S_IWUSR;
+ lid_ctl->attrs[LID_RESUME_S4].show = sony_nc_lid_resume_show;
+ lid_ctl->attrs[LID_RESUME_S4].store = sony_nc_lid_resume_store;
+
+ sysfs_attr_init(&lid_ctl->attrs[2].attr);
+ lid_ctl->attrs[LID_RESUME_S3].attr.name = "lid_resume_S3";
+ lid_ctl->attrs[LID_RESUME_S3].attr.mode = S_IRUGO | S_IWUSR;
+ lid_ctl->attrs[LID_RESUME_S3].show = sony_nc_lid_resume_show;
+ lid_ctl->attrs[LID_RESUME_S3].store = sony_nc_lid_resume_store;
+ }
+ for (i = 0; i < LID_RESUME_MAX &&
+ lid_ctl->attrs[LID_RESUME_S3].attr.name; i++) {
result = device_create_file(&pd->dev, &lid_ctl->attrs[i]);
if (result)
goto liderror;
@@ -2340,8 +2412,12 @@ static void sony_nc_lid_resume_cleanup(struct platform_device *pd)
int i;
if (lid_ctl) {
- for (i = 0; i < 3; i++)
+ for (i = 0; i < LID_RESUME_MAX; i++) {
+ if (!lid_ctl->attrs[i].attr.name)
+ break;
+
device_remove_file(&pd->dev, &lid_ctl->attrs[i]);
+ }
kfree(lid_ctl);
lid_ctl = NULL;
@@ -2524,6 +2600,355 @@ static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd)
}
}
+/* low battery function */
+static struct device_attribute *lowbatt_handle;
+
+static ssize_t sony_nc_lowbatt_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned int result;
+ unsigned long value;
+
+ if (count > 31)
+ return -EINVAL;
+
+ if (kstrtoul(buffer, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (sony_call_snc_handle(0x0121, value << 8, &result))
+ return -EIO;
+
+ return count;
+}
+
+static ssize_t sony_nc_lowbatt_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0121, 0x0200, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result & 1);
+}
+
+static int sony_nc_lowbatt_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ lowbatt_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!lowbatt_handle)
+ return -ENOMEM;
+
+ sysfs_attr_init(&lowbatt_handle->attr);
+ lowbatt_handle->attr.name = "lowbatt_hibernate";
+ lowbatt_handle->attr.mode = S_IRUGO | S_IWUSR;
+ lowbatt_handle->show = sony_nc_lowbatt_show;
+ lowbatt_handle->store = sony_nc_lowbatt_store;
+
+ result = device_create_file(&pd->dev, lowbatt_handle);
+ if (result) {
+ kfree(lowbatt_handle);
+ lowbatt_handle = NULL;
+ return result;
+ }
+
+ return 0;
+}
+
+static void sony_nc_lowbatt_cleanup(struct platform_device *pd)
+{
+ if (lowbatt_handle) {
+ device_remove_file(&pd->dev, lowbatt_handle);
+ kfree(lowbatt_handle);
+ lowbatt_handle = NULL;
+ }
+}
+
+/* fan speed function */
+static struct device_attribute *fan_handle, *hsf_handle;
+
+static ssize_t sony_nc_hsfan_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned int result;
+ unsigned long value;
+
+ if (count > 31)
+ return -EINVAL;
+
+ if (kstrtoul(buffer, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (sony_call_snc_handle(0x0149, value << 0x10 | 0x0200, &result))
+ return -EIO;
+
+ return count;
+}
+
+static ssize_t sony_nc_hsfan_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0149, 0x0100, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01);
+}
+
+static ssize_t sony_nc_fanspeed_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0149, 0x0300, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0xff);
+}
+
+static int sony_nc_fanspeed_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ fan_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!fan_handle)
+ return -ENOMEM;
+
+ hsf_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!hsf_handle) {
+ result = -ENOMEM;
+ goto out_hsf_handle_alloc;
+ }
+
+ sysfs_attr_init(&fan_handle->attr);
+ fan_handle->attr.name = "fanspeed";
+ fan_handle->attr.mode = S_IRUGO;
+ fan_handle->show = sony_nc_fanspeed_show;
+ fan_handle->store = NULL;
+
+ sysfs_attr_init(&hsf_handle->attr);
+ hsf_handle->attr.name = "fan_forced";
+ hsf_handle->attr.mode = S_IRUGO | S_IWUSR;
+ hsf_handle->show = sony_nc_hsfan_show;
+ hsf_handle->store = sony_nc_hsfan_store;
+
+ result = device_create_file(&pd->dev, fan_handle);
+ if (result)
+ goto out_fan_handle;
+
+ result = device_create_file(&pd->dev, hsf_handle);
+ if (result)
+ goto out_hsf_handle;
+
+ return 0;
+
+out_hsf_handle:
+ device_remove_file(&pd->dev, fan_handle);
+
+out_fan_handle:
+ kfree(hsf_handle);
+ hsf_handle = NULL;
+
+out_hsf_handle_alloc:
+ kfree(fan_handle);
+ fan_handle = NULL;
+ return result;
+}
+
+static void sony_nc_fanspeed_cleanup(struct platform_device *pd)
+{
+ if (fan_handle) {
+ device_remove_file(&pd->dev, fan_handle);
+ kfree(fan_handle);
+ fan_handle = NULL;
+ }
+ if (hsf_handle) {
+ device_remove_file(&pd->dev, hsf_handle);
+ kfree(hsf_handle);
+ hsf_handle = NULL;
+ }
+}
+
+/* USB charge function */
+static struct device_attribute *uc_handle;
+
+static ssize_t sony_nc_usb_charge_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned int result;
+ unsigned long value;
+
+ if (count > 31)
+ return -EINVAL;
+
+ if (kstrtoul(buffer, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (sony_call_snc_handle(0x0155, value << 0x10 | 0x0100, &result))
+ return -EIO;
+
+ return count;
+}
+
+static ssize_t sony_nc_usb_charge_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0155, 0x0000, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01);
+}
+
+static int sony_nc_usb_charge_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0155, 0x0000, &result) || !(result & 0x01)) {
+ /* some models advertise the handle but have no implementation
+ * for it
+ */
+ pr_info("No USB Charge capability found\n");
+ return 0;
+ }
+
+ uc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!uc_handle)
+ return -ENOMEM;
+
+ sysfs_attr_init(&uc_handle->attr);
+ uc_handle->attr.name = "usb_charge";
+ uc_handle->attr.mode = S_IRUGO | S_IWUSR;
+ uc_handle->show = sony_nc_usb_charge_show;
+ uc_handle->store = sony_nc_usb_charge_store;
+
+ result = device_create_file(&pd->dev, uc_handle);
+ if (result) {
+ kfree(uc_handle);
+ uc_handle = NULL;
+ return result;
+ }
+
+ return 0;
+}
+
+static void sony_nc_usb_charge_cleanup(struct platform_device *pd)
+{
+ if (uc_handle) {
+ device_remove_file(&pd->dev, uc_handle);
+ kfree(uc_handle);
+ uc_handle = NULL;
+ }
+}
+
+/* Panel ID function */
+static struct device_attribute *panel_handle;
+
+static ssize_t sony_nc_panelid_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x011D, 0x0000, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result);
+}
+
+static int sony_nc_panelid_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ panel_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!panel_handle)
+ return -ENOMEM;
+
+ sysfs_attr_init(&panel_handle->attr);
+ panel_handle->attr.name = "panel_id";
+ panel_handle->attr.mode = S_IRUGO;
+ panel_handle->show = sony_nc_panelid_show;
+ panel_handle->store = NULL;
+
+ result = device_create_file(&pd->dev, panel_handle);
+ if (result) {
+ kfree(panel_handle);
+ panel_handle = NULL;
+ return result;
+ }
+
+ return 0;
+}
+
+static void sony_nc_panelid_cleanup(struct platform_device *pd)
+{
+ if (panel_handle) {
+ device_remove_file(&pd->dev, panel_handle);
+ kfree(panel_handle);
+ panel_handle = NULL;
+ }
+}
+
+/* smart connect function */
+static struct device_attribute *sc_handle;
+
+static ssize_t sony_nc_smart_conn_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned int result;
+ unsigned long value;
+
+ if (count > 31)
+ return -EINVAL;
+
+ if (kstrtoul(buffer, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (sony_call_snc_handle(0x0168, value << 0x10, &result))
+ return -EIO;
+
+ return count;
+}
+
+static int sony_nc_smart_conn_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ sc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!sc_handle)
+ return -ENOMEM;
+
+ sysfs_attr_init(&sc_handle->attr);
+ sc_handle->attr.name = "smart_connect";
+ sc_handle->attr.mode = S_IWUSR;
+ sc_handle->show = NULL;
+ sc_handle->store = sony_nc_smart_conn_store;
+
+ result = device_create_file(&pd->dev, sc_handle);
+ if (result) {
+ kfree(sc_handle);
+ sc_handle = NULL;
+ return result;
+ }
+
+ return 0;
+}
+
+static void sony_nc_smart_conn_cleanup(struct platform_device *pd)
+{
+ if (sc_handle) {
+ device_remove_file(&pd->dev, sc_handle);
+ kfree(sc_handle);
+ sc_handle = NULL;
+ }
+}
+
/* Touchpad enable/disable */
struct touchpad_control {
struct device_attribute attr;
@@ -2726,8 +3151,6 @@ static int sony_nc_add(struct acpi_device *device)
int result = 0;
struct sony_nc_value *item;
- pr_info("%s v%s\n", SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
-
sony_nc_acpi_device = device;
strcpy(acpi_device_class(device), "sony/hotkey");
@@ -2821,6 +3244,7 @@ static int sony_nc_add(struct acpi_device *device)
}
}
+ pr_info("SNC setup done.\n");
return 0;
out_sysfs:
@@ -4259,8 +4683,6 @@ static int sony_pic_add(struct acpi_device *device)
struct sony_pic_ioport *io, *tmp_io;
struct sony_pic_irq *irq, *tmp_irq;
- pr_info("%s v%s\n", SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
-
spic_dev.acpi_dev = device;
strcpy(acpi_device_class(device), "sony/hotkey");
sony_pic_detect_device_type(&spic_dev);
@@ -4360,6 +4782,7 @@ static int sony_pic_add(struct acpi_device *device)
if (result)
goto err_remove_pf;
+ pr_info("SPIC setup done.\n");
return 0;
err_remove_pf:
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 94bb6157c957..15e61c16736e 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -2321,53 +2321,55 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
}
}
-static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
- struct tp_nvram_state *newn,
- const u32 event_mask)
-{
-
#define TPACPI_COMPARE_KEY(__scancode, __member) \
- do { \
- if ((event_mask & (1 << __scancode)) && \
- oldn->__member != newn->__member) \
- tpacpi_hotkey_send_key(__scancode); \
- } while (0)
+do { \
+ if ((event_mask & (1 << __scancode)) && \
+ oldn->__member != newn->__member) \
+ tpacpi_hotkey_send_key(__scancode); \
+} while (0)
#define TPACPI_MAY_SEND_KEY(__scancode) \
- do { \
- if (event_mask & (1 << __scancode)) \
- tpacpi_hotkey_send_key(__scancode); \
- } while (0)
+do { \
+ if (event_mask & (1 << __scancode)) \
+ tpacpi_hotkey_send_key(__scancode); \
+} while (0)
- void issue_volchange(const unsigned int oldvol,
- const unsigned int newvol)
- {
- unsigned int i = oldvol;
+static void issue_volchange(const unsigned int oldvol,
+ const unsigned int newvol,
+ const u32 event_mask)
+{
+ unsigned int i = oldvol;
- while (i > newvol) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
- i--;
- }
- while (i < newvol) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
- i++;
- }
+ while (i > newvol) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
+ i--;
+ }
+ while (i < newvol) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
+ i++;
}
+}
- void issue_brightnesschange(const unsigned int oldbrt,
- const unsigned int newbrt)
- {
- unsigned int i = oldbrt;
+static void issue_brightnesschange(const unsigned int oldbrt,
+ const unsigned int newbrt,
+ const u32 event_mask)
+{
+ unsigned int i = oldbrt;
- while (i > newbrt) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
- i--;
- }
- while (i < newbrt) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
- i++;
- }
+ while (i > newbrt) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
+ i--;
}
+ while (i < newbrt) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
+ i++;
+ }
+}
+
+static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
+ struct tp_nvram_state *newn,
+ const u32 event_mask)
+{
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
@@ -2402,7 +2404,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
oldn->volume_level != newn->volume_level) {
/* recently muted, or repeated mute keypress, or
* multiple presses ending in mute */
- issue_volchange(oldn->volume_level, newn->volume_level);
+ issue_volchange(oldn->volume_level, newn->volume_level,
+ event_mask);
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
}
} else {
@@ -2412,7 +2415,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
}
if (oldn->volume_level != newn->volume_level) {
- issue_volchange(oldn->volume_level, newn->volume_level);
+ issue_volchange(oldn->volume_level, newn->volume_level,
+ event_mask);
} else if (oldn->volume_toggle != newn->volume_toggle) {
/* repeated vol up/down keypress at end of scale ? */
if (newn->volume_level == 0)
@@ -2425,7 +2429,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
/* handle brightness */
if (oldn->brightness_level != newn->brightness_level) {
issue_brightnesschange(oldn->brightness_level,
- newn->brightness_level);
+ newn->brightness_level, event_mask);
} else if (oldn->brightness_toggle != newn->brightness_toggle) {
/* repeated key presses that didn't change state */
if (newn->brightness_level == 0)
@@ -3437,6 +3441,106 @@ err_exit:
return (res < 0)? res : 1;
}
+/* Thinkpad X1 Carbon support 5 modes including Home mode, Web browser
+ * mode, Web conference mode, Function mode and Lay-flat mode.
+ * We support Home mode and Function mode currently.
+ *
+ * Will consider support rest of modes in future.
+ *
+ */
+enum ADAPTIVE_KEY_MODE {
+ HOME_MODE,
+ WEB_BROWSER_MODE,
+ WEB_CONFERENCE_MODE,
+ FUNCTION_MODE,
+ LAYFLAT_MODE
+};
+
+const int adaptive_keyboard_modes[] = {
+ HOME_MODE,
+/* WEB_BROWSER_MODE = 2,
+ WEB_CONFERENCE_MODE = 3, */
+ FUNCTION_MODE
+};
+
+#define DFR_CHANGE_ROW 0x101
+#define DFR_SHOW_QUICKVIEW_ROW 0x102
+
+/* press Fn key a while second, it will switch to Function Mode. Then
+ * release Fn key, previous mode be restored.
+ */
+static bool adaptive_keyboard_mode_is_saved;
+static int adaptive_keyboard_prev_mode;
+
+static int adaptive_keyboard_get_next_mode(int mode)
+{
+ size_t i;
+ size_t max_mode = ARRAY_SIZE(adaptive_keyboard_modes) - 1;
+
+ for (i = 0; i <= max_mode; i++) {
+ if (adaptive_keyboard_modes[i] == mode)
+ break;
+ }
+
+ if (i >= max_mode)
+ i = 0;
+ else
+ i++;
+
+ return adaptive_keyboard_modes[i];
+}
+
+static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode)
+{
+ u32 current_mode = 0;
+ int new_mode = 0;
+
+ switch (scancode) {
+ case DFR_CHANGE_ROW:
+ if (adaptive_keyboard_mode_is_saved) {
+ new_mode = adaptive_keyboard_prev_mode;
+ adaptive_keyboard_mode_is_saved = false;
+ } else {
+ if (!acpi_evalf(
+ hkey_handle, &current_mode,
+ "GTRW", "dd", 0)) {
+ pr_err("Cannot read adaptive keyboard mode\n");
+ return false;
+ } else {
+ new_mode = adaptive_keyboard_get_next_mode(
+ current_mode);
+ }
+ }
+
+ if (!acpi_evalf(hkey_handle, NULL, "STRW", "vd", new_mode)) {
+ pr_err("Cannot set adaptive keyboard mode\n");
+ return false;
+ }
+
+ return true;
+
+ case DFR_SHOW_QUICKVIEW_ROW:
+ if (!acpi_evalf(hkey_handle,
+ &adaptive_keyboard_prev_mode,
+ "GTRW", "dd", 0)) {
+ pr_err("Cannot read adaptive keyboard mode\n");
+ return false;
+ } else {
+ adaptive_keyboard_mode_is_saved = true;
+
+ if (!acpi_evalf(hkey_handle,
+ NULL, "STRW", "vd", FUNCTION_MODE)) {
+ pr_err("Cannot set adaptive keyboard mode\n");
+ return false;
+ }
+ }
+ return true;
+
+ default:
+ return false;
+ }
+}
+
static bool hotkey_notify_hotkey(const u32 hkey,
bool *send_acpi_ev,
bool *ignore_acpi_ev)
@@ -3456,6 +3560,8 @@ static bool hotkey_notify_hotkey(const u32 hkey,
*ignore_acpi_ev = true;
}
return true;
+ } else {
+ return adaptive_keyboard_hotkey_notify_hotkey(scancode);
}
return false;
}
@@ -3728,13 +3834,28 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
static void hotkey_suspend(void)
{
+ int hkeyv;
+
/* Do these on suspend, we get the events on early resume! */
hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE;
hotkey_autosleep_ack = 0;
+
+ /* save previous mode of adaptive keyboard of X1 Carbon */
+ if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
+ if ((hkeyv >> 8) == 2) {
+ if (!acpi_evalf(hkey_handle,
+ &adaptive_keyboard_prev_mode,
+ "GTRW", "dd", 0)) {
+ pr_err("Cannot read adaptive keyboard mode.\n");
+ }
+ }
+ }
}
static void hotkey_resume(void)
{
+ int hkeyv;
+
tpacpi_disable_brightness_delay();
if (hotkey_status_set(true) < 0 ||
@@ -3747,6 +3868,18 @@ static void hotkey_resume(void)
hotkey_wakeup_reason_notify_change();
hotkey_wakeup_hotunplug_complete_notify_change();
hotkey_poll_setup_safe(false);
+
+ /* restore previous mode of adapive keyboard of X1 Carbon */
+ if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
+ if ((hkeyv >> 8) == 2) {
+ if (!acpi_evalf(hkey_handle,
+ NULL,
+ "STRW", "vd",
+ adaptive_keyboard_prev_mode)) {
+ pr_err("Cannot set adaptive keyboard mode.\n");
+ }
+ }
+ }
}
/* procfs -------------------------------------------------------------- */
@@ -8447,9 +8580,21 @@ static void mute_led_exit(void)
tpacpi_led_set(i, false);
}
+static void mute_led_resume(void)
+{
+ int i;
+
+ for (i = 0; i < TPACPI_LED_MAX; i++) {
+ struct tp_led_table *t = &led_tables[i];
+ if (t->state >= 0)
+ mute_led_on_off(t, t->state);
+ }
+}
+
static struct ibm_struct mute_led_driver_data = {
.name = "mute_led",
.exit = mute_led_exit,
+ .resume = mute_led_resume,
};
/****************************************************************************
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 90dd7645a9e5..46473ca7566b 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -5,6 +5,7 @@
* Copyright (C) 2002-2004 John Belmonte
* Copyright (C) 2008 Philip Langdale
* Copyright (C) 2010 Pierre Ducroquet
+ * Copyright (C) 2014 Azael Avalos
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -37,7 +38,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#define TOSHIBA_ACPI_VERSION "0.19"
+#define TOSHIBA_ACPI_VERSION "0.20"
#define PROC_INTERFACE_VERSION 1
#include <linux/kernel.h>
@@ -77,6 +78,9 @@ MODULE_LICENSE("GPL");
* However the ACPI methods seem to be incomplete in some areas (for
* example they allow setting, but not reading, the LCD brightness value),
* so this is still useful.
+ *
+ * SCI stands for "System Configuration Interface" which aim is to
+ * conceal differences in hardware between different models.
*/
#define HCI_WORDS 6
@@ -84,12 +88,23 @@ MODULE_LICENSE("GPL");
/* operations */
#define HCI_SET 0xff00
#define HCI_GET 0xfe00
+#define SCI_OPEN 0xf100
+#define SCI_CLOSE 0xf200
+#define SCI_GET 0xf300
+#define SCI_SET 0xf400
/* return codes */
#define HCI_SUCCESS 0x0000
#define HCI_FAILURE 0x1000
#define HCI_NOT_SUPPORTED 0x8000
#define HCI_EMPTY 0x8c00
+#define HCI_DATA_NOT_AVAILABLE 0x8d20
+#define HCI_NOT_INITIALIZED 0x8d50
+#define SCI_OPEN_CLOSE_OK 0x0044
+#define SCI_ALREADY_OPEN 0x8100
+#define SCI_NOT_OPENED 0x8200
+#define SCI_INPUT_DATA_ERROR 0x8300
+#define SCI_NOT_PRESENT 0x8600
/* registers */
#define HCI_FAN 0x0004
@@ -99,13 +114,22 @@ MODULE_LICENSE("GPL");
#define HCI_HOTKEY_EVENT 0x001e
#define HCI_LCD_BRIGHTNESS 0x002a
#define HCI_WIRELESS 0x0056
+#define HCI_ACCELEROMETER 0x006d
+#define HCI_KBD_ILLUMINATION 0x0095
+#define HCI_ECO_MODE 0x0097
+#define HCI_ACCELEROMETER2 0x00a6
+#define SCI_ILLUMINATION 0x014e
+#define SCI_KBD_ILLUM_STATUS 0x015c
+#define SCI_TOUCHPAD 0x050e
/* field definitions */
+#define HCI_ACCEL_MASK 0x7fff
#define HCI_HOTKEY_DISABLE 0x0b
#define HCI_HOTKEY_ENABLE 0x09
#define HCI_LCD_BRIGHTNESS_BITS 3
#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS)
#define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS)
+#define HCI_MISC_SHIFT 0x10
#define HCI_VIDEO_OUT_LCD 0x1
#define HCI_VIDEO_OUT_CRT 0x2
#define HCI_VIDEO_OUT_TV 0x4
@@ -113,6 +137,8 @@ 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_FNZ 0x1
+#define SCI_KBD_MODE_AUTO 0x2
struct toshiba_acpi_dev {
struct acpi_device *acpi_dev;
@@ -122,10 +148,14 @@ struct toshiba_acpi_dev {
struct work_struct hotkey_work;
struct backlight_device *backlight_dev;
struct led_classdev led_dev;
+ struct led_classdev kbd_led;
+ struct led_classdev eco_led;
int force_fan;
int last_key_event;
int key_event_valid;
+ int kbd_mode;
+ int kbd_time;
unsigned int illumination_supported:1;
unsigned int video_supported:1;
@@ -134,6 +164,12 @@ struct toshiba_acpi_dev {
unsigned int ntfy_supported:1;
unsigned int info_supported:1;
unsigned int tr_backlight_supported:1;
+ unsigned int kbd_illum_supported:1;
+ unsigned int kbd_led_registered:1;
+ unsigned int touchpad_supported:1;
+ unsigned int eco_supported:1;
+ unsigned int accelerometer_supported:1;
+ unsigned int sysfs_created:1;
struct mutex mutex;
};
@@ -280,21 +316,94 @@ static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg,
return status;
}
+/* common sci tasks
+ */
+
+static int sci_open(struct toshiba_acpi_dev *dev)
+{
+ u32 in[HCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
+ pr_err("ACPI call to open SCI failed\n");
+ return 0;
+ }
+
+ if (out[0] == SCI_OPEN_CLOSE_OK) {
+ return 1;
+ } else if (out[0] == SCI_ALREADY_OPEN) {
+ pr_info("Toshiba SCI already opened\n");
+ return 1;
+ } else if (out[0] == SCI_NOT_PRESENT) {
+ pr_info("Toshiba SCI is not present\n");
+ }
+
+ return 0;
+}
+
+static void sci_close(struct toshiba_acpi_dev *dev)
+{
+ u32 in[HCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
+ pr_err("ACPI call to close SCI failed\n");
+ return;
+ }
+
+ if (out[0] == SCI_OPEN_CLOSE_OK)
+ return;
+ else if (out[0] == SCI_NOT_OPENED)
+ pr_info("Toshiba SCI not opened\n");
+ else if (out[0] == SCI_NOT_PRESENT)
+ pr_info("Toshiba SCI is not present\n");
+}
+
+static acpi_status sci_read(struct toshiba_acpi_dev *dev, u32 reg,
+ u32 *out1, u32 *result)
+{
+ u32 in[HCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status = hci_raw(dev, in, out);
+ *out1 = out[2];
+ *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE;
+ return status;
+}
+
+static acpi_status sci_write(struct toshiba_acpi_dev *dev, u32 reg,
+ u32 in1, u32 *result)
+{
+ u32 in[HCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status = hci_raw(dev, in, out);
+ *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE;
+ return status;
+}
+
/* Illumination support */
static int toshiba_illumination_available(struct toshiba_acpi_dev *dev)
{
- u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
+ u32 in[HCI_WORDS] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status;
- in[0] = 0xf100;
+ if (!sci_open(dev))
+ return 0;
+
status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
+ pr_err("ACPI call to query Illumination support failed\n");
+ return 0;
+ } else if (out[0] == HCI_NOT_SUPPORTED || out[1] != 1) {
pr_info("Illumination device not available\n");
return 0;
}
- in[0] = 0xf400;
- status = hci_raw(dev, in, out);
+
return 1;
}
@@ -303,82 +412,270 @@ static void toshiba_illumination_set(struct led_classdev *cdev,
{
struct toshiba_acpi_dev *dev = container_of(cdev,
struct toshiba_acpi_dev, led_dev);
- u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
- u32 out[HCI_WORDS];
+ u32 state, result;
acpi_status status;
/* First request : initialize communication. */
- in[0] = 0xf100;
- status = hci_raw(dev, in, out);
+ if (!sci_open(dev))
+ return;
+
+ /* Switch the illumination on/off */
+ state = brightness ? 1 : 0;
+ status = sci_write(dev, SCI_ILLUMINATION, state, &result);
+ sci_close(dev);
if (ACPI_FAILURE(status)) {
- pr_info("Illumination device not available\n");
+ pr_err("ACPI call for illumination failed\n");
+ return;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Illumination not supported\n");
return;
}
+}
- if (brightness) {
- /* Switch the illumination on */
- in[0] = 0xf400;
- in[1] = 0x14e;
- in[2] = 1;
- status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("ACPI call for illumination failed\n");
- return;
- }
- } else {
- /* Switch the illumination off */
- in[0] = 0xf400;
- in[1] = 0x14e;
- in[2] = 0;
- status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("ACPI call for illumination failed.\n");
- return;
- }
+static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, led_dev);
+ u32 state, result;
+ acpi_status status;
+
+ /* First request : initialize communication. */
+ if (!sci_open(dev))
+ return LED_OFF;
+
+ /* Check the illumination */
+ status = sci_read(dev, SCI_ILLUMINATION, &state, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call for illumination failed\n");
+ return LED_OFF;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Illumination not supported\n");
+ return LED_OFF;
}
- /* Last request : close communication. */
- in[0] = 0xf200;
- in[1] = 0;
- in[2] = 0;
- hci_raw(dev, in, out);
+ return state ? LED_FULL : LED_OFF;
}
-static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
+/* KBD Illumination */
+static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_write(dev, SCI_KBD_ILLUM_STATUS, time, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to set KBD backlight status failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight status not supported\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_read(dev, SCI_KBD_ILLUM_STATUS, time, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to get KBD backlight status failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight status not supported\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static enum led_brightness toshiba_kbd_backlight_get(struct led_classdev *cdev)
{
struct toshiba_acpi_dev *dev = container_of(cdev,
- struct toshiba_acpi_dev, led_dev);
- u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
+ struct toshiba_acpi_dev, kbd_led);
+ u32 state, result;
+ acpi_status status;
+
+ /* Check the keyboard backlight state */
+ status = hci_read1(dev, HCI_KBD_ILLUMINATION, &state, &result);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to get the keyboard backlight failed\n");
+ return LED_OFF;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight not supported\n");
+ return LED_OFF;
+ }
+
+ return state ? LED_FULL : LED_OFF;
+}
+
+static void toshiba_kbd_backlight_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, kbd_led);
+ u32 state, result;
+ acpi_status status;
+
+ /* Set the keyboard backlight state */
+ state = brightness ? 1 : 0;
+ status = hci_write1(dev, HCI_KBD_ILLUMINATION, state, &result);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to set KBD Illumination mode failed\n");
+ return;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight not supported\n");
+ return;
+ }
+}
+
+/* TouchPad support */
+static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_write(dev, SCI_TOUCHPAD, state, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status)) {
+ pr_err("ACPI call to set the touchpad failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_read(dev, SCI_TOUCHPAD, state, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status)) {
+ pr_err("ACPI call to query the touchpad failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/* Eco Mode support */
+static int toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
+{
+ acpi_status status;
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 };
+ u32 out[HCI_WORDS];
+
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_info("ACPI call to get ECO led failed\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static enum led_brightness toshiba_eco_mode_get_status(struct led_classdev *cdev)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, eco_led);
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status;
- enum led_brightness result;
- /* First request : initialize communication. */
- in[0] = 0xf100;
status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("Illumination device not available\n");
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to get ECO led failed\n");
return LED_OFF;
}
- /* Check the illumination */
- in[0] = 0xf300;
- in[1] = 0x14e;
+ return out[2] ? LED_FULL : LED_OFF;
+}
+
+static void toshiba_eco_mode_set_status(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, eco_led);
+ u32 in[HCI_WORDS] = { HCI_SET, HCI_ECO_MODE, 0, 1, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ /* Switch the Eco Mode led on/off */
+ in[2] = (brightness) ? 1 : 0;
status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("ACPI call for illumination failed.\n");
- return LED_OFF;
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to set ECO led failed\n");
+ return;
}
+}
- result = out[2] ? LED_FULL : LED_OFF;
+/* Accelerometer support */
+static int toshiba_accelerometer_supported(struct toshiba_acpi_dev *dev)
+{
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER2, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ /* Check if the accelerometer call exists,
+ * this call also serves as initialization
+ */
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to query the accelerometer failed\n");
+ return -EIO;
+ } else if (out[0] == HCI_DATA_NOT_AVAILABLE ||
+ out[0] == HCI_NOT_INITIALIZED) {
+ pr_err("Accelerometer not initialized\n");
+ return -EIO;
+ } else if (out[0] == HCI_NOT_SUPPORTED) {
+ pr_info("Accelerometer not supported\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev,
+ u32 *xy, u32 *z)
+{
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER, 0, 1, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ /* Check the Accelerometer status */
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to query the accelerometer failed\n");
+ return -EIO;
+ }
- /* Last request : close communication. */
- in[0] = 0xf200;
- in[1] = 0;
- in[2] = 0;
- hci_raw(dev, in, out);
+ *xy = out[2];
+ *z = out[4];
- return result;
+ return 0;
}
/* Bluetooth rfkill handlers */
@@ -904,6 +1201,177 @@ static const struct backlight_ops toshiba_backlight_data = {
.update_status = set_lcd_status,
};
+/*
+ * Sysfs files
+ */
+
+static ssize_t toshiba_kbd_bl_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ int mode = -1;
+ int time = -1;
+
+ if (sscanf(buf, "%i", &mode) != 1 && (mode != 2 || mode != 1))
+ 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
+ */
+ if (mode != -1 && toshiba->kbd_mode != mode) {
+ time = toshiba->kbd_time << HCI_MISC_SHIFT;
+ time = time + toshiba->kbd_mode;
+ if (toshiba_kbd_illum_status_set(toshiba, time) < 0)
+ return -EIO;
+ toshiba->kbd_mode = mode;
+ }
+
+ return count;
+}
+
+static ssize_t toshiba_kbd_bl_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 time;
+
+ if (toshiba_kbd_illum_status_get(toshiba, &time) < 0)
+ return -EIO;
+
+ return sprintf(buf, "%i\n", time & 0x07);
+}
+
+static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ int time = -1;
+
+ if (sscanf(buf, "%i", &time) != 1 && (time < 0 || time > 60))
+ return -EINVAL;
+
+ /* Set the Keyboard Backlight Timeout: 0-60 seconds */
+ if (time != -1 && toshiba->kbd_time != time) {
+ time = time << HCI_MISC_SHIFT;
+ time = (toshiba->kbd_mode == SCI_KBD_MODE_AUTO) ?
+ time + 1 : time + 2;
+ if (toshiba_kbd_illum_status_set(toshiba, time) < 0)
+ return -EIO;
+ toshiba->kbd_time = time >> HCI_MISC_SHIFT;
+ }
+
+ return count;
+}
+
+static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 time;
+
+ if (toshiba_kbd_illum_status_get(toshiba, &time) < 0)
+ return -EIO;
+
+ return sprintf(buf, "%i\n", time >> HCI_MISC_SHIFT);
+}
+
+static ssize_t toshiba_touchpad_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ int state;
+
+ /* Set the TouchPad on/off, 0 - Disable | 1 - Enable */
+ if (sscanf(buf, "%i", &state) == 1 && (state == 0 || state == 1)) {
+ if (toshiba_touchpad_set(toshiba, state) < 0)
+ return -EIO;
+ }
+
+ return count;
+}
+
+static ssize_t toshiba_touchpad_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 state;
+ int ret;
+
+ ret = toshiba_touchpad_get(toshiba, &state);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%i\n", state);
+}
+
+static ssize_t toshiba_position_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 xyval, zval, tmp;
+ u16 x, y, z;
+ int ret;
+
+ xyval = zval = 0;
+ ret = toshiba_accelerometer_get(toshiba, &xyval, &zval);
+ if (ret < 0)
+ return ret;
+
+ x = xyval & HCI_ACCEL_MASK;
+ tmp = xyval >> HCI_MISC_SHIFT;
+ y = tmp & HCI_ACCEL_MASK;
+ z = zval & HCI_ACCEL_MASK;
+
+ 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)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct toshiba_acpi_dev *drv = dev_get_drvdata(dev);
+ bool exists = true;
+
+ if (attr == &dev_attr_kbd_backlight_mode.attr)
+ exists = (drv->kbd_illum_supported) ? true : false;
+ else if (attr == &dev_attr_kbd_backlight_timeout.attr)
+ exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false;
+ else if (attr == &dev_attr_touchpad.attr)
+ exists = (drv->touchpad_supported) ? true : false;
+ else if (attr == &dev_attr_position.attr)
+ exists = (drv->accelerometer_supported) ? true : false;
+
+ 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)
{
@@ -1106,6 +1574,10 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
remove_toshiba_proc_entries(dev);
+ if (dev->sysfs_created)
+ sysfs_remove_group(&dev->acpi_dev->dev.kobj,
+ &toshiba_attr_group);
+
if (dev->ntfy_supported) {
i8042_remove_filter(toshiba_acpi_i8042_filter);
cancel_work_sync(&dev->hotkey_work);
@@ -1127,6 +1599,12 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
if (dev->illumination_supported)
led_classdev_unregister(&dev->led_dev);
+ if (dev->kbd_led_registered)
+ led_classdev_unregister(&dev->kbd_led);
+
+ if (dev->eco_supported)
+ led_classdev_unregister(&dev->eco_led);
+
if (toshiba_acpi)
toshiba_acpi = NULL;
@@ -1172,6 +1650,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
dev->acpi_dev = acpi_dev;
dev->method_hci = hci_method;
acpi_dev->driver_data = dev;
+ dev_set_drvdata(&acpi_dev->dev, dev);
if (toshiba_acpi_setup_keyboard(dev))
pr_info("Unable to activate hotkeys\n");
@@ -1212,6 +1691,40 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
dev->illumination_supported = 1;
}
+ if (toshiba_eco_mode_available(dev)) {
+ dev->eco_led.name = "toshiba::eco_mode";
+ dev->eco_led.max_brightness = 1;
+ dev->eco_led.brightness_set = toshiba_eco_mode_set_status;
+ dev->eco_led.brightness_get = toshiba_eco_mode_get_status;
+ if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led))
+ 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;
+ /*
+ * Only register the LED if KBD illumination is supported
+ * and the keyboard backlight operation mode is set to FN-Z
+ */
+ if (dev->kbd_illum_supported && dev->kbd_mode == SCI_KBD_MODE_FNZ) {
+ dev->kbd_led.name = "toshiba::kbd_backlight";
+ dev->kbd_led.max_brightness = 1;
+ dev->kbd_led.brightness_set = toshiba_kbd_backlight_set;
+ dev->kbd_led.brightness_get = toshiba_kbd_backlight_get;
+ if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led))
+ dev->kbd_led_registered = 1;
+ }
+
+ ret = toshiba_touchpad_get(dev, &dummy);
+ dev->touchpad_supported = !ret;
+
+ ret = toshiba_accelerometer_supported(dev);
+ dev->accelerometer_supported = !ret;
+
/* Determine whether or not BIOS supports fan and video interfaces */
ret = get_video_status(dev, &dummy);
@@ -1220,6 +1733,14 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
ret = get_fan_status(dev, &dummy);
dev->fan_supported = !ret;
+ ret = sysfs_create_group(&dev->acpi_dev->dev.kobj,
+ &toshiba_attr_group);
+ if (ret) {
+ dev->sysfs_created = 0;
+ goto error;
+ }
+ dev->sysfs_created = !ret;
+
create_toshiba_proc_entries(dev);
toshiba_acpi = dev;