xref: /linux/drivers/platform/x86/classmate-laptop.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  Copyright (C) 2009  Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
4  */
5 
6 
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>
15 
16 struct cmpc_accel {
17 	int sensitivity;
18 	int g_select;
19 	int inputdev_state;
20 };
21 
22 #define CMPC_ACCEL_DEV_STATE_CLOSED	0
23 #define CMPC_ACCEL_DEV_STATE_OPEN	1
24 
25 #define CMPC_ACCEL_SENSITIVITY_DEFAULT		5
26 #define CMPC_ACCEL_G_SELECT_DEFAULT		0
27 
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"
33 
34 /*
35  * Generic input device code.
36  */
37 
38 typedef void (*input_device_init)(struct input_dev *dev);
39 
40 static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,
41 				       input_device_init idev_init)
42 {
43 	struct input_dev *inputdev;
44 	int error;
45 
46 	inputdev = input_allocate_device();
47 	if (!inputdev)
48 		return -ENOMEM;
49 	inputdev->name = name;
50 	inputdev->dev.parent = &acpi->dev;
51 	idev_init(inputdev);
52 	error = input_register_device(inputdev);
53 	if (error) {
54 		input_free_device(inputdev);
55 		return error;
56 	}
57 	dev_set_drvdata(&acpi->dev, inputdev);
58 	return 0;
59 }
60 
61 static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
62 {
63 	struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
64 	input_unregister_device(inputdev);
65 	return 0;
66 }
67 
68 /*
69  * Accelerometer code for Classmate V4
70  */
71 static acpi_status cmpc_start_accel_v4(acpi_handle handle)
72 {
73 	union acpi_object param[4];
74 	struct acpi_object_list input;
75 	acpi_status status;
76 
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;
85 	input.count = 4;
86 	input.pointer = param;
87 	status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
88 	return status;
89 }
90 
91 static acpi_status cmpc_stop_accel_v4(acpi_handle handle)
92 {
93 	union acpi_object param[4];
94 	struct acpi_object_list input;
95 	acpi_status status;
96 
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;
105 	input.count = 4;
106 	input.pointer = param;
107 	status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
108 	return status;
109 }
110 
111 static acpi_status cmpc_accel_set_sensitivity_v4(acpi_handle handle, int val)
112 {
113 	union acpi_object param[4];
114 	struct acpi_object_list input;
115 
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;
124 	input.count = 4;
125 	input.pointer = param;
126 	return acpi_evaluate_object(handle, "ACMD", &input, NULL);
127 }
128 
129 static acpi_status cmpc_accel_set_g_select_v4(acpi_handle handle, int val)
130 {
131 	union acpi_object param[4];
132 	struct acpi_object_list input;
133 
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;
142 	input.count = 4;
143 	input.pointer = param;
144 	return acpi_evaluate_object(handle, "ACMD", &input, NULL);
145 }
146 
147 static acpi_status cmpc_get_accel_v4(acpi_handle handle,
148 				     int16_t *x,
149 				     int16_t *y,
150 				     int16_t *z)
151 {
152 	union acpi_object param[4];
153 	struct acpi_object_list input;
154 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
155 	int16_t *locs;
156 	acpi_status status;
157 
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;
166 	input.count = 4;
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;
173 		*x = locs[0];
174 		*y = locs[1];
175 		*z = locs[2];
176 		kfree(output.pointer);
177 	}
178 	return status;
179 }
180 
181 static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event)
182 {
183 	if (event == 0x81) {
184 		int16_t x, y, z;
185 		acpi_status status;
186 
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);
190 
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);
195 		}
196 	}
197 }
198 
199 static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev,
200 					      struct device_attribute *attr,
201 					      char *buf)
202 {
203 	struct acpi_device *acpi;
204 	struct input_dev *inputdev;
205 	struct cmpc_accel *accel;
206 
207 	acpi = to_acpi_device(dev);
208 	inputdev = dev_get_drvdata(&acpi->dev);
209 	accel = dev_get_drvdata(&inputdev->dev);
210 
211 	return sprintf(buf, "%d\n", accel->sensitivity);
212 }
213 
214 static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev,
215 					       struct device_attribute *attr,
216 					       const char *buf, size_t count)
217 {
218 	struct acpi_device *acpi;
219 	struct input_dev *inputdev;
220 	struct cmpc_accel *accel;
221 	unsigned long sensitivity;
222 	int r;
223 
224 	acpi = to_acpi_device(dev);
225 	inputdev = dev_get_drvdata(&acpi->dev);
226 	accel = dev_get_drvdata(&inputdev->dev);
227 
228 	r = kstrtoul(buf, 0, &sensitivity);
229 	if (r)
230 		return r;
231 
232 	/* sensitivity must be between 1 and 127 */
233 	if (sensitivity < 1 || sensitivity > 127)
234 		return -EINVAL;
235 
236 	accel->sensitivity = sensitivity;
237 	cmpc_accel_set_sensitivity_v4(acpi->handle, sensitivity);
238 
239 	return strnlen(buf, count);
240 }
241 
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
246 };
247 
248 static ssize_t cmpc_accel_g_select_show_v4(struct device *dev,
249 					   struct device_attribute *attr,
250 					   char *buf)
251 {
252 	struct acpi_device *acpi;
253 	struct input_dev *inputdev;
254 	struct cmpc_accel *accel;
255 
256 	acpi = to_acpi_device(dev);
257 	inputdev = dev_get_drvdata(&acpi->dev);
258 	accel = dev_get_drvdata(&inputdev->dev);
259 
260 	return sprintf(buf, "%d\n", accel->g_select);
261 }
262 
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)
266 {
267 	struct acpi_device *acpi;
268 	struct input_dev *inputdev;
269 	struct cmpc_accel *accel;
270 	unsigned long g_select;
271 	int r;
272 
273 	acpi = to_acpi_device(dev);
274 	inputdev = dev_get_drvdata(&acpi->dev);
275 	accel = dev_get_drvdata(&inputdev->dev);
276 
277 	r = kstrtoul(buf, 0, &g_select);
278 	if (r)
279 		return r;
280 
281 	/* 0 means 1.5g, 1 means 6g, everything else is wrong */
282 	if (g_select != 0 && g_select != 1)
283 		return -EINVAL;
284 
285 	accel->g_select = g_select;
286 	cmpc_accel_set_g_select_v4(acpi->handle, g_select);
287 
288 	return strnlen(buf, count);
289 }
290 
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
295 };
296 
297 static int cmpc_accel_open_v4(struct input_dev *input)
298 {
299 	struct acpi_device *acpi;
300 	struct cmpc_accel *accel;
301 
302 	acpi = to_acpi_device(input->dev.parent);
303 	accel = dev_get_drvdata(&input->dev);
304 
305 	cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
306 	cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
307 
308 	if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) {
309 		accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN;
310 		return 0;
311 	}
312 	return -EIO;
313 }
314 
315 static void cmpc_accel_close_v4(struct input_dev *input)
316 {
317 	struct acpi_device *acpi;
318 	struct cmpc_accel *accel;
319 
320 	acpi = to_acpi_device(input->dev.parent);
321 	accel = dev_get_drvdata(&input->dev);
322 
323 	cmpc_stop_accel_v4(acpi->handle);
324 	accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
325 }
326 
327 static void cmpc_accel_idev_init_v4(struct input_dev *inputdev)
328 {
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;
335 }
336 
337 #ifdef CONFIG_PM_SLEEP
338 static int cmpc_accel_suspend_v4(struct device *dev)
339 {
340 	struct input_dev *inputdev;
341 	struct cmpc_accel *accel;
342 
343 	inputdev = dev_get_drvdata(dev);
344 	accel = dev_get_drvdata(&inputdev->dev);
345 
346 	if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN)
347 		return cmpc_stop_accel_v4(to_acpi_device(dev)->handle);
348 
349 	return 0;
350 }
351 
352 static int cmpc_accel_resume_v4(struct device *dev)
353 {
354 	struct input_dev *inputdev;
355 	struct cmpc_accel *accel;
356 
357 	inputdev = dev_get_drvdata(dev);
358 	accel = dev_get_drvdata(&inputdev->dev);
359 
360 	if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) {
361 		cmpc_accel_set_sensitivity_v4(to_acpi_device(dev)->handle,
362 					      accel->sensitivity);
363 		cmpc_accel_set_g_select_v4(to_acpi_device(dev)->handle,
364 					   accel->g_select);
365 
366 		if (ACPI_FAILURE(cmpc_start_accel_v4(to_acpi_device(dev)->handle)))
367 			return -EIO;
368 	}
369 
370 	return 0;
371 }
372 #endif
373 
374 static int cmpc_accel_add_v4(struct acpi_device *acpi)
375 {
376 	int error;
377 	struct input_dev *inputdev;
378 	struct cmpc_accel *accel;
379 
380 	accel = kmalloc(sizeof(*accel), GFP_KERNEL);
381 	if (!accel)
382 		return -ENOMEM;
383 
384 	accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
385 
386 	accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
387 	cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
388 
389 	error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
390 	if (error)
391 		goto failed_sensitivity;
392 
393 	accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT;
394 	cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
395 
396 	error = device_create_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
397 	if (error)
398 		goto failed_g_select;
399 
400 	error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4",
401 					    cmpc_accel_idev_init_v4);
402 	if (error)
403 		goto failed_input;
404 
405 	inputdev = dev_get_drvdata(&acpi->dev);
406 	dev_set_drvdata(&inputdev->dev, accel);
407 
408 	return 0;
409 
410 failed_input:
411 	device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
412 failed_g_select:
413 	device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
414 failed_sensitivity:
415 	kfree(accel);
416 	return error;
417 }
418 
419 static void cmpc_accel_remove_v4(struct acpi_device *acpi)
420 {
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);
424 }
425 
426 static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4,
427 			 cmpc_accel_resume_v4);
428 
429 static const struct acpi_device_id cmpc_accel_device_ids_v4[] = {
430 	{CMPC_ACCEL_HID_V4, 0},
431 	{"", 0}
432 };
433 
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,
438 	.ops = {
439 		.add = cmpc_accel_add_v4,
440 		.remove = cmpc_accel_remove_v4,
441 		.notify = cmpc_accel_handler_v4,
442 	},
443 	.drv.pm = &cmpc_accel_pm,
444 };
445 
446 
447 /*
448  * Accelerometer code for Classmate versions prior to V4
449  */
450 static acpi_status cmpc_start_accel(acpi_handle handle)
451 {
452 	union acpi_object param[2];
453 	struct acpi_object_list input;
454 	acpi_status status;
455 
456 	param[0].type = ACPI_TYPE_INTEGER;
457 	param[0].integer.value = 0x3;
458 	param[1].type = ACPI_TYPE_INTEGER;
459 	input.count = 2;
460 	input.pointer = param;
461 	status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
462 	return status;
463 }
464 
465 static acpi_status cmpc_stop_accel(acpi_handle handle)
466 {
467 	union acpi_object param[2];
468 	struct acpi_object_list input;
469 	acpi_status status;
470 
471 	param[0].type = ACPI_TYPE_INTEGER;
472 	param[0].integer.value = 0x4;
473 	param[1].type = ACPI_TYPE_INTEGER;
474 	input.count = 2;
475 	input.pointer = param;
476 	status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
477 	return status;
478 }
479 
480 static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val)
481 {
482 	union acpi_object param[2];
483 	struct acpi_object_list input;
484 
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;
489 	input.count = 2;
490 	input.pointer = param;
491 	return acpi_evaluate_object(handle, "ACMD", &input, NULL);
492 }
493 
494 static acpi_status cmpc_get_accel(acpi_handle handle,
495 				  unsigned char *x,
496 				  unsigned char *y,
497 				  unsigned char *z)
498 {
499 	union acpi_object param[2];
500 	struct acpi_object_list input;
501 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
502 	unsigned char *locs;
503 	acpi_status status;
504 
505 	param[0].type = ACPI_TYPE_INTEGER;
506 	param[0].integer.value = 0x01;
507 	param[1].type = ACPI_TYPE_INTEGER;
508 	input.count = 2;
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;
515 		*x = locs[0];
516 		*y = locs[1];
517 		*z = locs[2];
518 		kfree(output.pointer);
519 	}
520 	return status;
521 }
522 
523 static void cmpc_accel_handler(struct acpi_device *dev, u32 event)
524 {
525 	if (event == 0x81) {
526 		unsigned char x, y, z;
527 		acpi_status status;
528 
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);
532 
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);
537 		}
538 	}
539 }
540 
541 static ssize_t cmpc_accel_sensitivity_show(struct device *dev,
542 					   struct device_attribute *attr,
543 					   char *buf)
544 {
545 	struct acpi_device *acpi;
546 	struct input_dev *inputdev;
547 	struct cmpc_accel *accel;
548 
549 	acpi = to_acpi_device(dev);
550 	inputdev = dev_get_drvdata(&acpi->dev);
551 	accel = dev_get_drvdata(&inputdev->dev);
552 
553 	return sprintf(buf, "%d\n", accel->sensitivity);
554 }
555 
556 static ssize_t cmpc_accel_sensitivity_store(struct device *dev,
557 					    struct device_attribute *attr,
558 					    const char *buf, size_t count)
559 {
560 	struct acpi_device *acpi;
561 	struct input_dev *inputdev;
562 	struct cmpc_accel *accel;
563 	unsigned long sensitivity;
564 	int r;
565 
566 	acpi = to_acpi_device(dev);
567 	inputdev = dev_get_drvdata(&acpi->dev);
568 	accel = dev_get_drvdata(&inputdev->dev);
569 
570 	r = kstrtoul(buf, 0, &sensitivity);
571 	if (r)
572 		return r;
573 
574 	accel->sensitivity = sensitivity;
575 	cmpc_accel_set_sensitivity(acpi->handle, sensitivity);
576 
577 	return strnlen(buf, count);
578 }
579 
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
584 };
585 
586 static int cmpc_accel_open(struct input_dev *input)
587 {
588 	struct acpi_device *acpi;
589 
590 	acpi = to_acpi_device(input->dev.parent);
591 	if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle)))
592 		return 0;
593 	return -EIO;
594 }
595 
596 static void cmpc_accel_close(struct input_dev *input)
597 {
598 	struct acpi_device *acpi;
599 
600 	acpi = to_acpi_device(input->dev.parent);
601 	cmpc_stop_accel(acpi->handle);
602 }
603 
604 static void cmpc_accel_idev_init(struct input_dev *inputdev)
605 {
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;
612 }
613 
614 static int cmpc_accel_add(struct acpi_device *acpi)
615 {
616 	int error;
617 	struct input_dev *inputdev;
618 	struct cmpc_accel *accel;
619 
620 	accel = kmalloc(sizeof(*accel), GFP_KERNEL);
621 	if (!accel)
622 		return -ENOMEM;
623 
624 	accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
625 	cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity);
626 
627 	error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
628 	if (error)
629 		goto failed_file;
630 
631 	error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",
632 					    cmpc_accel_idev_init);
633 	if (error)
634 		goto failed_input;
635 
636 	inputdev = dev_get_drvdata(&acpi->dev);
637 	dev_set_drvdata(&inputdev->dev, accel);
638 
639 	return 0;
640 
641 failed_input:
642 	device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
643 failed_file:
644 	kfree(accel);
645 	return error;
646 }
647 
648 static void cmpc_accel_remove(struct acpi_device *acpi)
649 {
650 	device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
651 	cmpc_remove_acpi_notify_device(acpi);
652 }
653 
654 static const struct acpi_device_id cmpc_accel_device_ids[] = {
655 	{CMPC_ACCEL_HID, 0},
656 	{"", 0}
657 };
658 
659 static struct acpi_driver cmpc_accel_acpi_driver = {
660 	.name = "cmpc_accel",
661 	.class = "cmpc_accel",
662 	.ids = cmpc_accel_device_ids,
663 	.ops = {
664 		.add = cmpc_accel_add,
665 		.remove = cmpc_accel_remove,
666 		.notify = cmpc_accel_handler,
667 	}
668 };
669 
670 
671 /*
672  * Tablet mode code.
673  */
674 static acpi_status cmpc_get_tablet(acpi_handle handle,
675 				   unsigned long long *value)
676 {
677 	union acpi_object param;
678 	struct acpi_object_list input;
679 	unsigned long long output;
680 	acpi_status status;
681 
682 	param.type = ACPI_TYPE_INTEGER;
683 	param.integer.value = 0x01;
684 	input.count = 1;
685 	input.pointer = &param;
686 	status = acpi_evaluate_integer(handle, "TCMD", &input, &output);
687 	if (ACPI_SUCCESS(status))
688 		*value = output;
689 	return status;
690 }
691 
692 static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
693 {
694 	unsigned long long val = 0;
695 	struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
696 
697 	if (event == 0x81) {
698 		if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) {
699 			input_report_switch(inputdev, SW_TABLET_MODE, !val);
700 			input_sync(inputdev);
701 		}
702 	}
703 }
704 
705 static void cmpc_tablet_idev_init(struct input_dev *inputdev)
706 {
707 	unsigned long long val = 0;
708 	struct acpi_device *acpi;
709 
710 	set_bit(EV_SW, inputdev->evbit);
711 	set_bit(SW_TABLET_MODE, inputdev->swbit);
712 
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);
717 	}
718 }
719 
720 static int cmpc_tablet_add(struct acpi_device *acpi)
721 {
722 	return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet",
723 					   cmpc_tablet_idev_init);
724 }
725 
726 static void cmpc_tablet_remove(struct acpi_device *acpi)
727 {
728 	cmpc_remove_acpi_notify_device(acpi);
729 }
730 
731 #ifdef CONFIG_PM_SLEEP
732 static int cmpc_tablet_resume(struct device *dev)
733 {
734 	struct input_dev *inputdev = dev_get_drvdata(dev);
735 
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);
740 	}
741 	return 0;
742 }
743 #endif
744 
745 static SIMPLE_DEV_PM_OPS(cmpc_tablet_pm, NULL, cmpc_tablet_resume);
746 
747 static const struct acpi_device_id cmpc_tablet_device_ids[] = {
748 	{CMPC_TABLET_HID, 0},
749 	{"", 0}
750 };
751 
752 static struct acpi_driver cmpc_tablet_acpi_driver = {
753 	.name = "cmpc_tablet",
754 	.class = "cmpc_tablet",
755 	.ids = cmpc_tablet_device_ids,
756 	.ops = {
757 		.add = cmpc_tablet_add,
758 		.remove = cmpc_tablet_remove,
759 		.notify = cmpc_tablet_handler,
760 	},
761 	.drv.pm = &cmpc_tablet_pm,
762 };
763 
764 
765 /*
766  * Backlight code.
767  */
768 
769 static acpi_status cmpc_get_brightness(acpi_handle handle,
770 				       unsigned long long *value)
771 {
772 	union acpi_object param;
773 	struct acpi_object_list input;
774 	unsigned long long output;
775 	acpi_status status;
776 
777 	param.type = ACPI_TYPE_INTEGER;
778 	param.integer.value = 0xC0;
779 	input.count = 1;
780 	input.pointer = &param;
781 	status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
782 	if (ACPI_SUCCESS(status))
783 		*value = output;
784 	return status;
785 }
786 
787 static acpi_status cmpc_set_brightness(acpi_handle handle,
788 				       unsigned long long value)
789 {
790 	union acpi_object param[2];
791 	struct acpi_object_list input;
792 	acpi_status status;
793 	unsigned long long output;
794 
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;
799 	input.count = 2;
800 	input.pointer = param;
801 	status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
802 	return status;
803 }
804 
805 static int cmpc_bl_get_brightness(struct backlight_device *bd)
806 {
807 	acpi_status status;
808 	acpi_handle handle;
809 	unsigned long long brightness;
810 
811 	handle = bl_get_data(bd);
812 	status = cmpc_get_brightness(handle, &brightness);
813 	if (ACPI_SUCCESS(status))
814 		return brightness;
815 	else
816 		return -1;
817 }
818 
819 static int cmpc_bl_update_status(struct backlight_device *bd)
820 {
821 	acpi_status status;
822 	acpi_handle handle;
823 
824 	handle = bl_get_data(bd);
825 	status = cmpc_set_brightness(handle, bd->props.brightness);
826 	if (ACPI_SUCCESS(status))
827 		return 0;
828 	else
829 		return -1;
830 }
831 
832 static const struct backlight_ops cmpc_bl_ops = {
833 	.get_brightness = cmpc_bl_get_brightness,
834 	.update_status = cmpc_bl_update_status
835 };
836 
837 /*
838  * RFKILL code.
839  */
840 
841 static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle,
842 					unsigned long long *value)
843 {
844 	union acpi_object param;
845 	struct acpi_object_list input;
846 	unsigned long long output;
847 	acpi_status status;
848 
849 	param.type = ACPI_TYPE_INTEGER;
850 	param.integer.value = 0xC1;
851 	input.count = 1;
852 	input.pointer = &param;
853 	status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
854 	if (ACPI_SUCCESS(status))
855 		*value = output;
856 	return status;
857 }
858 
859 static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle,
860 					unsigned long long value)
861 {
862 	union acpi_object param[2];
863 	struct acpi_object_list input;
864 	acpi_status status;
865 	unsigned long long output;
866 
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;
871 	input.count = 2;
872 	input.pointer = param;
873 	status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
874 	return status;
875 }
876 
877 static void cmpc_rfkill_query(struct rfkill *rfkill, void *data)
878 {
879 	acpi_status status;
880 	acpi_handle handle;
881 	unsigned long long state;
882 	bool blocked;
883 
884 	handle = data;
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);
889 	}
890 }
891 
892 static int cmpc_rfkill_block(void *data, bool blocked)
893 {
894 	acpi_status status;
895 	acpi_handle handle;
896 	unsigned long long state;
897 	bool is_blocked;
898 
899 	handle = data;
900 	status = cmpc_get_rfkill_wlan(handle, &state);
901 	if (ACPI_FAILURE(status))
902 		return -ENODEV;
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))
909 			return -ENODEV;
910 	}
911 	return 0;
912 }
913 
914 static const struct rfkill_ops cmpc_rfkill_ops = {
915 	.query = cmpc_rfkill_query,
916 	.set_block = cmpc_rfkill_block,
917 };
918 
919 /*
920  * Common backlight and rfkill code.
921  */
922 
923 struct ipml200_dev {
924 	struct backlight_device *bd;
925 	struct rfkill *rf;
926 };
927 
928 static int cmpc_ipml_add(struct acpi_device *acpi)
929 {
930 	int retval;
931 	struct ipml200_dev *ipml;
932 	struct backlight_properties props;
933 
934 	ipml = kmalloc(sizeof(*ipml), GFP_KERNEL);
935 	if (ipml == NULL)
936 		return -ENOMEM;
937 
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,
943 					     &props);
944 	if (IS_ERR(ipml->bd)) {
945 		retval = PTR_ERR(ipml->bd);
946 		goto out_bd;
947 	}
948 
949 	ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN,
950 				&cmpc_rfkill_ops, acpi->handle);
951 	/*
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
954 	 * dereference it.
955 	 */
956 	if (ipml->rf) {
957 		retval = rfkill_register(ipml->rf);
958 		if (retval) {
959 			rfkill_destroy(ipml->rf);
960 			ipml->rf = NULL;
961 		}
962 	}
963 
964 	dev_set_drvdata(&acpi->dev, ipml);
965 	return 0;
966 
967 out_bd:
968 	kfree(ipml);
969 	return retval;
970 }
971 
972 static void cmpc_ipml_remove(struct acpi_device *acpi)
973 {
974 	struct ipml200_dev *ipml;
975 
976 	ipml = dev_get_drvdata(&acpi->dev);
977 
978 	backlight_device_unregister(ipml->bd);
979 
980 	if (ipml->rf) {
981 		rfkill_unregister(ipml->rf);
982 		rfkill_destroy(ipml->rf);
983 	}
984 
985 	kfree(ipml);
986 }
987 
988 static const struct acpi_device_id cmpc_ipml_device_ids[] = {
989 	{CMPC_IPML_HID, 0},
990 	{"", 0}
991 };
992 
993 static struct acpi_driver cmpc_ipml_acpi_driver = {
994 	.name = "cmpc",
995 	.class = "cmpc",
996 	.ids = cmpc_ipml_device_ids,
997 	.ops = {
998 		.add = cmpc_ipml_add,
999 		.remove = cmpc_ipml_remove
1000 	}
1001 };
1002 
1003 
1004 /*
1005  * Extra keys code.
1006  */
1007 static int cmpc_keys_codes[] = {
1008 	KEY_UNKNOWN,
1009 	KEY_WLAN,
1010 	KEY_SWITCHVIDEOMODE,
1011 	KEY_BRIGHTNESSDOWN,
1012 	KEY_BRIGHTNESSUP,
1013 	KEY_VENDOR,
1014 	KEY_UNKNOWN,
1015 	KEY_CAMERA,
1016 	KEY_BACK,
1017 	KEY_FORWARD,
1018 	KEY_UNKNOWN,
1019 	KEY_WLAN, /* NL3: 0x8b (press), 0x9b (release) */
1020 	KEY_MAX
1021 };
1022 
1023 static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
1024 {
1025 	struct input_dev *inputdev;
1026 	int code = KEY_MAX;
1027 
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);
1033 }
1034 
1035 static void cmpc_keys_idev_init(struct input_dev *inputdev)
1036 {
1037 	int i;
1038 
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);
1042 }
1043 
1044 static int cmpc_keys_add(struct acpi_device *acpi)
1045 {
1046 	return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
1047 					   cmpc_keys_idev_init);
1048 }
1049 
1050 static void cmpc_keys_remove(struct acpi_device *acpi)
1051 {
1052 	cmpc_remove_acpi_notify_device(acpi);
1053 }
1054 
1055 static const struct acpi_device_id cmpc_keys_device_ids[] = {
1056 	{CMPC_KEYS_HID, 0},
1057 	{"", 0}
1058 };
1059 
1060 static struct acpi_driver cmpc_keys_acpi_driver = {
1061 	.name = "cmpc_keys",
1062 	.class = "cmpc_keys",
1063 	.ids = cmpc_keys_device_ids,
1064 	.ops = {
1065 		.add = cmpc_keys_add,
1066 		.remove = cmpc_keys_remove,
1067 		.notify = cmpc_keys_handler,
1068 	}
1069 };
1070 
1071 
1072 /*
1073  * General init/exit code.
1074  */
1075 
1076 static int cmpc_init(void)
1077 {
1078 	int r;
1079 
1080 	r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
1081 	if (r)
1082 		goto failed_keys;
1083 
1084 	r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver);
1085 	if (r)
1086 		goto failed_bl;
1087 
1088 	r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
1089 	if (r)
1090 		goto failed_tablet;
1091 
1092 	r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
1093 	if (r)
1094 		goto failed_accel;
1095 
1096 	r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4);
1097 	if (r)
1098 		goto failed_accel_v4;
1099 
1100 	return r;
1101 
1102 failed_accel_v4:
1103 	acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
1104 
1105 failed_accel:
1106 	acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
1107 
1108 failed_tablet:
1109 	acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
1110 
1111 failed_bl:
1112 	acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
1113 
1114 failed_keys:
1115 	return r;
1116 }
1117 
1118 static void cmpc_exit(void)
1119 {
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);
1125 }
1126 
1127 module_init(cmpc_init);
1128 module_exit(cmpc_exit);
1129 
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},
1134 	{CMPC_IPML_HID, 0},
1135 	{CMPC_KEYS_HID, 0},
1136 	{"", 0}
1137 };
1138 
1139 MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
1140 MODULE_DESCRIPTION("Support for Intel Classmate PC ACPI devices");
1141 MODULE_LICENSE("GPL");
1142