1 // SPDX-License-Identifier: GPL-2.0-or-later
7 #include <linux/init.h>
8 #include <linux/module.h>
9 #include <linux/slab.h>
10 #include <linux/workqueue.h>
11 #include <linux/acpi.h>
12 #include <linux/backlight.h>
13 #include <linux/input.h>
14 #include <linux/rfkill.h>
22 #define CMPC_ACCEL_DEV_STATE_CLOSED 0
23 #define CMPC_ACCEL_DEV_STATE_OPEN 1
25 #define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
26 #define CMPC_ACCEL_G_SELECT_DEFAULT 0
28 #define CMPC_ACCEL_HID "ACCE0000"
29 #define CMPC_ACCEL_HID_V4 "ACCE0001"
30 #define CMPC_TABLET_HID "TBLT0000"
31 #define CMPC_IPML_HID "IPML200"
32 #define CMPC_KEYS_HID "FNBT0000"
35 * Generic input device code.
38 typedef void (*input_device_init)(struct input_dev *dev);
40 static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,
41 input_device_init idev_init)
43 struct input_dev *inputdev;
46 inputdev = input_allocate_device();
49 inputdev->name = name;
50 inputdev->dev.parent = &acpi->dev;
52 error = input_register_device(inputdev);
54 input_free_device(inputdev);
57 dev_set_drvdata(&acpi->dev, inputdev);
61 static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
63 struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
64 input_unregister_device(inputdev);
69 * Accelerometer code for Classmate V4
71 static acpi_status cmpc_start_accel_v4(acpi_handle handle)
73 union acpi_object param[4];
74 struct acpi_object_list input;
77 param[0].type = ACPI_TYPE_INTEGER;
78 param[0].integer.value = 0x3;
79 param[1].type = ACPI_TYPE_INTEGER;
80 param[1].integer.value = 0;
81 param[2].type = ACPI_TYPE_INTEGER;
82 param[2].integer.value = 0;
83 param[3].type = ACPI_TYPE_INTEGER;
84 param[3].integer.value = 0;
86 input.pointer = param;
87 status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
91 static acpi_status cmpc_stop_accel_v4(acpi_handle handle)
93 union acpi_object param[4];
94 struct acpi_object_list input;
97 param[0].type = ACPI_TYPE_INTEGER;
98 param[0].integer.value = 0x4;
99 param[1].type = ACPI_TYPE_INTEGER;
100 param[1].integer.value = 0;
101 param[2].type = ACPI_TYPE_INTEGER;
102 param[2].integer.value = 0;
103 param[3].type = ACPI_TYPE_INTEGER;
104 param[3].integer.value = 0;
106 input.pointer = param;
107 status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
111 static acpi_status cmpc_accel_set_sensitivity_v4(acpi_handle handle, int val)
113 union acpi_object param[4];
114 struct acpi_object_list input;
116 param[0].type = ACPI_TYPE_INTEGER;
117 param[0].integer.value = 0x02;
118 param[1].type = ACPI_TYPE_INTEGER;
119 param[1].integer.value = val;
120 param[2].type = ACPI_TYPE_INTEGER;
121 param[2].integer.value = 0;
122 param[3].type = ACPI_TYPE_INTEGER;
123 param[3].integer.value = 0;
125 input.pointer = param;
126 return acpi_evaluate_object(handle, "ACMD", &input, NULL);
129 static acpi_status cmpc_accel_set_g_select_v4(acpi_handle handle, int val)
131 union acpi_object param[4];
132 struct acpi_object_list input;
134 param[0].type = ACPI_TYPE_INTEGER;
135 param[0].integer.value = 0x05;
136 param[1].type = ACPI_TYPE_INTEGER;
137 param[1].integer.value = val;
138 param[2].type = ACPI_TYPE_INTEGER;
139 param[2].integer.value = 0;
140 param[3].type = ACPI_TYPE_INTEGER;
141 param[3].integer.value = 0;
143 input.pointer = param;
144 return acpi_evaluate_object(handle, "ACMD", &input, NULL);
147 static acpi_status cmpc_get_accel_v4(acpi_handle handle,
152 union acpi_object param[4];
153 struct acpi_object_list input;
154 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
158 param[0].type = ACPI_TYPE_INTEGER;
159 param[0].integer.value = 0x01;
160 param[1].type = ACPI_TYPE_INTEGER;
161 param[1].integer.value = 0;
162 param[2].type = ACPI_TYPE_INTEGER;
163 param[2].integer.value = 0;
164 param[3].type = ACPI_TYPE_INTEGER;
165 param[3].integer.value = 0;
167 input.pointer = param;
168 status = acpi_evaluate_object(handle, "ACMD", &input, &output);
169 if (ACPI_SUCCESS(status)) {
170 union acpi_object *obj;
171 obj = output.pointer;
172 locs = (int16_t *) obj->buffer.pointer;
176 kfree(output.pointer);
181 static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event)
187 status = cmpc_get_accel_v4(dev->handle, &x, &y, &z);
188 if (ACPI_SUCCESS(status)) {
189 struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
191 input_report_abs(inputdev, ABS_X, x);
192 input_report_abs(inputdev, ABS_Y, y);
193 input_report_abs(inputdev, ABS_Z, z);
194 input_sync(inputdev);
199 static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev,
200 struct device_attribute *attr,
203 struct acpi_device *acpi;
204 struct input_dev *inputdev;
205 struct cmpc_accel *accel;
207 acpi = to_acpi_device(dev);
208 inputdev = dev_get_drvdata(&acpi->dev);
209 accel = dev_get_drvdata(&inputdev->dev);
211 return sprintf(buf, "%d\n", accel->sensitivity);
214 static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev,
215 struct device_attribute *attr,
216 const char *buf, size_t count)
218 struct acpi_device *acpi;
219 struct input_dev *inputdev;
220 struct cmpc_accel *accel;
221 unsigned long sensitivity;
224 acpi = to_acpi_device(dev);
225 inputdev = dev_get_drvdata(&acpi->dev);
226 accel = dev_get_drvdata(&inputdev->dev);
228 r = kstrtoul(buf, 0, &sensitivity);
232 /* sensitivity must be between 1 and 127 */
233 if (sensitivity < 1 || sensitivity > 127)
236 accel->sensitivity = sensitivity;
237 cmpc_accel_set_sensitivity_v4(acpi->handle, sensitivity);
239 return strnlen(buf, count);
242 static struct device_attribute cmpc_accel_sensitivity_attr_v4 = {
243 .attr = { .name = "sensitivity", .mode = 0660 },
244 .show = cmpc_accel_sensitivity_show_v4,
245 .store = cmpc_accel_sensitivity_store_v4
248 static ssize_t cmpc_accel_g_select_show_v4(struct device *dev,
249 struct device_attribute *attr,
252 struct acpi_device *acpi;
253 struct input_dev *inputdev;
254 struct cmpc_accel *accel;
256 acpi = to_acpi_device(dev);
257 inputdev = dev_get_drvdata(&acpi->dev);
258 accel = dev_get_drvdata(&inputdev->dev);
260 return sprintf(buf, "%d\n", accel->g_select);
263 static ssize_t cmpc_accel_g_select_store_v4(struct device *dev,
264 struct device_attribute *attr,
265 const char *buf, size_t count)
267 struct acpi_device *acpi;
268 struct input_dev *inputdev;
269 struct cmpc_accel *accel;
270 unsigned long g_select;
273 acpi = to_acpi_device(dev);
274 inputdev = dev_get_drvdata(&acpi->dev);
275 accel = dev_get_drvdata(&inputdev->dev);
277 r = kstrtoul(buf, 0, &g_select);
281 /* 0 means 1.5g, 1 means 6g, everything else is wrong */
282 if (g_select != 0 && g_select != 1)
285 accel->g_select = g_select;
286 cmpc_accel_set_g_select_v4(acpi->handle, g_select);
288 return strnlen(buf, count);
291 static struct device_attribute cmpc_accel_g_select_attr_v4 = {
292 .attr = { .name = "g_select", .mode = 0660 },
293 .show = cmpc_accel_g_select_show_v4,
294 .store = cmpc_accel_g_select_store_v4
297 static int cmpc_accel_open_v4(struct input_dev *input)
299 struct acpi_device *acpi;
300 struct cmpc_accel *accel;
302 acpi = to_acpi_device(input->dev.parent);
303 accel = dev_get_drvdata(&input->dev);
305 cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
306 cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
308 if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) {
309 accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN;
315 static void cmpc_accel_close_v4(struct input_dev *input)
317 struct acpi_device *acpi;
318 struct cmpc_accel *accel;
320 acpi = to_acpi_device(input->dev.parent);
321 accel = dev_get_drvdata(&input->dev);
323 cmpc_stop_accel_v4(acpi->handle);
324 accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
327 static void cmpc_accel_idev_init_v4(struct input_dev *inputdev)
329 set_bit(EV_ABS, inputdev->evbit);
330 input_set_abs_params(inputdev, ABS_X, -255, 255, 16, 0);
331 input_set_abs_params(inputdev, ABS_Y, -255, 255, 16, 0);
332 input_set_abs_params(inputdev, ABS_Z, -255, 255, 16, 0);
333 inputdev->open = cmpc_accel_open_v4;
334 inputdev->close = cmpc_accel_close_v4;
337 #ifdef CONFIG_PM_SLEEP
338 static int cmpc_accel_suspend_v4(struct device *dev)
340 struct input_dev *inputdev;
341 struct cmpc_accel *accel;
343 inputdev = dev_get_drvdata(dev);
344 accel = dev_get_drvdata(&inputdev->dev);
346 if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN)
347 return cmpc_stop_accel_v4(to_acpi_device(dev)->handle);
352 static int cmpc_accel_resume_v4(struct device *dev)
354 struct input_dev *inputdev;
355 struct cmpc_accel *accel;
357 inputdev = dev_get_drvdata(dev);
358 accel = dev_get_drvdata(&inputdev->dev);
360 if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) {
361 cmpc_accel_set_sensitivity_v4(to_acpi_device(dev)->handle,
363 cmpc_accel_set_g_select_v4(to_acpi_device(dev)->handle,
366 if (ACPI_FAILURE(cmpc_start_accel_v4(to_acpi_device(dev)->handle)))
374 static int cmpc_accel_add_v4(struct acpi_device *acpi)
377 struct input_dev *inputdev;
378 struct cmpc_accel *accel;
380 accel = kmalloc(sizeof(*accel), GFP_KERNEL);
384 accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
386 accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
387 cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
389 error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
391 goto failed_sensitivity;
393 accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT;
394 cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
396 error = device_create_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
398 goto failed_g_select;
400 error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4",
401 cmpc_accel_idev_init_v4);
405 inputdev = dev_get_drvdata(&acpi->dev);
406 dev_set_drvdata(&inputdev->dev, accel);
411 device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
413 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
419 static void cmpc_accel_remove_v4(struct acpi_device *acpi)
421 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
422 device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
423 cmpc_remove_acpi_notify_device(acpi);
426 static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4,
427 cmpc_accel_resume_v4);
429 static const struct acpi_device_id cmpc_accel_device_ids_v4[] = {
430 {CMPC_ACCEL_HID_V4, 0},
434 static struct acpi_driver cmpc_accel_acpi_driver_v4 = {
435 .name = "cmpc_accel_v4",
436 .class = "cmpc_accel_v4",
437 .ids = cmpc_accel_device_ids_v4,
439 .add = cmpc_accel_add_v4,
440 .remove = cmpc_accel_remove_v4,
441 .notify = cmpc_accel_handler_v4,
443 .drv.pm = &cmpc_accel_pm,
448 * Accelerometer code for Classmate versions prior to V4
450 static acpi_status cmpc_start_accel(acpi_handle handle)
452 union acpi_object param[2];
453 struct acpi_object_list input;
456 param[0].type = ACPI_TYPE_INTEGER;
457 param[0].integer.value = 0x3;
458 param[1].type = ACPI_TYPE_INTEGER;
460 input.pointer = param;
461 status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
465 static acpi_status cmpc_stop_accel(acpi_handle handle)
467 union acpi_object param[2];
468 struct acpi_object_list input;
471 param[0].type = ACPI_TYPE_INTEGER;
472 param[0].integer.value = 0x4;
473 param[1].type = ACPI_TYPE_INTEGER;
475 input.pointer = param;
476 status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
480 static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val)
482 union acpi_object param[2];
483 struct acpi_object_list input;
485 param[0].type = ACPI_TYPE_INTEGER;
486 param[0].integer.value = 0x02;
487 param[1].type = ACPI_TYPE_INTEGER;
488 param[1].integer.value = val;
490 input.pointer = param;
491 return acpi_evaluate_object(handle, "ACMD", &input, NULL);
494 static acpi_status cmpc_get_accel(acpi_handle handle,
499 union acpi_object param[2];
500 struct acpi_object_list input;
501 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
505 param[0].type = ACPI_TYPE_INTEGER;
506 param[0].integer.value = 0x01;
507 param[1].type = ACPI_TYPE_INTEGER;
509 input.pointer = param;
510 status = acpi_evaluate_object(handle, "ACMD", &input, &output);
511 if (ACPI_SUCCESS(status)) {
512 union acpi_object *obj;
513 obj = output.pointer;
514 locs = obj->buffer.pointer;
518 kfree(output.pointer);
523 static void cmpc_accel_handler(struct acpi_device *dev, u32 event)
526 unsigned char x, y, z;
529 status = cmpc_get_accel(dev->handle, &x, &y, &z);
530 if (ACPI_SUCCESS(status)) {
531 struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
533 input_report_abs(inputdev, ABS_X, x);
534 input_report_abs(inputdev, ABS_Y, y);
535 input_report_abs(inputdev, ABS_Z, z);
536 input_sync(inputdev);
541 static ssize_t cmpc_accel_sensitivity_show(struct device *dev,
542 struct device_attribute *attr,
545 struct acpi_device *acpi;
546 struct input_dev *inputdev;
547 struct cmpc_accel *accel;
549 acpi = to_acpi_device(dev);
550 inputdev = dev_get_drvdata(&acpi->dev);
551 accel = dev_get_drvdata(&inputdev->dev);
553 return sprintf(buf, "%d\n", accel->sensitivity);
556 static ssize_t cmpc_accel_sensitivity_store(struct device *dev,
557 struct device_attribute *attr,
558 const char *buf, size_t count)
560 struct acpi_device *acpi;
561 struct input_dev *inputdev;
562 struct cmpc_accel *accel;
563 unsigned long sensitivity;
566 acpi = to_acpi_device(dev);
567 inputdev = dev_get_drvdata(&acpi->dev);
568 accel = dev_get_drvdata(&inputdev->dev);
570 r = kstrtoul(buf, 0, &sensitivity);
574 accel->sensitivity = sensitivity;
575 cmpc_accel_set_sensitivity(acpi->handle, sensitivity);
577 return strnlen(buf, count);
580 static struct device_attribute cmpc_accel_sensitivity_attr = {
581 .attr = { .name = "sensitivity", .mode = 0660 },
582 .show = cmpc_accel_sensitivity_show,
583 .store = cmpc_accel_sensitivity_store
586 static int cmpc_accel_open(struct input_dev *input)
588 struct acpi_device *acpi;
590 acpi = to_acpi_device(input->dev.parent);
591 if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle)))
596 static void cmpc_accel_close(struct input_dev *input)
598 struct acpi_device *acpi;
600 acpi = to_acpi_device(input->dev.parent);
601 cmpc_stop_accel(acpi->handle);
604 static void cmpc_accel_idev_init(struct input_dev *inputdev)
606 set_bit(EV_ABS, inputdev->evbit);
607 input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0);
608 input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0);
609 input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0);
610 inputdev->open = cmpc_accel_open;
611 inputdev->close = cmpc_accel_close;
614 static int cmpc_accel_add(struct acpi_device *acpi)
617 struct input_dev *inputdev;
618 struct cmpc_accel *accel;
620 accel = kmalloc(sizeof(*accel), GFP_KERNEL);
624 accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
625 cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity);
627 error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
631 error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",
632 cmpc_accel_idev_init);
636 inputdev = dev_get_drvdata(&acpi->dev);
637 dev_set_drvdata(&inputdev->dev, accel);
642 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
648 static void cmpc_accel_remove(struct acpi_device *acpi)
650 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
651 cmpc_remove_acpi_notify_device(acpi);
654 static const struct acpi_device_id cmpc_accel_device_ids[] = {
659 static struct acpi_driver cmpc_accel_acpi_driver = {
660 .name = "cmpc_accel",
661 .class = "cmpc_accel",
662 .ids = cmpc_accel_device_ids,
664 .add = cmpc_accel_add,
665 .remove = cmpc_accel_remove,
666 .notify = cmpc_accel_handler,
674 static acpi_status cmpc_get_tablet(acpi_handle handle,
675 unsigned long long *value)
677 union acpi_object param;
678 struct acpi_object_list input;
679 unsigned long long output;
682 param.type = ACPI_TYPE_INTEGER;
683 param.integer.value = 0x01;
685 input.pointer = ¶m;
686 status = acpi_evaluate_integer(handle, "TCMD", &input, &output);
687 if (ACPI_SUCCESS(status))
692 static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
694 unsigned long long val = 0;
695 struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
698 if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) {
699 input_report_switch(inputdev, SW_TABLET_MODE, !val);
700 input_sync(inputdev);
705 static void cmpc_tablet_idev_init(struct input_dev *inputdev)
707 unsigned long long val = 0;
708 struct acpi_device *acpi;
710 set_bit(EV_SW, inputdev->evbit);
711 set_bit(SW_TABLET_MODE, inputdev->swbit);
713 acpi = to_acpi_device(inputdev->dev.parent);
714 if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) {
715 input_report_switch(inputdev, SW_TABLET_MODE, !val);
716 input_sync(inputdev);
720 static int cmpc_tablet_add(struct acpi_device *acpi)
722 return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet",
723 cmpc_tablet_idev_init);
726 static void cmpc_tablet_remove(struct acpi_device *acpi)
728 cmpc_remove_acpi_notify_device(acpi);
731 #ifdef CONFIG_PM_SLEEP
732 static int cmpc_tablet_resume(struct device *dev)
734 struct input_dev *inputdev = dev_get_drvdata(dev);
736 unsigned long long val = 0;
737 if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) {
738 input_report_switch(inputdev, SW_TABLET_MODE, !val);
739 input_sync(inputdev);
745 static SIMPLE_DEV_PM_OPS(cmpc_tablet_pm, NULL, cmpc_tablet_resume);
747 static const struct acpi_device_id cmpc_tablet_device_ids[] = {
748 {CMPC_TABLET_HID, 0},
752 static struct acpi_driver cmpc_tablet_acpi_driver = {
753 .name = "cmpc_tablet",
754 .class = "cmpc_tablet",
755 .ids = cmpc_tablet_device_ids,
757 .add = cmpc_tablet_add,
758 .remove = cmpc_tablet_remove,
759 .notify = cmpc_tablet_handler,
761 .drv.pm = &cmpc_tablet_pm,
769 static acpi_status cmpc_get_brightness(acpi_handle handle,
770 unsigned long long *value)
772 union acpi_object param;
773 struct acpi_object_list input;
774 unsigned long long output;
777 param.type = ACPI_TYPE_INTEGER;
778 param.integer.value = 0xC0;
780 input.pointer = ¶m;
781 status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
782 if (ACPI_SUCCESS(status))
787 static acpi_status cmpc_set_brightness(acpi_handle handle,
788 unsigned long long value)
790 union acpi_object param[2];
791 struct acpi_object_list input;
793 unsigned long long output;
795 param[0].type = ACPI_TYPE_INTEGER;
796 param[0].integer.value = 0xC0;
797 param[1].type = ACPI_TYPE_INTEGER;
798 param[1].integer.value = value;
800 input.pointer = param;
801 status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
805 static int cmpc_bl_get_brightness(struct backlight_device *bd)
809 unsigned long long brightness;
811 handle = bl_get_data(bd);
812 status = cmpc_get_brightness(handle, &brightness);
813 if (ACPI_SUCCESS(status))
819 static int cmpc_bl_update_status(struct backlight_device *bd)
824 handle = bl_get_data(bd);
825 status = cmpc_set_brightness(handle, bd->props.brightness);
826 if (ACPI_SUCCESS(status))
832 static const struct backlight_ops cmpc_bl_ops = {
833 .get_brightness = cmpc_bl_get_brightness,
834 .update_status = cmpc_bl_update_status
841 static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle,
842 unsigned long long *value)
844 union acpi_object param;
845 struct acpi_object_list input;
846 unsigned long long output;
849 param.type = ACPI_TYPE_INTEGER;
850 param.integer.value = 0xC1;
852 input.pointer = ¶m;
853 status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
854 if (ACPI_SUCCESS(status))
859 static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle,
860 unsigned long long value)
862 union acpi_object param[2];
863 struct acpi_object_list input;
865 unsigned long long output;
867 param[0].type = ACPI_TYPE_INTEGER;
868 param[0].integer.value = 0xC1;
869 param[1].type = ACPI_TYPE_INTEGER;
870 param[1].integer.value = value;
872 input.pointer = param;
873 status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
877 static void cmpc_rfkill_query(struct rfkill *rfkill, void *data)
881 unsigned long long state;
885 status = cmpc_get_rfkill_wlan(handle, &state);
886 if (ACPI_SUCCESS(status)) {
887 blocked = state & 1 ? false : true;
888 rfkill_set_sw_state(rfkill, blocked);
892 static int cmpc_rfkill_block(void *data, bool blocked)
896 unsigned long long state;
900 status = cmpc_get_rfkill_wlan(handle, &state);
901 if (ACPI_FAILURE(status))
903 /* Check if we really need to call cmpc_set_rfkill_wlan */
904 is_blocked = state & 1 ? false : true;
905 if (is_blocked != blocked) {
906 state = blocked ? 0 : 1;
907 status = cmpc_set_rfkill_wlan(handle, state);
908 if (ACPI_FAILURE(status))
914 static const struct rfkill_ops cmpc_rfkill_ops = {
915 .query = cmpc_rfkill_query,
916 .set_block = cmpc_rfkill_block,
920 * Common backlight and rfkill code.
924 struct backlight_device *bd;
928 static int cmpc_ipml_add(struct acpi_device *acpi)
931 struct ipml200_dev *ipml;
932 struct backlight_properties props;
934 ipml = kmalloc(sizeof(*ipml), GFP_KERNEL);
938 memset(&props, 0, sizeof(struct backlight_properties));
939 props.type = BACKLIGHT_PLATFORM;
940 props.max_brightness = 7;
941 ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev,
942 acpi->handle, &cmpc_bl_ops,
944 if (IS_ERR(ipml->bd)) {
945 retval = PTR_ERR(ipml->bd);
949 ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN,
950 &cmpc_rfkill_ops, acpi->handle);
952 * If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV).
953 * This is OK, however, since all other uses of the device will not
957 retval = rfkill_register(ipml->rf);
959 rfkill_destroy(ipml->rf);
964 dev_set_drvdata(&acpi->dev, ipml);
972 static void cmpc_ipml_remove(struct acpi_device *acpi)
974 struct ipml200_dev *ipml;
976 ipml = dev_get_drvdata(&acpi->dev);
978 backlight_device_unregister(ipml->bd);
981 rfkill_unregister(ipml->rf);
982 rfkill_destroy(ipml->rf);
988 static const struct acpi_device_id cmpc_ipml_device_ids[] = {
993 static struct acpi_driver cmpc_ipml_acpi_driver = {
996 .ids = cmpc_ipml_device_ids,
998 .add = cmpc_ipml_add,
999 .remove = cmpc_ipml_remove
1007 static int cmpc_keys_codes[] = {
1010 KEY_SWITCHVIDEOMODE,
1019 KEY_WLAN, /* NL3: 0x8b (press), 0x9b (release) */
1023 static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
1025 struct input_dev *inputdev;
1028 if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
1029 code = cmpc_keys_codes[event & 0x0F];
1030 inputdev = dev_get_drvdata(&dev->dev);
1031 input_report_key(inputdev, code, !(event & 0x10));
1032 input_sync(inputdev);
1035 static void cmpc_keys_idev_init(struct input_dev *inputdev)
1039 set_bit(EV_KEY, inputdev->evbit);
1040 for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++)
1041 set_bit(cmpc_keys_codes[i], inputdev->keybit);
1044 static int cmpc_keys_add(struct acpi_device *acpi)
1046 return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
1047 cmpc_keys_idev_init);
1050 static void cmpc_keys_remove(struct acpi_device *acpi)
1052 cmpc_remove_acpi_notify_device(acpi);
1055 static const struct acpi_device_id cmpc_keys_device_ids[] = {
1060 static struct acpi_driver cmpc_keys_acpi_driver = {
1061 .name = "cmpc_keys",
1062 .class = "cmpc_keys",
1063 .ids = cmpc_keys_device_ids,
1065 .add = cmpc_keys_add,
1066 .remove = cmpc_keys_remove,
1067 .notify = cmpc_keys_handler,
1073 * General init/exit code.
1076 static int cmpc_init(void)
1080 r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
1084 r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver);
1088 r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
1092 r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
1096 r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4);
1098 goto failed_accel_v4;
1103 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
1106 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
1109 acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
1112 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
1118 static void cmpc_exit(void)
1120 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4);
1121 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
1122 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
1123 acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
1124 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
1127 module_init(cmpc_init);
1128 module_exit(cmpc_exit);
1130 static const struct acpi_device_id cmpc_device_ids[] __maybe_unused = {
1131 {CMPC_ACCEL_HID, 0},
1132 {CMPC_ACCEL_HID_V4, 0},
1133 {CMPC_TABLET_HID, 0},
1139 MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
1140 MODULE_DESCRIPTION("Support for Intel Classmate PC ACPI devices");
1141 MODULE_LICENSE("GPL");