aboutsummaryrefslogtreecommitdiff
path: root/drivers/platform/x86/thinkpad_acpi.c
diff options
context:
space:
mode:
authorLinus Torvalds2016-01-19 17:54:15 -0800
committerLinus Torvalds2016-01-19 17:54:15 -0800
commit2b4015e9fb335aa6982a68dbe6e4158d6c1b10ee (patch)
tree2af6f884c42804cda14821f527a6237d4a1a3ee7 /drivers/platform/x86/thinkpad_acpi.c
parentd36ccdbd1ca1050ad4e7b9b16ced848132533f6e (diff)
parent4bef0a27e414f79e0200f33ab23adeb07965272e (diff)
Merge tag 'platform-drivers-x86-v4.5-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86
Pull x86 platform driver updates from Darren Hart: "Add intel punit and telemetry driver for APL SoCs. Add intel-hid driver for various laptop hotkey support. Add asus-wireless radio control driver. Keyboard backlight support/improvements for ThinkPads, Vaio, and Toshiba. Several hotkey related fixes and improvements for dell and toshiba. Fix oops on dual GPU Macs in apple-gmux. A few new device IDs and quirks. Various minor config related build issues and cleanups. surface pro 4: - fix compare_const_fl.cocci warnings - Add support for Surface Pro 4 Buttons platform/x86: - Add Intel Telemetry Debugfs interfaces - Add Intel telemetry platform device - Add Intel telemetry platform driver - Add Intel Telemetry Core Driver - add NULL check for input parameters - add Intel P-Unit mailbox IPC driver - update acpi resource structure for Punit thinkpad_acpi: - Add support for keyboard backlight dell-wmi: - Process only one event on devices with interface version 0 - Check if Dell WMI descriptor structure is valid - Improve unknown hotkey handling - Use a C99-style array for bios_to_linux_keycode tc1100-wmi: - fix build warning when CONFIG_PM not enabled asus-wireless: - Add ACPI HID ATK4001 - Add Asus Wireless Radio Control driver asus-wmi: - drop to_platform_driver macro intel-hid: - new hid event driver for hotkeys sony-laptop: - Keyboard backlight control for some Vaio Fit models ideapad-laptop: - Add Lenovo ideapad Y700-17ISK to no_hw_rfkill dmi list apple-gmux: - Assign apple_gmux_data before registering toshiba_acpi: - Add rfkill dependency to ACPI_TOSHIBA entry - Fix keyboard backlight sysfs entries not being updated - Add WWAN RFKill support - Add support for WWAN devices - Fix blank screen at boot if transflective backlight is supported - Propagate the hotkey value via genetlink toshiba_bluetooth: - Add missing newline in toshiba_bluetooth_present function" * tag 'platform-drivers-x86-v4.5-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86: (29 commits) surface pro 4: fix compare_const_fl.cocci warnings surface pro 4: Add support for Surface Pro 4 Buttons platform:x86: Add Intel Telemetry Debugfs interfaces platform:x86: Add Intel telemetry platform device platform:x86: Add Intel telemetry platform driver platform/x86: Add Intel Telemetry Core Driver intel_punit_ipc: add NULL check for input parameters thinkpad_acpi: Add support for keyboard backlight dell-wmi: Process only one event on devices with interface version 0 dell-wmi: Check if Dell WMI descriptor structure is valid tc1100-wmi: fix build warning when CONFIG_PM not enabled asus-wireless: Add ACPI HID ATK4001 platform/x86: Add Asus Wireless Radio Control driver asus-wmi: drop to_platform_driver macro intel-hid: new hid event driver for hotkeys Keyboard backlight control for some Vaio Fit models platform/x86: Add rfkill dependency to ACPI_TOSHIBA entry platform:x86: add Intel P-Unit mailbox IPC driver intel_pmc_ipc: update acpi resource structure for Punit ideapad-laptop: Add Lenovo ideapad Y700-17ISK to no_hw_rfkill dmi list ...
Diffstat (limited to 'drivers/platform/x86/thinkpad_acpi.c')
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c206
1 files changed, 206 insertions, 0 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index f453d5dc085e..1f0eda2cc06a 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -303,6 +303,7 @@ static struct {
u32 hotkey_mask:1;
u32 hotkey_wlsw:1;
u32 hotkey_tablet:1;
+ u32 kbdlight:1;
u32 light:1;
u32 light_status:1;
u32 bright_acpimode:1;
@@ -4986,6 +4987,207 @@ static struct ibm_struct video_driver_data = {
#endif /* CONFIG_THINKPAD_ACPI_VIDEO */
/*************************************************************************
+ * Keyboard backlight subdriver
+ */
+
+static int kbdlight_set_level(int level)
+{
+ if (!hkey_handle)
+ return -ENXIO;
+
+ if (!acpi_evalf(hkey_handle, NULL, "MLCS", "dd", level))
+ return -EIO;
+
+ return 0;
+}
+
+static int kbdlight_get_level(void)
+{
+ int status = 0;
+
+ if (!hkey_handle)
+ return -ENXIO;
+
+ if (!acpi_evalf(hkey_handle, &status, "MLCG", "dd", 0))
+ return -EIO;
+
+ if (status < 0)
+ return status;
+
+ return status & 0x3;
+}
+
+static bool kbdlight_is_supported(void)
+{
+ int status = 0;
+
+ if (!hkey_handle)
+ return false;
+
+ if (!acpi_has_method(hkey_handle, "MLCG")) {
+ vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG is unavailable\n");
+ return false;
+ }
+
+ if (!acpi_evalf(hkey_handle, &status, "MLCG", "qdd", 0)) {
+ vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG failed\n");
+ return false;
+ }
+
+ if (status < 0) {
+ vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG err: %d\n", status);
+ return false;
+ }
+
+ vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG returned 0x%x\n", status);
+ /*
+ * Guessed test for keyboard backlight:
+ *
+ * Machines with backlight keyboard return:
+ * b010100000010000000XX - ThinkPad X1 Carbon 3rd
+ * b110100010010000000XX - ThinkPad x230
+ * b010100000010000000XX - ThinkPad x240
+ * b010100000010000000XX - ThinkPad W541
+ * (XX is current backlight level)
+ *
+ * Machines without backlight keyboard return:
+ * b10100001000000000000 - ThinkPad x230
+ * b10110001000000000000 - ThinkPad E430
+ * b00000000000000000000 - ThinkPad E450
+ *
+ * Candidate BITs for detection test (XOR):
+ * b01000000001000000000
+ * ^
+ */
+ return status & BIT(9);
+}
+
+static void kbdlight_set_worker(struct work_struct *work)
+{
+ struct tpacpi_led_classdev *data =
+ container_of(work, struct tpacpi_led_classdev, work);
+
+ if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
+ kbdlight_set_level(data->new_state);
+}
+
+static void kbdlight_sysfs_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct tpacpi_led_classdev *data =
+ container_of(led_cdev,
+ struct tpacpi_led_classdev,
+ led_classdev);
+ data->new_state = brightness;
+ queue_work(tpacpi_wq, &data->work);
+}
+
+static enum led_brightness kbdlight_sysfs_get(struct led_classdev *led_cdev)
+{
+ int level;
+
+ level = kbdlight_get_level();
+ if (level < 0)
+ return 0;
+
+ return level;
+}
+
+static struct tpacpi_led_classdev tpacpi_led_kbdlight = {
+ .led_classdev = {
+ .name = "tpacpi::kbd_backlight",
+ .max_brightness = 2,
+ .brightness_set = &kbdlight_sysfs_set,
+ .brightness_get = &kbdlight_sysfs_get,
+ .flags = LED_CORE_SUSPENDRESUME,
+ }
+};
+
+static int __init kbdlight_init(struct ibm_init_struct *iibm)
+{
+ int rc;
+
+ vdbg_printk(TPACPI_DBG_INIT, "initializing kbdlight subdriver\n");
+
+ TPACPI_ACPIHANDLE_INIT(hkey);
+ INIT_WORK(&tpacpi_led_kbdlight.work, kbdlight_set_worker);
+
+ if (!kbdlight_is_supported()) {
+ tp_features.kbdlight = 0;
+ vdbg_printk(TPACPI_DBG_INIT, "kbdlight is unsupported\n");
+ return 1;
+ }
+
+ tp_features.kbdlight = 1;
+
+ rc = led_classdev_register(&tpacpi_pdev->dev,
+ &tpacpi_led_kbdlight.led_classdev);
+ if (rc < 0) {
+ tp_features.kbdlight = 0;
+ return rc;
+ }
+
+ return 0;
+}
+
+static void kbdlight_exit(void)
+{
+ if (tp_features.kbdlight)
+ led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev);
+ flush_workqueue(tpacpi_wq);
+}
+
+static int kbdlight_read(struct seq_file *m)
+{
+ int level;
+
+ if (!tp_features.kbdlight) {
+ seq_printf(m, "status:\t\tnot supported\n");
+ } else {
+ level = kbdlight_get_level();
+ if (level < 0)
+ seq_printf(m, "status:\t\terror %d\n", level);
+ else
+ seq_printf(m, "status:\t\t%d\n", level);
+ seq_printf(m, "commands:\t0, 1, 2\n");
+ }
+
+ return 0;
+}
+
+static int kbdlight_write(char *buf)
+{
+ char *cmd;
+ int level = -1;
+
+ if (!tp_features.kbdlight)
+ return -ENODEV;
+
+ while ((cmd = next_cmd(&buf))) {
+ if (strlencmp(cmd, "0") == 0)
+ level = 0;
+ else if (strlencmp(cmd, "1") == 0)
+ level = 1;
+ else if (strlencmp(cmd, "2") == 0)
+ level = 2;
+ else
+ return -EINVAL;
+ }
+
+ if (level == -1)
+ return -EINVAL;
+
+ return kbdlight_set_level(level);
+}
+
+static struct ibm_struct kbdlight_driver_data = {
+ .name = "kbdlight",
+ .read = kbdlight_read,
+ .write = kbdlight_write,
+ .exit = kbdlight_exit,
+};
+
+/*************************************************************************
* Light (thinklight) subdriver
*/
@@ -9207,6 +9409,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
},
#endif
{
+ .init = kbdlight_init,
+ .data = &kbdlight_driver_data,
+ },
+ {
.init = light_init,
.data = &light_driver_data,
},