eeepc-wmi: Add cpufv sysfs interface
[pandora-kernel.git] / drivers / platform / x86 / eeepc-wmi.c
1 /*
2  * Eee PC WMI hotkey driver
3  *
4  * Copyright(C) 2010 Intel Corporation.
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/types.h>
32 #include <linux/slab.h>
33 #include <linux/input.h>
34 #include <linux/input/sparse-keymap.h>
35 #include <linux/fb.h>
36 #include <linux/backlight.h>
37 #include <linux/platform_device.h>
38 #include <acpi/acpi_bus.h>
39 #include <acpi/acpi_drivers.h>
40
41 #define EEEPC_WMI_FILE  "eeepc-wmi"
42
43 MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>");
44 MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver");
45 MODULE_LICENSE("GPL");
46
47 #define EEEPC_WMI_EVENT_GUID    "ABBC0F72-8EA1-11D1-00A0-C90629100000"
48 #define EEEPC_WMI_MGMT_GUID     "97845ED0-4E6D-11DE-8A39-0800200C9A66"
49
50 MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID);
51 MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);
52
53 #define NOTIFY_BRNUP_MIN        0x11
54 #define NOTIFY_BRNUP_MAX        0x1f
55 #define NOTIFY_BRNDOWN_MIN      0x20
56 #define NOTIFY_BRNDOWN_MAX      0x2e
57
58 #define EEEPC_WMI_METHODID_DEVS 0x53564544
59 #define EEEPC_WMI_METHODID_DSTS 0x53544344
60 #define EEEPC_WMI_METHODID_CFVS 0x53564643
61
62 #define EEEPC_WMI_DEVID_BACKLIGHT       0x00050012
63
64 static const struct key_entry eeepc_wmi_keymap[] = {
65         /* Sleep already handled via generic ACPI code */
66         { KE_KEY, 0x5d, { KEY_WLAN } },
67         { KE_KEY, 0x32, { KEY_MUTE } },
68         { KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
69         { KE_KEY, 0x30, { KEY_VOLUMEUP } },
70         { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } },
71         { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } },
72         { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
73         { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */
74         { KE_KEY, 0xe1, { KEY_F14 } },
75         { KE_KEY, 0xe9, { KEY_DISPLAY_OFF } },
76         { KE_KEY, 0xe0, { KEY_PROG1 } },
77         { KE_KEY, 0x5c, { KEY_F15 } },
78         { KE_END, 0},
79 };
80
81 struct bios_args {
82         u32     dev_id;
83         u32     ctrl_param;
84 };
85
86 struct eeepc_wmi {
87         struct input_dev *inputdev;
88         struct backlight_device *backlight_device;
89 };
90
91 static struct platform_device *platform_device;
92
93 static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc)
94 {
95         int err;
96
97         eeepc->inputdev = input_allocate_device();
98         if (!eeepc->inputdev)
99                 return -ENOMEM;
100
101         eeepc->inputdev->name = "Eee PC WMI hotkeys";
102         eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0";
103         eeepc->inputdev->id.bustype = BUS_HOST;
104         eeepc->inputdev->dev.parent = &platform_device->dev;
105
106         err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL);
107         if (err)
108                 goto err_free_dev;
109
110         err = input_register_device(eeepc->inputdev);
111         if (err)
112                 goto err_free_keymap;
113
114         return 0;
115
116 err_free_keymap:
117         sparse_keymap_free(eeepc->inputdev);
118 err_free_dev:
119         input_free_device(eeepc->inputdev);
120         return err;
121 }
122
123 static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc)
124 {
125         if (eeepc->inputdev) {
126                 sparse_keymap_free(eeepc->inputdev);
127                 input_unregister_device(eeepc->inputdev);
128         }
129
130         eeepc->inputdev = NULL;
131 }
132
133 static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param)
134 {
135         struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id };
136         struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
137         union acpi_object *obj;
138         acpi_status status;
139         u32 tmp;
140
141         status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
142                         1, EEEPC_WMI_METHODID_DSTS, &input, &output);
143
144         if (ACPI_FAILURE(status))
145                 return status;
146
147         obj = (union acpi_object *)output.pointer;
148         if (obj && obj->type == ACPI_TYPE_INTEGER)
149                 tmp = (u32)obj->integer.value;
150         else
151                 tmp = 0;
152
153         if (ctrl_param)
154                 *ctrl_param = tmp;
155
156         kfree(obj);
157
158         return status;
159
160 }
161
162 static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param)
163 {
164         struct bios_args args = {
165                 .dev_id = dev_id,
166                 .ctrl_param = ctrl_param,
167         };
168         struct acpi_buffer input = { (acpi_size)sizeof(args), &args };
169         acpi_status status;
170
171         status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
172                         1, EEEPC_WMI_METHODID_DEVS, &input, NULL);
173
174         return status;
175 }
176
177 static int read_brightness(struct backlight_device *bd)
178 {
179         static u32 ctrl_param;
180         acpi_status status;
181
182         status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &ctrl_param);
183
184         if (ACPI_FAILURE(status))
185                 return -1;
186         else
187                 return ctrl_param & 0xFF;
188 }
189
190 static int update_bl_status(struct backlight_device *bd)
191 {
192
193         static u32 ctrl_param;
194         acpi_status status;
195
196         ctrl_param = bd->props.brightness;
197
198         status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, ctrl_param);
199
200         if (ACPI_FAILURE(status))
201                 return -1;
202         else
203                 return 0;
204 }
205
206 static const struct backlight_ops eeepc_wmi_bl_ops = {
207         .get_brightness = read_brightness,
208         .update_status = update_bl_status,
209 };
210
211 static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code)
212 {
213         struct backlight_device *bd = eeepc->backlight_device;
214         int old = bd->props.brightness;
215         int new = old;
216
217         if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
218                 new = code - NOTIFY_BRNUP_MIN + 1;
219         else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
220                 new = code - NOTIFY_BRNDOWN_MIN;
221
222         bd->props.brightness = new;
223         backlight_update_status(bd);
224         backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
225
226         return old;
227 }
228
229 static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc)
230 {
231         struct backlight_device *bd;
232         struct backlight_properties props;
233
234         memset(&props, 0, sizeof(struct backlight_properties));
235         props.max_brightness = 15;
236         bd = backlight_device_register(EEEPC_WMI_FILE,
237                                        &platform_device->dev, eeepc,
238                                        &eeepc_wmi_bl_ops, &props);
239         if (IS_ERR(bd)) {
240                 pr_err("Could not register backlight device\n");
241                 return PTR_ERR(bd);
242         }
243
244         eeepc->backlight_device = bd;
245
246         bd->props.brightness = read_brightness(bd);
247         bd->props.power = FB_BLANK_UNBLANK;
248         backlight_update_status(bd);
249
250         return 0;
251 }
252
253 static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc)
254 {
255         if (eeepc->backlight_device)
256                 backlight_device_unregister(eeepc->backlight_device);
257
258         eeepc->backlight_device = NULL;
259 }
260
261 static void eeepc_wmi_notify(u32 value, void *context)
262 {
263         struct eeepc_wmi *eeepc = context;
264         struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
265         union acpi_object *obj;
266         acpi_status status;
267         int code;
268         int orig_code;
269
270         status = wmi_get_event_data(value, &response);
271         if (status != AE_OK) {
272                 pr_err("bad event status 0x%x\n", status);
273                 return;
274         }
275
276         obj = (union acpi_object *)response.pointer;
277
278         if (obj && obj->type == ACPI_TYPE_INTEGER) {
279                 code = obj->integer.value;
280                 orig_code = code;
281
282                 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
283                         code = NOTIFY_BRNUP_MIN;
284                 else if (code >= NOTIFY_BRNDOWN_MIN &&
285                          code <= NOTIFY_BRNDOWN_MAX)
286                         code = NOTIFY_BRNDOWN_MIN;
287
288                 if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) {
289                         if (!acpi_video_backlight_support())
290                                 eeepc_wmi_backlight_notify(eeepc, orig_code);
291                 }
292
293                 if (!sparse_keymap_report_event(eeepc->inputdev,
294                                                 code, 1, true))
295                         pr_info("Unknown key %x pressed\n", code);
296         }
297
298         kfree(obj);
299 }
300
301 static int store_cpufv(struct device *dev, struct device_attribute *attr,
302                        const char *buf, size_t count)
303 {
304         int value;
305         struct acpi_buffer input = { (acpi_size)sizeof(value), &value };
306         acpi_status status;
307
308         if (!count || sscanf(buf, "%i", &value) != 1)
309                 return -EINVAL;
310         if (value < 0 || value > 2)
311                 return -EINVAL;
312
313         status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
314                                      1, EEEPC_WMI_METHODID_CFVS, &input, NULL);
315
316         if (ACPI_FAILURE(status))
317                 return -EIO;
318         else
319                 return count;
320 }
321
322 static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
323
324 static void eeepc_wmi_sysfs_exit(struct platform_device *device)
325 {
326         device_remove_file(&device->dev, &dev_attr_cpufv);
327 }
328
329 static int eeepc_wmi_sysfs_init(struct platform_device *device)
330 {
331         int retval = -ENOMEM;
332
333         retval = device_create_file(&device->dev, &dev_attr_cpufv);
334         if (retval)
335                 goto error_sysfs;
336
337         return 0;
338
339 error_sysfs:
340         eeepc_wmi_sysfs_exit(platform_device);
341         return retval;
342 }
343
344 static int __devinit eeepc_wmi_platform_probe(struct platform_device *device)
345 {
346         struct eeepc_wmi *eeepc;
347         int err;
348         acpi_status status;
349
350         eeepc = platform_get_drvdata(device);
351
352         err = eeepc_wmi_input_init(eeepc);
353         if (err)
354                 goto error_input;
355
356         if (!acpi_video_backlight_support()) {
357                 err = eeepc_wmi_backlight_init(eeepc);
358                 if (err)
359                         goto error_backlight;
360         } else
361                 pr_info("Backlight controlled by ACPI video driver\n");
362
363         status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID,
364                                         eeepc_wmi_notify, eeepc);
365         if (ACPI_FAILURE(status)) {
366                 pr_err("Unable to register notify handler - %d\n",
367                         status);
368                 err = -ENODEV;
369                 goto error_wmi;
370         }
371
372         return 0;
373
374 error_wmi:
375         eeepc_wmi_backlight_exit(eeepc);
376 error_backlight:
377         eeepc_wmi_input_exit(eeepc);
378 error_input:
379         return err;
380 }
381
382 static int __devexit eeepc_wmi_platform_remove(struct platform_device *device)
383 {
384         struct eeepc_wmi *eeepc;
385
386         eeepc = platform_get_drvdata(device);
387         wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
388         eeepc_wmi_backlight_exit(eeepc);
389         eeepc_wmi_input_exit(eeepc);
390
391         return 0;
392 }
393
394 static struct platform_driver platform_driver = {
395         .driver = {
396                 .name = EEEPC_WMI_FILE,
397                 .owner = THIS_MODULE,
398         },
399         .probe = eeepc_wmi_platform_probe,
400         .remove = __devexit_p(eeepc_wmi_platform_remove),
401 };
402
403 static int __init eeepc_wmi_init(void)
404 {
405         struct eeepc_wmi *eeepc;
406         int err;
407
408         if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) ||
409             !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) {
410                 pr_warning("No known WMI GUID found\n");
411                 return -ENODEV;
412         }
413
414         eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL);
415         if (!eeepc)
416                 return -ENOMEM;
417
418         platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1);
419         if (!platform_device) {
420                 pr_warning("Unable to allocate platform device\n");
421                 err = -ENOMEM;
422                 goto fail_platform;
423         }
424
425         err = platform_device_add(platform_device);
426         if (err) {
427                 pr_warning("Unable to add platform device\n");
428                 goto put_dev;
429         }
430
431         platform_set_drvdata(platform_device, eeepc);
432
433         err = platform_driver_register(&platform_driver);
434         if (err) {
435                 pr_warning("Unable to register platform driver\n");
436                 goto del_dev;
437         }
438
439         err = eeepc_wmi_sysfs_init(platform_device);
440         if (err)
441                 goto del_sysfs;
442
443         return 0;
444
445 del_sysfs:
446         eeepc_wmi_sysfs_exit(platform_device);
447 del_dev:
448         platform_device_del(platform_device);
449 put_dev:
450         platform_device_put(platform_device);
451 fail_platform:
452         kfree(eeepc);
453
454         return err;
455 }
456
457 static void __exit eeepc_wmi_exit(void)
458 {
459         struct eeepc_wmi *eeepc;
460
461         eeepc_wmi_sysfs_exit(platform_device);
462         eeepc = platform_get_drvdata(platform_device);
463         platform_driver_unregister(&platform_driver);
464         platform_device_unregister(platform_device);
465         kfree(eeepc);
466 }
467
468 module_init(eeepc_wmi_init);
469 module_exit(eeepc_wmi_exit);