[go: nahoru, domu]

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