[go: nahoru, domu]

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