6670ed8f9e5b4c849f4f8b853b4fda41069d2a05
[platform/kernel/linux-arm64.git] / drivers / platform / x86 / classmate-laptop.c
1 /*
2  *  Copyright (C) 2009  Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19
20 #include <linux/init.h>
21 #include <linux/module.h>
22 #include <linux/workqueue.h>
23 #include <acpi/acpi_drivers.h>
24 #include <linux/backlight.h>
25 #include <linux/input.h>
26
27 MODULE_LICENSE("GPL");
28
29
30 struct cmpc_accel {
31         int sensitivity;
32 };
33
34 #define CMPC_ACCEL_SENSITIVITY_DEFAULT          5
35
36
37 #define CMPC_ACCEL_HID          "ACCE0000"
38 #define CMPC_TABLET_HID         "TBLT0000"
39 #define CMPC_BL_HID             "IPML200"
40 #define CMPC_KEYS_HID           "FnBT0000"
41
42 /*
43  * Generic input device code.
44  */
45
46 typedef void (*input_device_init)(struct input_dev *dev);
47
48 static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,
49                                        input_device_init idev_init)
50 {
51         struct input_dev *inputdev;
52         int error;
53
54         inputdev = input_allocate_device();
55         if (!inputdev)
56                 return -ENOMEM;
57         inputdev->name = name;
58         inputdev->dev.parent = &acpi->dev;
59         idev_init(inputdev);
60         error = input_register_device(inputdev);
61         if (error) {
62                 input_free_device(inputdev);
63                 return error;
64         }
65         dev_set_drvdata(&acpi->dev, inputdev);
66         return 0;
67 }
68
69 static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
70 {
71         struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
72         input_unregister_device(inputdev);
73         return 0;
74 }
75
76 /*
77  * Accelerometer code.
78  */
79 static acpi_status cmpc_start_accel(acpi_handle handle)
80 {
81         union acpi_object param[2];
82         struct acpi_object_list input;
83         acpi_status status;
84
85         param[0].type = ACPI_TYPE_INTEGER;
86         param[0].integer.value = 0x3;
87         param[1].type = ACPI_TYPE_INTEGER;
88         input.count = 2;
89         input.pointer = param;
90         status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
91         return status;
92 }
93
94 static acpi_status cmpc_stop_accel(acpi_handle handle)
95 {
96         union acpi_object param[2];
97         struct acpi_object_list input;
98         acpi_status status;
99
100         param[0].type = ACPI_TYPE_INTEGER;
101         param[0].integer.value = 0x4;
102         param[1].type = ACPI_TYPE_INTEGER;
103         input.count = 2;
104         input.pointer = param;
105         status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
106         return status;
107 }
108
109 static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val)
110 {
111         union acpi_object param[2];
112         struct acpi_object_list input;
113
114         param[0].type = ACPI_TYPE_INTEGER;
115         param[0].integer.value = 0x02;
116         param[1].type = ACPI_TYPE_INTEGER;
117         param[1].integer.value = val;
118         input.count = 2;
119         input.pointer = param;
120         return acpi_evaluate_object(handle, "ACMD", &input, NULL);
121 }
122
123 static acpi_status cmpc_get_accel(acpi_handle handle,
124                                   unsigned char *x,
125                                   unsigned char *y,
126                                   unsigned char *z)
127 {
128         union acpi_object param[2];
129         struct acpi_object_list input;
130         struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, 0 };
131         unsigned char *locs;
132         acpi_status status;
133
134         param[0].type = ACPI_TYPE_INTEGER;
135         param[0].integer.value = 0x01;
136         param[1].type = ACPI_TYPE_INTEGER;
137         input.count = 2;
138         input.pointer = param;
139         status = acpi_evaluate_object(handle, "ACMD", &input, &output);
140         if (ACPI_SUCCESS(status)) {
141                 union acpi_object *obj;
142                 obj = output.pointer;
143                 locs = obj->buffer.pointer;
144                 *x = locs[0];
145                 *y = locs[1];
146                 *z = locs[2];
147                 kfree(output.pointer);
148         }
149         return status;
150 }
151
152 static void cmpc_accel_handler(struct acpi_device *dev, u32 event)
153 {
154         if (event == 0x81) {
155                 unsigned char x, y, z;
156                 acpi_status status;
157
158                 status = cmpc_get_accel(dev->handle, &x, &y, &z);
159                 if (ACPI_SUCCESS(status)) {
160                         struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
161
162                         input_report_abs(inputdev, ABS_X, x);
163                         input_report_abs(inputdev, ABS_Y, y);
164                         input_report_abs(inputdev, ABS_Z, z);
165                         input_sync(inputdev);
166                 }
167         }
168 }
169
170 static ssize_t cmpc_accel_sensitivity_show(struct device *dev,
171                                            struct device_attribute *attr,
172                                            char *buf)
173 {
174         struct acpi_device *acpi;
175         struct input_dev *inputdev;
176         struct cmpc_accel *accel;
177
178         acpi = to_acpi_device(dev);
179         inputdev = dev_get_drvdata(&acpi->dev);
180         accel = dev_get_drvdata(&inputdev->dev);
181
182         return sprintf(buf, "%d\n", accel->sensitivity);
183 }
184
185 static ssize_t cmpc_accel_sensitivity_store(struct device *dev,
186                                             struct device_attribute *attr,
187                                             const char *buf, size_t count)
188 {
189         struct acpi_device *acpi;
190         struct input_dev *inputdev;
191         struct cmpc_accel *accel;
192         unsigned long sensitivity;
193         int r;
194
195         acpi = to_acpi_device(dev);
196         inputdev = dev_get_drvdata(&acpi->dev);
197         accel = dev_get_drvdata(&inputdev->dev);
198
199         r = strict_strtoul(buf, 0, &sensitivity);
200         if (r)
201                 return r;
202
203         accel->sensitivity = sensitivity;
204         cmpc_accel_set_sensitivity(acpi->handle, sensitivity);
205
206         return strnlen(buf, count);
207 }
208
209 struct device_attribute cmpc_accel_sensitivity_attr = {
210         .attr = { .name = "sensitivity", .mode = 0660 },
211         .show = cmpc_accel_sensitivity_show,
212         .store = cmpc_accel_sensitivity_store
213 };
214
215 static int cmpc_accel_open(struct input_dev *input)
216 {
217         struct acpi_device *acpi;
218
219         acpi = to_acpi_device(input->dev.parent);
220         if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle)))
221                 return 0;
222         return -EIO;
223 }
224
225 static void cmpc_accel_close(struct input_dev *input)
226 {
227         struct acpi_device *acpi;
228
229         acpi = to_acpi_device(input->dev.parent);
230         cmpc_stop_accel(acpi->handle);
231 }
232
233 static void cmpc_accel_idev_init(struct input_dev *inputdev)
234 {
235         set_bit(EV_ABS, inputdev->evbit);
236         input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0);
237         input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0);
238         input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0);
239         inputdev->open = cmpc_accel_open;
240         inputdev->close = cmpc_accel_close;
241 }
242
243 static int cmpc_accel_add(struct acpi_device *acpi)
244 {
245         int error;
246         struct input_dev *inputdev;
247         struct cmpc_accel *accel;
248
249         accel = kmalloc(sizeof(*accel), GFP_KERNEL);
250         if (!accel)
251                 return -ENOMEM;
252
253         accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
254         cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity);
255
256         error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
257         if (error)
258                 goto failed_file;
259
260         error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",
261                                             cmpc_accel_idev_init);
262         if (error)
263                 goto failed_input;
264
265         inputdev = dev_get_drvdata(&acpi->dev);
266         dev_set_drvdata(&inputdev->dev, accel);
267
268         return 0;
269
270 failed_input:
271         device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
272 failed_file:
273         kfree(accel);
274         return error;
275 }
276
277 static int cmpc_accel_remove(struct acpi_device *acpi, int type)
278 {
279         struct input_dev *inputdev;
280         struct cmpc_accel *accel;
281
282         inputdev = dev_get_drvdata(&acpi->dev);
283         accel = dev_get_drvdata(&inputdev->dev);
284
285         device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
286         return cmpc_remove_acpi_notify_device(acpi);
287 }
288
289 static const struct acpi_device_id cmpc_accel_device_ids[] = {
290         {CMPC_ACCEL_HID, 0},
291         {"", 0}
292 };
293
294 static struct acpi_driver cmpc_accel_acpi_driver = {
295         .owner = THIS_MODULE,
296         .name = "cmpc_accel",
297         .class = "cmpc_accel",
298         .ids = cmpc_accel_device_ids,
299         .ops = {
300                 .add = cmpc_accel_add,
301                 .remove = cmpc_accel_remove,
302                 .notify = cmpc_accel_handler,
303         }
304 };
305
306
307 /*
308  * Tablet mode code.
309  */
310 static acpi_status cmpc_get_tablet(acpi_handle handle,
311                                    unsigned long long *value)
312 {
313         union acpi_object param;
314         struct acpi_object_list input;
315         unsigned long long output;
316         acpi_status status;
317
318         param.type = ACPI_TYPE_INTEGER;
319         param.integer.value = 0x01;
320         input.count = 1;
321         input.pointer = &param;
322         status = acpi_evaluate_integer(handle, "TCMD", &input, &output);
323         if (ACPI_SUCCESS(status))
324                 *value = output;
325         return status;
326 }
327
328 static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
329 {
330         unsigned long long val = 0;
331         struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
332
333         if (event == 0x81) {
334                 if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val)))
335                         input_report_switch(inputdev, SW_TABLET_MODE, !val);
336         }
337 }
338
339 static void cmpc_tablet_idev_init(struct input_dev *inputdev)
340 {
341         unsigned long long val = 0;
342         struct acpi_device *acpi;
343
344         set_bit(EV_SW, inputdev->evbit);
345         set_bit(SW_TABLET_MODE, inputdev->swbit);
346
347         acpi = to_acpi_device(inputdev->dev.parent);
348         if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
349                 input_report_switch(inputdev, SW_TABLET_MODE, !val);
350 }
351
352 static int cmpc_tablet_add(struct acpi_device *acpi)
353 {
354         return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet",
355                                            cmpc_tablet_idev_init);
356 }
357
358 static int cmpc_tablet_remove(struct acpi_device *acpi, int type)
359 {
360         return cmpc_remove_acpi_notify_device(acpi);
361 }
362
363 static int cmpc_tablet_resume(struct acpi_device *acpi)
364 {
365         struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
366         unsigned long long val = 0;
367         if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
368                 input_report_switch(inputdev, SW_TABLET_MODE, !val);
369         return 0;
370 }
371
372 static const struct acpi_device_id cmpc_tablet_device_ids[] = {
373         {CMPC_TABLET_HID, 0},
374         {"", 0}
375 };
376
377 static struct acpi_driver cmpc_tablet_acpi_driver = {
378         .owner = THIS_MODULE,
379         .name = "cmpc_tablet",
380         .class = "cmpc_tablet",
381         .ids = cmpc_tablet_device_ids,
382         .ops = {
383                 .add = cmpc_tablet_add,
384                 .remove = cmpc_tablet_remove,
385                 .resume = cmpc_tablet_resume,
386                 .notify = cmpc_tablet_handler,
387         }
388 };
389
390
391 /*
392  * Backlight code.
393  */
394
395 static acpi_status cmpc_get_brightness(acpi_handle handle,
396                                        unsigned long long *value)
397 {
398         union acpi_object param;
399         struct acpi_object_list input;
400         unsigned long long output;
401         acpi_status status;
402
403         param.type = ACPI_TYPE_INTEGER;
404         param.integer.value = 0xC0;
405         input.count = 1;
406         input.pointer = &param;
407         status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
408         if (ACPI_SUCCESS(status))
409                 *value = output;
410         return status;
411 }
412
413 static acpi_status cmpc_set_brightness(acpi_handle handle,
414                                        unsigned long long value)
415 {
416         union acpi_object param[2];
417         struct acpi_object_list input;
418         acpi_status status;
419         unsigned long long output;
420
421         param[0].type = ACPI_TYPE_INTEGER;
422         param[0].integer.value = 0xC0;
423         param[1].type = ACPI_TYPE_INTEGER;
424         param[1].integer.value = value;
425         input.count = 2;
426         input.pointer = param;
427         status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
428         return status;
429 }
430
431 static int cmpc_bl_get_brightness(struct backlight_device *bd)
432 {
433         acpi_status status;
434         acpi_handle handle;
435         unsigned long long brightness;
436
437         handle = bl_get_data(bd);
438         status = cmpc_get_brightness(handle, &brightness);
439         if (ACPI_SUCCESS(status))
440                 return brightness;
441         else
442                 return -1;
443 }
444
445 static int cmpc_bl_update_status(struct backlight_device *bd)
446 {
447         acpi_status status;
448         acpi_handle handle;
449
450         handle = bl_get_data(bd);
451         status = cmpc_set_brightness(handle, bd->props.brightness);
452         if (ACPI_SUCCESS(status))
453                 return 0;
454         else
455                 return -1;
456 }
457
458 static struct backlight_ops cmpc_bl_ops = {
459         .get_brightness = cmpc_bl_get_brightness,
460         .update_status = cmpc_bl_update_status
461 };
462
463 static int cmpc_bl_add(struct acpi_device *acpi)
464 {
465         struct backlight_properties props;
466         struct backlight_device *bd;
467
468         memset(&props, 0, sizeof(struct backlight_properties));
469         props.max_brightness = 7;
470         bd = backlight_device_register("cmpc_bl", &acpi->dev, acpi->handle,
471                                        &cmpc_bl_ops, &props);
472         dev_set_drvdata(&acpi->dev, bd);
473         return 0;
474 }
475
476 static int cmpc_bl_remove(struct acpi_device *acpi, int type)
477 {
478         struct backlight_device *bd;
479
480         bd = dev_get_drvdata(&acpi->dev);
481         backlight_device_unregister(bd);
482         return 0;
483 }
484
485 static const struct acpi_device_id cmpc_bl_device_ids[] = {
486         {CMPC_BL_HID, 0},
487         {"", 0}
488 };
489
490 static struct acpi_driver cmpc_bl_acpi_driver = {
491         .owner = THIS_MODULE,
492         .name = "cmpc",
493         .class = "cmpc",
494         .ids = cmpc_bl_device_ids,
495         .ops = {
496                 .add = cmpc_bl_add,
497                 .remove = cmpc_bl_remove
498         }
499 };
500
501
502 /*
503  * Extra keys code.
504  */
505 static int cmpc_keys_codes[] = {
506         KEY_UNKNOWN,
507         KEY_WLAN,
508         KEY_SWITCHVIDEOMODE,
509         KEY_BRIGHTNESSDOWN,
510         KEY_BRIGHTNESSUP,
511         KEY_VENDOR,
512         KEY_UNKNOWN,
513         KEY_CAMERA,
514         KEY_BACK,
515         KEY_FORWARD,
516         KEY_MAX
517 };
518
519 static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
520 {
521         struct input_dev *inputdev;
522         int code = KEY_MAX;
523
524         if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
525                 code = cmpc_keys_codes[event & 0x0F];
526         inputdev = dev_get_drvdata(&dev->dev);;
527         input_report_key(inputdev, code, !(event & 0x10));
528 }
529
530 static void cmpc_keys_idev_init(struct input_dev *inputdev)
531 {
532         int i;
533
534         set_bit(EV_KEY, inputdev->evbit);
535         for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++)
536                 set_bit(cmpc_keys_codes[i], inputdev->keybit);
537 }
538
539 static int cmpc_keys_add(struct acpi_device *acpi)
540 {
541         return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
542                                            cmpc_keys_idev_init);
543 }
544
545 static int cmpc_keys_remove(struct acpi_device *acpi, int type)
546 {
547         return cmpc_remove_acpi_notify_device(acpi);
548 }
549
550 static const struct acpi_device_id cmpc_keys_device_ids[] = {
551         {CMPC_KEYS_HID, 0},
552         {"", 0}
553 };
554
555 static struct acpi_driver cmpc_keys_acpi_driver = {
556         .owner = THIS_MODULE,
557         .name = "cmpc_keys",
558         .class = "cmpc_keys",
559         .ids = cmpc_keys_device_ids,
560         .ops = {
561                 .add = cmpc_keys_add,
562                 .remove = cmpc_keys_remove,
563                 .notify = cmpc_keys_handler,
564         }
565 };
566
567
568 /*
569  * General init/exit code.
570  */
571
572 static int cmpc_init(void)
573 {
574         int r;
575
576         r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
577         if (r)
578                 goto failed_keys;
579
580         r = acpi_bus_register_driver(&cmpc_bl_acpi_driver);
581         if (r)
582                 goto failed_bl;
583
584         r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
585         if (r)
586                 goto failed_tablet;
587
588         r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
589         if (r)
590                 goto failed_accel;
591
592         return r;
593
594 failed_accel:
595         acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
596
597 failed_tablet:
598         acpi_bus_unregister_driver(&cmpc_bl_acpi_driver);
599
600 failed_bl:
601         acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
602
603 failed_keys:
604         return r;
605 }
606
607 static void cmpc_exit(void)
608 {
609         acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
610         acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
611         acpi_bus_unregister_driver(&cmpc_bl_acpi_driver);
612         acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
613 }
614
615 module_init(cmpc_init);
616 module_exit(cmpc_exit);
617
618 static const struct acpi_device_id cmpc_device_ids[] = {
619         {CMPC_ACCEL_HID, 0},
620         {CMPC_TABLET_HID, 0},
621         {CMPC_BL_HID, 0},
622         {CMPC_KEYS_HID, 0},
623         {"", 0}
624 };
625
626 MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);