Merge tag 'backport/v3.14.24-ltsi-rc1/phy-rcar-gen2-usb-to-v3.15' into backport/v3...
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / platform / x86 / dell-wmi.c
1 /*
2  * Dell WMI hotkeys
3  *
4  * Copyright (C) 2008 Red Hat <mjg@redhat.com>
5  *
6  * Portions based on wistron_btns.c:
7  * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
8  * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
9  * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
27
28 #include <linux/kernel.h>
29 #include <linux/module.h>
30 #include <linux/init.h>
31 #include <linux/slab.h>
32 #include <linux/types.h>
33 #include <linux/input.h>
34 #include <linux/input/sparse-keymap.h>
35 #include <linux/acpi.h>
36 #include <linux/string.h>
37 #include <linux/dmi.h>
38
39 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
40 MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
41 MODULE_LICENSE("GPL");
42
43 #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
44
45 static int acpi_video;
46
47 MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
48
49 /*
50  * Certain keys are flagged as KE_IGNORE. All of these are either
51  * notifications (rather than requests for change) or are also sent
52  * via the keyboard controller so should not be sent again.
53  */
54
55 static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
56         { KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },
57
58         { KE_KEY, 0xe045, { KEY_PROG1 } },
59         { KE_KEY, 0xe009, { KEY_EJECTCD } },
60
61         /* These also contain the brightness level at offset 6 */
62         { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
63         { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
64
65         /* Battery health status button */
66         { KE_KEY, 0xe007, { KEY_BATTERY } },
67
68         /* This is actually for all radios. Although physically a
69          * switch, the notification does not provide an indication of
70          * state and so it should be reported as a key */
71         { KE_KEY, 0xe008, { KEY_WLAN } },
72
73         /* The next device is at offset 6, the active devices are at
74            offset 8 and the attached devices at offset 10 */
75         { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
76
77         { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } },
78
79         /* BIOS error detected */
80         { KE_IGNORE, 0xe00d, { KEY_RESERVED } },
81
82         /* Wifi Catcher */
83         { KE_KEY, 0xe011, {KEY_PROG2 } },
84
85         /* Ambient light sensor toggle */
86         { KE_IGNORE, 0xe013, { KEY_RESERVED } },
87
88         { KE_IGNORE, 0xe020, { KEY_MUTE } },
89
90         /* Shortcut and audio panel keys */
91         { KE_IGNORE, 0xe025, { KEY_RESERVED } },
92         { KE_IGNORE, 0xe026, { KEY_RESERVED } },
93
94         { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
95         { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
96         { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
97         { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
98         { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
99         { KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
100         { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
101         { KE_IGNORE, 0xe0f7, { KEY_MUTE } },
102         { KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } },
103         { KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } },
104         { KE_END, 0 }
105 };
106
107 static bool dell_new_hk_type;
108
109 struct dell_bios_keymap_entry {
110         u16 scancode;
111         u16 keycode;
112 };
113
114 struct dell_bios_hotkey_table {
115         struct dmi_header header;
116         struct dell_bios_keymap_entry keymap[];
117
118 };
119
120 static const struct dell_bios_hotkey_table *dell_bios_hotkey_table;
121
122 static const u16 bios_to_linux_keycode[256] __initconst = {
123
124         KEY_MEDIA,      KEY_NEXTSONG,   KEY_PLAYPAUSE, KEY_PREVIOUSSONG,
125         KEY_STOPCD,     KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,
126         KEY_WWW,        KEY_UNKNOWN,    KEY_VOLUMEDOWN, KEY_MUTE,
127         KEY_VOLUMEUP,   KEY_UNKNOWN,    KEY_BATTERY,    KEY_EJECTCD,
128         KEY_UNKNOWN,    KEY_SLEEP,      KEY_PROG1, KEY_BRIGHTNESSDOWN,
129         KEY_BRIGHTNESSUP,       KEY_UNKNOWN,    KEY_KBDILLUMTOGGLE,
130         KEY_UNKNOWN,    KEY_SWITCHVIDEOMODE,    KEY_UNKNOWN, KEY_UNKNOWN,
131         KEY_SWITCHVIDEOMODE,    KEY_UNKNOWN,    KEY_UNKNOWN, KEY_PROG2,
132         KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,
133         KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_MICMUTE,
134         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
135         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
136         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
137         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
138         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
139         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
140         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
141         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
142         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
143         0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PROG3
144 };
145
146 static struct input_dev *dell_wmi_input_dev;
147
148 static void dell_wmi_notify(u32 value, void *context)
149 {
150         struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
151         union acpi_object *obj;
152         acpi_status status;
153
154         status = wmi_get_event_data(value, &response);
155         if (status != AE_OK) {
156                 pr_info("bad event status 0x%x\n", status);
157                 return;
158         }
159
160         obj = (union acpi_object *)response.pointer;
161
162         if (obj && obj->type == ACPI_TYPE_BUFFER) {
163                 const struct key_entry *key;
164                 int reported_key;
165                 u16 *buffer_entry = (u16 *)obj->buffer.pointer;
166                 int buffer_size = obj->buffer.length/2;
167
168                 if (buffer_size >= 2 && dell_new_hk_type && buffer_entry[1] != 0x10) {
169                         pr_info("Received unknown WMI event (0x%x)\n",
170                                 buffer_entry[1]);
171                         kfree(obj);
172                         return;
173                 }
174
175                 if (buffer_size >= 3 && (dell_new_hk_type || buffer_entry[1] == 0x0))
176                         reported_key = (int)buffer_entry[2];
177                 else if (buffer_size >= 2)
178                         reported_key = (int)buffer_entry[1] & 0xffff;
179                 else {
180                         pr_info("Received unknown WMI event\n");
181                         kfree(obj);
182                         return;
183                 }
184
185                 key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
186                                                         reported_key);
187                 if (!key) {
188                         pr_info("Unknown key %x pressed\n", reported_key);
189                 } else if ((key->keycode == KEY_BRIGHTNESSUP ||
190                             key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) {
191                         /* Don't report brightness notifications that will also
192                          * come via ACPI */
193                         ;
194                 } else {
195                         sparse_keymap_report_entry(dell_wmi_input_dev, key,
196                                                    1, true);
197                 }
198         }
199         kfree(obj);
200 }
201
202 static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
203 {
204         int hotkey_num = (dell_bios_hotkey_table->header.length - 4) /
205                                 sizeof(struct dell_bios_keymap_entry);
206         struct key_entry *keymap;
207         int i;
208
209         keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL);
210         if (!keymap)
211                 return NULL;
212
213         for (i = 0; i < hotkey_num; i++) {
214                 const struct dell_bios_keymap_entry *bios_entry =
215                                         &dell_bios_hotkey_table->keymap[i];
216                 keymap[i].type = KE_KEY;
217                 keymap[i].code = bios_entry->scancode;
218                 keymap[i].keycode = bios_entry->keycode < 256 ?
219                                     bios_to_linux_keycode[bios_entry->keycode] :
220                                     KEY_RESERVED;
221         }
222
223         keymap[hotkey_num].type = KE_END;
224
225         return keymap;
226 }
227
228 static int __init dell_wmi_input_setup(void)
229 {
230         int err;
231
232         dell_wmi_input_dev = input_allocate_device();
233         if (!dell_wmi_input_dev)
234                 return -ENOMEM;
235
236         dell_wmi_input_dev->name = "Dell WMI hotkeys";
237         dell_wmi_input_dev->phys = "wmi/input0";
238         dell_wmi_input_dev->id.bustype = BUS_HOST;
239
240         if (dell_new_hk_type) {
241                 const struct key_entry *keymap = dell_wmi_prepare_new_keymap();
242                 if (!keymap) {
243                         err = -ENOMEM;
244                         goto err_free_dev;
245                 }
246
247                 err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
248
249                 /*
250                  * Sparse keymap library makes a copy of keymap so we
251                  * don't need the original one that was allocated.
252                  */
253                 kfree(keymap);
254         } else {
255                 err = sparse_keymap_setup(dell_wmi_input_dev,
256                                           dell_wmi_legacy_keymap, NULL);
257         }
258         if (err)
259                 goto err_free_dev;
260
261         err = input_register_device(dell_wmi_input_dev);
262         if (err)
263                 goto err_free_keymap;
264
265         return 0;
266
267  err_free_keymap:
268         sparse_keymap_free(dell_wmi_input_dev);
269  err_free_dev:
270         input_free_device(dell_wmi_input_dev);
271         return err;
272 }
273
274 static void dell_wmi_input_destroy(void)
275 {
276         sparse_keymap_free(dell_wmi_input_dev);
277         input_unregister_device(dell_wmi_input_dev);
278 }
279
280 static void __init find_hk_type(const struct dmi_header *dm, void *dummy)
281 {
282         if (dm->type == 0xb2 && dm->length > 6) {
283                 dell_new_hk_type = true;
284                 dell_bios_hotkey_table =
285                         container_of(dm, struct dell_bios_hotkey_table, header);
286         }
287 }
288
289 static int __init dell_wmi_init(void)
290 {
291         int err;
292         acpi_status status;
293
294         if (!wmi_has_guid(DELL_EVENT_GUID)) {
295                 pr_warn("No known WMI GUID found\n");
296                 return -ENODEV;
297         }
298
299         dmi_walk(find_hk_type, NULL);
300         acpi_video = acpi_video_backlight_support();
301
302         err = dell_wmi_input_setup();
303         if (err)
304                 return err;
305
306         status = wmi_install_notify_handler(DELL_EVENT_GUID,
307                                          dell_wmi_notify, NULL);
308         if (ACPI_FAILURE(status)) {
309                 dell_wmi_input_destroy();
310                 pr_err("Unable to register notify handler - %d\n", status);
311                 return -ENODEV;
312         }
313
314         return 0;
315 }
316 module_init(dell_wmi_init);
317
318 static void __exit dell_wmi_exit(void)
319 {
320         wmi_remove_notify_handler(DELL_EVENT_GUID);
321         dell_wmi_input_destroy();
322 }
323 module_exit(dell_wmi_exit);