aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86
diff options
context:
space:
mode:
authorAceLan Kao <acelan.kao@canonical.com>2016-06-30 21:51:49 -0400
committerDarren Hart <dvhart@linux.intel.com>2016-07-22 16:17:40 -0400
commit332e081225fc2a657aa587c42943d5f5a7dae88b (patch)
tree74254faa2c2c0ca64d41bf4153a485467bfeba5f /drivers/platform/x86
parentdf2294fb64285d2d793cf50c682ac4bfddf56c4c (diff)
intel-vbtn: new driver for Intel Virtual Button
This driver supports power button event in Intel Virtual Button currently. New Dell XPS 13 requires this driver for the power button. This driver is copied/modified from intel-hid.c Most credit goes to the author of intel-hid.c, Alex Hung <alex.hung@canonical.com> Signed-off-by: AceLan Kao <acelan.kao@canonical.com> Signed-off-by: Darren Hart <dvhart@linux.intel.com>
Diffstat (limited to 'drivers/platform/x86')
-rw-r--r--drivers/platform/x86/Kconfig12
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/intel-vbtn.c188
3 files changed, 201 insertions, 0 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index b0b98e413343..bdf027716f3e 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -774,6 +774,18 @@ config INTEL_HID_EVENT
774 To compile this driver as a module, choose M here: the module will 774 To compile this driver as a module, choose M here: the module will
775 be called intel_hid. 775 be called intel_hid.
776 776
777config INTEL_VBTN
778 tristate "INTEL VIRTUAL BUTTON"
779 depends on ACPI
780 depends on INPUT
781 select INPUT_SPARSEKMAP
782 help
783 This driver provides support for the Intel Virtual Button interface.
784 Some laptops require this driver for power button support.
785
786 To compile this driver as a module, choose M here: the module will
787 be called intel_vbtn.
788
777config INTEL_SCU_IPC 789config INTEL_SCU_IPC
778 bool "Intel SCU IPC Support" 790 bool "Intel SCU IPC Support"
779 depends on X86_INTEL_MID 791 depends on X86_INTEL_MID
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 9b11b4073e03..2efa86d2a1a7 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
44obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o 44obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o
45obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o 45obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o
46obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o 46obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o
47obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o
47obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o 48obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
48obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o 49obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
49obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o 50obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c
new file mode 100644
index 000000000000..146d02f8c9bc
--- /dev/null
+++ b/drivers/platform/x86/intel-vbtn.c
@@ -0,0 +1,188 @@
1/*
2 * Intel Virtual Button driver for Windows 8.1+
3 *
4 * Copyright (C) 2016 AceLan Kao <acelan.kao@canonical.com>
5 * Copyright (C) 2016 Alex Hung <alex.hung@canonical.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 */
18
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/input.h>
23#include <linux/platform_device.h>
24#include <linux/input/sparse-keymap.h>
25#include <linux/acpi.h>
26#include <acpi/acpi_bus.h>
27
28MODULE_LICENSE("GPL");
29MODULE_AUTHOR("AceLan Kao");
30
31static const struct acpi_device_id intel_vbtn_ids[] = {
32 {"INT33D6", 0},
33 {"", 0},
34};
35
36/* In theory, these are HID usages. */
37static const struct key_entry intel_vbtn_keymap[] = {
38 { KE_IGNORE, 0xC0, { KEY_POWER } }, /* power key press */
39 { KE_KEY, 0xC1, { KEY_POWER } }, /* power key release */
40 { KE_END },
41};
42
43struct intel_vbtn_priv {
44 struct input_dev *input_dev;
45};
46
47static int intel_vbtn_input_setup(struct platform_device *device)
48{
49 struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
50 int ret;
51
52 priv->input_dev = input_allocate_device();
53 if (!priv->input_dev)
54 return -ENOMEM;
55
56 ret = sparse_keymap_setup(priv->input_dev, intel_vbtn_keymap, NULL);
57 if (ret)
58 goto err_free_device;
59
60 priv->input_dev->dev.parent = &device->dev;
61 priv->input_dev->name = "Intel Virtual Button driver";
62 priv->input_dev->id.bustype = BUS_HOST;
63
64 ret = input_register_device(priv->input_dev);
65 if (ret)
66 goto err_free_device;
67
68 return 0;
69
70err_free_device:
71 input_free_device(priv->input_dev);
72 return ret;
73}
74
75static void intel_vbtn_input_destroy(struct platform_device *device)
76{
77 struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
78
79 input_unregister_device(priv->input_dev);
80}
81
82static void notify_handler(acpi_handle handle, u32 event, void *context)
83{
84 struct platform_device *device = context;
85 struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
86
87 if (!sparse_keymap_report_event(priv->input_dev, event, 1, true))
88 dev_info(&device->dev, "unknown event index 0x%x\n",
89 event);
90}
91
92static int intel_vbtn_probe(struct platform_device *device)
93{
94 acpi_handle handle = ACPI_HANDLE(&device->dev);
95 struct intel_vbtn_priv *priv;
96 acpi_status status;
97 int err;
98
99 status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
100 if (!ACPI_SUCCESS(status)) {
101 dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n");
102 return -ENODEV;
103 }
104
105 priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
106 if (!priv)
107 return -ENOMEM;
108 dev_set_drvdata(&device->dev, priv);
109
110 err = intel_vbtn_input_setup(device);
111 if (err) {
112 pr_err("Failed to setup Intel Virtual Button\n");
113 return err;
114 }
115
116 status = acpi_install_notify_handler(handle,
117 ACPI_DEVICE_NOTIFY,
118 notify_handler,
119 device);
120 if (ACPI_FAILURE(status)) {
121 err = -EBUSY;
122 goto err_remove_input;
123 }
124
125 return 0;
126
127err_remove_input:
128 intel_vbtn_input_destroy(device);
129
130 return err;
131}
132
133static int intel_vbtn_remove(struct platform_device *device)
134{
135 acpi_handle handle = ACPI_HANDLE(&device->dev);
136
137 intel_vbtn_input_destroy(device);
138 acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
139
140 /*
141 * Even if we failed to shut off the event stream, we can still
142 * safely detach from the device.
143 */
144 return 0;
145}
146
147static struct platform_driver intel_vbtn_pl_driver = {
148 .driver = {
149 .name = "intel-vbtn",
150 .acpi_match_table = intel_vbtn_ids,
151 },
152 .probe = intel_vbtn_probe,
153 .remove = intel_vbtn_remove,
154};
155MODULE_DEVICE_TABLE(acpi, intel_vbtn_ids);
156
157static acpi_status __init
158check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv)
159{
160 const struct acpi_device_id *ids = context;
161 struct acpi_device *dev;
162
163 if (acpi_bus_get_device(handle, &dev) != 0)
164 return AE_OK;
165
166 if (acpi_match_device_ids(dev, ids) == 0)
167 if (acpi_create_platform_device(dev))
168 dev_info(&dev->dev,
169 "intel-vbtn: created platform device\n");
170
171 return AE_OK;
172}
173
174static int __init intel_vbtn_init(void)
175{
176 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
177 ACPI_UINT32_MAX, check_acpi_dev, NULL,
178 (void *)intel_vbtn_ids, NULL);
179
180 return platform_driver_register(&intel_vbtn_pl_driver);
181}
182module_init(intel_vbtn_init);
183
184static void __exit intel_vbtn_exit(void)
185{
186 platform_driver_unregister(&intel_vbtn_pl_driver);
187}
188module_exit(intel_vbtn_exit);