]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/platform/x86/intel-vbtn.c
69bc39f8d61dc710e84924a244036fb3a0f6e486
[linux.git] / drivers / platform / x86 / intel-vbtn.c
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/acpi.h>
20 #include <linux/input.h>
21 #include <linux/input/sparse-keymap.h>
22 #include <linux/kernel.h>
23 #include <linux/module.h>
24 #include <linux/platform_device.h>
25 #include <linux/suspend.h>
26
27 /* When NOT in tablet mode, VGBS returns with the flag 0x40 */
28 #define TABLET_MODE_FLAG 0x40
29
30 MODULE_LICENSE("GPL");
31 MODULE_AUTHOR("AceLan Kao");
32
33 static const struct acpi_device_id intel_vbtn_ids[] = {
34         {"INT33D6", 0},
35         {"", 0},
36 };
37
38 /* In theory, these are HID usages. */
39 static const struct key_entry intel_vbtn_keymap[] = {
40         { KE_KEY, 0xC0, { KEY_POWER } },        /* power key press */
41         { KE_IGNORE, 0xC1, { KEY_POWER } },     /* power key release */
42         { KE_KEY, 0xC2, { KEY_LEFTMETA } },             /* 'Windows' key press */
43         { KE_KEY, 0xC3, { KEY_LEFTMETA } },             /* 'Windows' key release */
44         { KE_KEY, 0xC4, { KEY_VOLUMEUP } },             /* volume-up key press */
45         { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } },          /* volume-up key release */
46         { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } },           /* volume-down key press */
47         { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } },        /* volume-down key release */
48         { KE_KEY,    0xC8, { KEY_ROTATE_LOCK_TOGGLE } },        /* rotate-lock key press */
49         { KE_KEY,    0xC9, { KEY_ROTATE_LOCK_TOGGLE } },        /* rotate-lock key release */
50         { KE_SW,     0xCC, { .sw = { SW_TABLET_MODE, 1 } } },   /* Tablet */
51         { KE_SW,     0xCD, { .sw = { SW_TABLET_MODE, 0 } } },   /* Laptop */
52         { KE_END },
53 };
54
55 struct intel_vbtn_priv {
56         struct input_dev *input_dev;
57         bool wakeup_mode;
58 };
59
60 static int intel_vbtn_input_setup(struct platform_device *device)
61 {
62         struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
63         int ret;
64
65         priv->input_dev = devm_input_allocate_device(&device->dev);
66         if (!priv->input_dev)
67                 return -ENOMEM;
68
69         ret = sparse_keymap_setup(priv->input_dev, intel_vbtn_keymap, NULL);
70         if (ret)
71                 return ret;
72
73         priv->input_dev->dev.parent = &device->dev;
74         priv->input_dev->name = "Intel Virtual Button driver";
75         priv->input_dev->id.bustype = BUS_HOST;
76
77         return input_register_device(priv->input_dev);
78 }
79
80 static void notify_handler(acpi_handle handle, u32 event, void *context)
81 {
82         struct platform_device *device = context;
83         struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
84         unsigned int val = !(event & 1); /* Even=press, Odd=release */
85         const struct key_entry *ke_rel;
86         bool autorelease;
87
88         if (priv->wakeup_mode) {
89                 if (sparse_keymap_entry_from_scancode(priv->input_dev, event)) {
90                         pm_wakeup_hard_event(&device->dev);
91                         return;
92                 }
93                 goto out_unknown;
94         }
95
96         /*
97          * Even press events are autorelease if there is no corresponding odd
98          * release event, or if the odd event is KE_IGNORE.
99          */
100         ke_rel = sparse_keymap_entry_from_scancode(priv->input_dev, event | 1);
101         autorelease = val && (!ke_rel || ke_rel->type == KE_IGNORE);
102
103         if (sparse_keymap_report_event(priv->input_dev, event, val, autorelease))
104                 return;
105
106 out_unknown:
107         dev_dbg(&device->dev, "unknown event index 0x%x\n", event);
108 }
109
110 static int intel_vbtn_probe(struct platform_device *device)
111 {
112         struct acpi_buffer vgbs_output = { ACPI_ALLOCATE_BUFFER, NULL };
113         acpi_handle handle = ACPI_HANDLE(&device->dev);
114         struct intel_vbtn_priv *priv;
115         acpi_status status;
116         int err;
117
118         status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
119         if (ACPI_FAILURE(status)) {
120                 dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n");
121                 return -ENODEV;
122         }
123
124         priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
125         if (!priv)
126                 return -ENOMEM;
127         dev_set_drvdata(&device->dev, priv);
128
129         err = intel_vbtn_input_setup(device);
130         if (err) {
131                 pr_err("Failed to setup Intel Virtual Button\n");
132                 return err;
133         }
134
135         /*
136          * VGBS being present and returning something means we have
137          * a tablet mode switch.
138          */
139         status = acpi_evaluate_object(handle, "VGBS", NULL, &vgbs_output);
140         if (ACPI_SUCCESS(status)) {
141                 union acpi_object *obj = vgbs_output.pointer;
142
143                 if (obj && obj->type == ACPI_TYPE_INTEGER) {
144                         int m = !(obj->integer.value & TABLET_MODE_FLAG);
145
146                         input_report_switch(priv->input_dev, SW_TABLET_MODE, m);
147                 }
148         }
149
150         kfree(vgbs_output.pointer);
151
152         status = acpi_install_notify_handler(handle,
153                                              ACPI_DEVICE_NOTIFY,
154                                              notify_handler,
155                                              device);
156         if (ACPI_FAILURE(status))
157                 return -EBUSY;
158
159         device_init_wakeup(&device->dev, true);
160         return 0;
161 }
162
163 static int intel_vbtn_remove(struct platform_device *device)
164 {
165         acpi_handle handle = ACPI_HANDLE(&device->dev);
166
167         acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
168
169         /*
170          * Even if we failed to shut off the event stream, we can still
171          * safely detach from the device.
172          */
173         return 0;
174 }
175
176 static int intel_vbtn_pm_prepare(struct device *dev)
177 {
178         struct intel_vbtn_priv *priv = dev_get_drvdata(dev);
179
180         priv->wakeup_mode = true;
181         return 0;
182 }
183
184 static int intel_vbtn_pm_resume(struct device *dev)
185 {
186         struct intel_vbtn_priv *priv = dev_get_drvdata(dev);
187
188         priv->wakeup_mode = false;
189         return 0;
190 }
191
192 static const struct dev_pm_ops intel_vbtn_pm_ops = {
193         .prepare = intel_vbtn_pm_prepare,
194         .resume = intel_vbtn_pm_resume,
195         .restore = intel_vbtn_pm_resume,
196         .thaw = intel_vbtn_pm_resume,
197 };
198
199 static struct platform_driver intel_vbtn_pl_driver = {
200         .driver = {
201                 .name = "intel-vbtn",
202                 .acpi_match_table = intel_vbtn_ids,
203                 .pm = &intel_vbtn_pm_ops,
204         },
205         .probe = intel_vbtn_probe,
206         .remove = intel_vbtn_remove,
207 };
208 MODULE_DEVICE_TABLE(acpi, intel_vbtn_ids);
209
210 static acpi_status __init
211 check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv)
212 {
213         const struct acpi_device_id *ids = context;
214         struct acpi_device *dev;
215
216         if (acpi_bus_get_device(handle, &dev) != 0)
217                 return AE_OK;
218
219         if (acpi_match_device_ids(dev, ids) == 0)
220                 if (acpi_create_platform_device(dev, NULL))
221                         dev_info(&dev->dev,
222                                  "intel-vbtn: created platform device\n");
223
224         return AE_OK;
225 }
226
227 static int __init intel_vbtn_init(void)
228 {
229         acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
230                             ACPI_UINT32_MAX, check_acpi_dev, NULL,
231                             (void *)intel_vbtn_ids, NULL);
232
233         return platform_driver_register(&intel_vbtn_pl_driver);
234 }
235 module_init(intel_vbtn_init);
236
237 static void __exit intel_vbtn_exit(void)
238 {
239         platform_driver_unregister(&intel_vbtn_pl_driver);
240 }
241 module_exit(intel_vbtn_exit);