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