GNU Linux-libre 4.9.328-gnu1
[releases.git] / drivers / platform / x86 / msi-wmi.c
1 /*
2  * MSI WMI hotkeys
3  *
4  * Copyright (C) 2009 Novell <trenn@suse.de>
5  *
6  * Most stuff taken over from hp-wmi
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24
25 #include <linux/kernel.h>
26 #include <linux/input.h>
27 #include <linux/input/sparse-keymap.h>
28 #include <linux/acpi.h>
29 #include <linux/backlight.h>
30 #include <linux/slab.h>
31 #include <linux/module.h>
32 #include <acpi/video.h>
33
34 MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
35 MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver");
36 MODULE_LICENSE("GPL");
37
38 #define DRV_NAME "msi-wmi"
39
40 #define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45"
41 #define MSIWMI_MSI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"
42 #define MSIWMI_WIND_EVENT_GUID "5B3CC38A-40D9-7245-8AE6-1145B751BE3F"
43
44 MODULE_ALIAS("wmi:" MSIWMI_BIOS_GUID);
45 MODULE_ALIAS("wmi:" MSIWMI_MSI_EVENT_GUID);
46 MODULE_ALIAS("wmi:" MSIWMI_WIND_EVENT_GUID);
47
48 enum msi_scancodes {
49         /* Generic MSI keys (not present on MSI Wind) */
50         MSI_KEY_BRIGHTNESSUP    = 0xD0,
51         MSI_KEY_BRIGHTNESSDOWN,
52         MSI_KEY_VOLUMEUP,
53         MSI_KEY_VOLUMEDOWN,
54         MSI_KEY_MUTE,
55         /* MSI Wind keys */
56         WIND_KEY_TOUCHPAD       = 0x08, /* Fn+F3 touchpad toggle */
57         WIND_KEY_BLUETOOTH      = 0x56, /* Fn+F11 Bluetooth toggle */
58         WIND_KEY_CAMERA,                /* Fn+F6 webcam toggle */
59         WIND_KEY_WLAN           = 0x5f, /* Fn+F11 Wi-Fi toggle */
60         WIND_KEY_TURBO,                 /* Fn+F10 turbo mode toggle */
61         WIND_KEY_ECO            = 0x69, /* Fn+F10 ECO mode toggle */
62 };
63 static struct key_entry msi_wmi_keymap[] = {
64         { KE_KEY, MSI_KEY_BRIGHTNESSUP,         {KEY_BRIGHTNESSUP} },
65         { KE_KEY, MSI_KEY_BRIGHTNESSDOWN,       {KEY_BRIGHTNESSDOWN} },
66         { KE_KEY, MSI_KEY_VOLUMEUP,             {KEY_VOLUMEUP} },
67         { KE_KEY, MSI_KEY_VOLUMEDOWN,           {KEY_VOLUMEDOWN} },
68         { KE_KEY, MSI_KEY_MUTE,                 {KEY_MUTE} },
69
70         /* These keys work without WMI. Ignore them to avoid double keycodes */
71         { KE_IGNORE, WIND_KEY_TOUCHPAD,         {KEY_TOUCHPAD_TOGGLE} },
72         { KE_IGNORE, WIND_KEY_BLUETOOTH,        {KEY_BLUETOOTH} },
73         { KE_IGNORE, WIND_KEY_CAMERA,           {KEY_CAMERA} },
74         { KE_IGNORE, WIND_KEY_WLAN,             {KEY_WLAN} },
75
76         /* These are unknown WMI events found on MSI Wind */
77         { KE_IGNORE, 0x00 },
78         { KE_IGNORE, 0x62 },
79         { KE_IGNORE, 0x63 },
80
81         /* These are MSI Wind keys that should be handled via WMI */
82         { KE_KEY, WIND_KEY_TURBO,               {KEY_PROG1} },
83         { KE_KEY, WIND_KEY_ECO,                 {KEY_PROG2} },
84
85         { KE_END, 0 }
86 };
87
88 static ktime_t last_pressed;
89
90 static const struct {
91         const char *guid;
92         bool quirk_last_pressed;
93 } *event_wmi, event_wmis[] = {
94         { MSIWMI_MSI_EVENT_GUID, true },
95         { MSIWMI_WIND_EVENT_GUID, false },
96 };
97
98 static struct backlight_device *backlight;
99
100 static int backlight_map[] = { 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF };
101
102 static struct input_dev *msi_wmi_input_dev;
103
104 static int msi_wmi_query_block(int instance, int *ret)
105 {
106         acpi_status status;
107         union acpi_object *obj;
108
109         struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
110
111         status = wmi_query_block(MSIWMI_BIOS_GUID, instance, &output);
112
113         obj = output.pointer;
114
115         if (!obj || obj->type != ACPI_TYPE_INTEGER) {
116                 if (obj) {
117                         pr_err("query block returned object "
118                                "type: %d - buffer length:%d\n", obj->type,
119                                obj->type == ACPI_TYPE_BUFFER ?
120                                obj->buffer.length : 0);
121                 }
122                 kfree(obj);
123                 return -EINVAL;
124         }
125         *ret = obj->integer.value;
126         kfree(obj);
127         return 0;
128 }
129
130 static int msi_wmi_set_block(int instance, int value)
131 {
132         acpi_status status;
133
134         struct acpi_buffer input = { sizeof(int), &value };
135
136         pr_debug("Going to set block of instance: %d - value: %d\n",
137                  instance, value);
138
139         status = wmi_set_block(MSIWMI_BIOS_GUID, instance, &input);
140
141         return ACPI_SUCCESS(status) ? 0 : 1;
142 }
143
144 static int bl_get(struct backlight_device *bd)
145 {
146         int level, err, ret;
147
148         /* Instance 1 is "get backlight", cmp with DSDT */
149         err = msi_wmi_query_block(1, &ret);
150         if (err) {
151                 pr_err("Could not query backlight: %d\n", err);
152                 return -EINVAL;
153         }
154         pr_debug("Get: Query block returned: %d\n", ret);
155         for (level = 0; level < ARRAY_SIZE(backlight_map); level++) {
156                 if (backlight_map[level] == ret) {
157                         pr_debug("Current backlight level: 0x%X - index: %d\n",
158                                  backlight_map[level], level);
159                         break;
160                 }
161         }
162         if (level == ARRAY_SIZE(backlight_map)) {
163                 pr_err("get: Invalid brightness value: 0x%X\n", ret);
164                 return -EINVAL;
165         }
166         return level;
167 }
168
169 static int bl_set_status(struct backlight_device *bd)
170 {
171         int bright = bd->props.brightness;
172         if (bright >= ARRAY_SIZE(backlight_map) || bright < 0)
173                 return -EINVAL;
174
175         /* Instance 0 is "set backlight" */
176         return msi_wmi_set_block(0, backlight_map[bright]);
177 }
178
179 static const struct backlight_ops msi_backlight_ops = {
180         .get_brightness = bl_get,
181         .update_status  = bl_set_status,
182 };
183
184 static void msi_wmi_notify(u32 value, void *context)
185 {
186         struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
187         static struct key_entry *key;
188         union acpi_object *obj;
189         acpi_status status;
190
191         status = wmi_get_event_data(value, &response);
192         if (status != AE_OK) {
193                 pr_info("bad event status 0x%x\n", status);
194                 return;
195         }
196
197         obj = (union acpi_object *)response.pointer;
198
199         if (obj && obj->type == ACPI_TYPE_INTEGER) {
200                 int eventcode = obj->integer.value;
201                 pr_debug("Eventcode: 0x%x\n", eventcode);
202                 key = sparse_keymap_entry_from_scancode(msi_wmi_input_dev,
203                                 eventcode);
204                 if (!key) {
205                         pr_info("Unknown key pressed - %x\n", eventcode);
206                         goto msi_wmi_notify_exit;
207                 }
208
209                 if (event_wmi->quirk_last_pressed) {
210                         ktime_t cur = ktime_get_real();
211                         ktime_t diff = ktime_sub(cur, last_pressed);
212                         /* Ignore event if any event happened in a 50 ms
213                            timeframe -> Key press may result in 10-20 GPEs */
214                         if (ktime_to_us(diff) < 1000 * 50) {
215                                 pr_debug("Suppressed key event 0x%X - "
216                                          "Last press was %lld us ago\n",
217                                          key->code, ktime_to_us(diff));
218                                 goto msi_wmi_notify_exit;
219                         }
220                         last_pressed = cur;
221                 }
222
223                 if (key->type == KE_KEY &&
224                 /* Brightness is served via acpi video driver */
225                 (backlight ||
226                 (key->code != MSI_KEY_BRIGHTNESSUP &&
227                 key->code != MSI_KEY_BRIGHTNESSDOWN))) {
228                         pr_debug("Send key: 0x%X - Input layer keycode: %d\n",
229                                  key->code, key->keycode);
230                         sparse_keymap_report_entry(msi_wmi_input_dev, key, 1,
231                                                    true);
232                 }
233         } else
234                 pr_info("Unknown event received\n");
235
236 msi_wmi_notify_exit:
237         kfree(response.pointer);
238 }
239
240 static int __init msi_wmi_backlight_setup(void)
241 {
242         int err;
243         struct backlight_properties props;
244
245         memset(&props, 0, sizeof(struct backlight_properties));
246         props.type = BACKLIGHT_PLATFORM;
247         props.max_brightness = ARRAY_SIZE(backlight_map) - 1;
248         backlight = backlight_device_register(DRV_NAME, NULL, NULL,
249                                               &msi_backlight_ops,
250                                               &props);
251         if (IS_ERR(backlight))
252                 return PTR_ERR(backlight);
253
254         err = bl_get(NULL);
255         if (err < 0) {
256                 backlight_device_unregister(backlight);
257                 return err;
258         }
259
260         backlight->props.brightness = err;
261
262         return 0;
263 }
264
265 static int __init msi_wmi_input_setup(void)
266 {
267         int err;
268
269         msi_wmi_input_dev = input_allocate_device();
270         if (!msi_wmi_input_dev)
271                 return -ENOMEM;
272
273         msi_wmi_input_dev->name = "MSI WMI hotkeys";
274         msi_wmi_input_dev->phys = "wmi/input0";
275         msi_wmi_input_dev->id.bustype = BUS_HOST;
276
277         err = sparse_keymap_setup(msi_wmi_input_dev, msi_wmi_keymap, NULL);
278         if (err)
279                 goto err_free_dev;
280
281         err = input_register_device(msi_wmi_input_dev);
282
283         if (err)
284                 goto err_free_keymap;
285
286         last_pressed = ktime_set(0, 0);
287
288         return 0;
289
290 err_free_keymap:
291         sparse_keymap_free(msi_wmi_input_dev);
292 err_free_dev:
293         input_free_device(msi_wmi_input_dev);
294         return err;
295 }
296
297 static int __init msi_wmi_init(void)
298 {
299         int err;
300         int i;
301
302         for (i = 0; i < ARRAY_SIZE(event_wmis); i++) {
303                 if (!wmi_has_guid(event_wmis[i].guid))
304                         continue;
305
306                 err = msi_wmi_input_setup();
307                 if (err) {
308                         pr_err("Unable to setup input device\n");
309                         return err;
310                 }
311
312                 err = wmi_install_notify_handler(event_wmis[i].guid,
313                         msi_wmi_notify, NULL);
314                 if (ACPI_FAILURE(err)) {
315                         pr_err("Unable to setup WMI notify handler\n");
316                         goto err_free_input;
317                 }
318
319                 pr_debug("Event handler installed\n");
320                 event_wmi = &event_wmis[i];
321                 break;
322         }
323
324         if (wmi_has_guid(MSIWMI_BIOS_GUID) &&
325             acpi_video_get_backlight_type() == acpi_backlight_vendor) {
326                 err = msi_wmi_backlight_setup();
327                 if (err) {
328                         pr_err("Unable to setup backlight device\n");
329                         goto err_uninstall_handler;
330                 }
331                 pr_debug("Backlight device created\n");
332         }
333
334         if (!event_wmi && !backlight) {
335                 pr_err("This machine doesn't have neither MSI-hotkeys nor backlight through WMI\n");
336                 return -ENODEV;
337         }
338
339         return 0;
340
341 err_uninstall_handler:
342         if (event_wmi)
343                 wmi_remove_notify_handler(event_wmi->guid);
344 err_free_input:
345         if (event_wmi) {
346                 sparse_keymap_free(msi_wmi_input_dev);
347                 input_unregister_device(msi_wmi_input_dev);
348         }
349         return err;
350 }
351
352 static void __exit msi_wmi_exit(void)
353 {
354         if (event_wmi) {
355                 wmi_remove_notify_handler(event_wmi->guid);
356                 sparse_keymap_free(msi_wmi_input_dev);
357                 input_unregister_device(msi_wmi_input_dev);
358         }
359         backlight_device_unregister(backlight);
360 }
361
362 module_init(msi_wmi_init);
363 module_exit(msi_wmi_exit);