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