• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/platform/x86/
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};
35
36#define CMPC_ACCEL_SENSITIVITY_DEFAULT		5
37
38
39#define CMPC_ACCEL_HID		"ACCE0000"
40#define CMPC_TABLET_HID		"TBLT0000"
41#define CMPC_IPML_HID	"IPML200"
42#define CMPC_KEYS_HID		"FnBT0000"
43
44/*
45 * Generic input device code.
46 */
47
48typedef void (*input_device_init)(struct input_dev *dev);
49
50static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,
51				       input_device_init idev_init)
52{
53	struct input_dev *inputdev;
54	int error;
55
56	inputdev = input_allocate_device();
57	if (!inputdev)
58		return -ENOMEM;
59	inputdev->name = name;
60	inputdev->dev.parent = &acpi->dev;
61	idev_init(inputdev);
62	error = input_register_device(inputdev);
63	if (error) {
64		input_free_device(inputdev);
65		return error;
66	}
67	dev_set_drvdata(&acpi->dev, inputdev);
68	return 0;
69}
70
71static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
72{
73	struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
74	input_unregister_device(inputdev);
75	return 0;
76}
77
78/*
79 * Accelerometer code.
80 */
81static acpi_status cmpc_start_accel(acpi_handle handle)
82{
83	union acpi_object param[2];
84	struct acpi_object_list input;
85	acpi_status status;
86
87	param[0].type = ACPI_TYPE_INTEGER;
88	param[0].integer.value = 0x3;
89	param[1].type = ACPI_TYPE_INTEGER;
90	input.count = 2;
91	input.pointer = param;
92	status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
93	return status;
94}
95
96static acpi_status cmpc_stop_accel(acpi_handle handle)
97{
98	union acpi_object param[2];
99	struct acpi_object_list input;
100	acpi_status status;
101
102	param[0].type = ACPI_TYPE_INTEGER;
103	param[0].integer.value = 0x4;
104	param[1].type = ACPI_TYPE_INTEGER;
105	input.count = 2;
106	input.pointer = param;
107	status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
108	return status;
109}
110
111static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val)
112{
113	union acpi_object param[2];
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	input.count = 2;
121	input.pointer = param;
122	return acpi_evaluate_object(handle, "ACMD", &input, NULL);
123}
124
125static acpi_status cmpc_get_accel(acpi_handle handle,
126				  unsigned char *x,
127				  unsigned char *y,
128				  unsigned char *z)
129{
130	union acpi_object param[2];
131	struct acpi_object_list input;
132	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, 0 };
133	unsigned char *locs;
134	acpi_status status;
135
136	param[0].type = ACPI_TYPE_INTEGER;
137	param[0].integer.value = 0x01;
138	param[1].type = ACPI_TYPE_INTEGER;
139	input.count = 2;
140	input.pointer = param;
141	status = acpi_evaluate_object(handle, "ACMD", &input, &output);
142	if (ACPI_SUCCESS(status)) {
143		union acpi_object *obj;
144		obj = output.pointer;
145		locs = obj->buffer.pointer;
146		*x = locs[0];
147		*y = locs[1];
148		*z = locs[2];
149		kfree(output.pointer);
150	}
151	return status;
152}
153
154static void cmpc_accel_handler(struct acpi_device *dev, u32 event)
155{
156	if (event == 0x81) {
157		unsigned char x, y, z;
158		acpi_status status;
159
160		status = cmpc_get_accel(dev->handle, &x, &y, &z);
161		if (ACPI_SUCCESS(status)) {
162			struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
163
164			input_report_abs(inputdev, ABS_X, x);
165			input_report_abs(inputdev, ABS_Y, y);
166			input_report_abs(inputdev, ABS_Z, z);
167			input_sync(inputdev);
168		}
169	}
170}
171
172static ssize_t cmpc_accel_sensitivity_show(struct device *dev,
173					   struct device_attribute *attr,
174					   char *buf)
175{
176	struct acpi_device *acpi;
177	struct input_dev *inputdev;
178	struct cmpc_accel *accel;
179
180	acpi = to_acpi_device(dev);
181	inputdev = dev_get_drvdata(&acpi->dev);
182	accel = dev_get_drvdata(&inputdev->dev);
183
184	return sprintf(buf, "%d\n", accel->sensitivity);
185}
186
187static ssize_t cmpc_accel_sensitivity_store(struct device *dev,
188					    struct device_attribute *attr,
189					    const char *buf, size_t count)
190{
191	struct acpi_device *acpi;
192	struct input_dev *inputdev;
193	struct cmpc_accel *accel;
194	unsigned long sensitivity;
195	int r;
196
197	acpi = to_acpi_device(dev);
198	inputdev = dev_get_drvdata(&acpi->dev);
199	accel = dev_get_drvdata(&inputdev->dev);
200
201	r = strict_strtoul(buf, 0, &sensitivity);
202	if (r)
203		return r;
204
205	accel->sensitivity = sensitivity;
206	cmpc_accel_set_sensitivity(acpi->handle, sensitivity);
207
208	return strnlen(buf, count);
209}
210
211static struct device_attribute cmpc_accel_sensitivity_attr = {
212	.attr = { .name = "sensitivity", .mode = 0660 },
213	.show = cmpc_accel_sensitivity_show,
214	.store = cmpc_accel_sensitivity_store
215};
216
217static int cmpc_accel_open(struct input_dev *input)
218{
219	struct acpi_device *acpi;
220
221	acpi = to_acpi_device(input->dev.parent);
222	if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle)))
223		return 0;
224	return -EIO;
225}
226
227static void cmpc_accel_close(struct input_dev *input)
228{
229	struct acpi_device *acpi;
230
231	acpi = to_acpi_device(input->dev.parent);
232	cmpc_stop_accel(acpi->handle);
233}
234
235static void cmpc_accel_idev_init(struct input_dev *inputdev)
236{
237	set_bit(EV_ABS, inputdev->evbit);
238	input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0);
239	input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0);
240	input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0);
241	inputdev->open = cmpc_accel_open;
242	inputdev->close = cmpc_accel_close;
243}
244
245static int cmpc_accel_add(struct acpi_device *acpi)
246{
247	int error;
248	struct input_dev *inputdev;
249	struct cmpc_accel *accel;
250
251	accel = kmalloc(sizeof(*accel), GFP_KERNEL);
252	if (!accel)
253		return -ENOMEM;
254
255	accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
256	cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity);
257
258	error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
259	if (error)
260		goto failed_file;
261
262	error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",
263					    cmpc_accel_idev_init);
264	if (error)
265		goto failed_input;
266
267	inputdev = dev_get_drvdata(&acpi->dev);
268	dev_set_drvdata(&inputdev->dev, accel);
269
270	return 0;
271
272failed_input:
273	device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
274failed_file:
275	kfree(accel);
276	return error;
277}
278
279static int cmpc_accel_remove(struct acpi_device *acpi, int type)
280{
281	struct input_dev *inputdev;
282	struct cmpc_accel *accel;
283
284	inputdev = dev_get_drvdata(&acpi->dev);
285	accel = dev_get_drvdata(&inputdev->dev);
286
287	device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
288	return cmpc_remove_acpi_notify_device(acpi);
289}
290
291static const struct acpi_device_id cmpc_accel_device_ids[] = {
292	{CMPC_ACCEL_HID, 0},
293	{"", 0}
294};
295
296static struct acpi_driver cmpc_accel_acpi_driver = {
297	.owner = THIS_MODULE,
298	.name = "cmpc_accel",
299	.class = "cmpc_accel",
300	.ids = cmpc_accel_device_ids,
301	.ops = {
302		.add = cmpc_accel_add,
303		.remove = cmpc_accel_remove,
304		.notify = cmpc_accel_handler,
305	}
306};
307
308
309/*
310 * Tablet mode code.
311 */
312static acpi_status cmpc_get_tablet(acpi_handle handle,
313				   unsigned long long *value)
314{
315	union acpi_object param;
316	struct acpi_object_list input;
317	unsigned long long output;
318	acpi_status status;
319
320	param.type = ACPI_TYPE_INTEGER;
321	param.integer.value = 0x01;
322	input.count = 1;
323	input.pointer = &param;
324	status = acpi_evaluate_integer(handle, "TCMD", &input, &output);
325	if (ACPI_SUCCESS(status))
326		*value = output;
327	return status;
328}
329
330static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
331{
332	unsigned long long val = 0;
333	struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
334
335	if (event == 0x81) {
336		if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val)))
337			input_report_switch(inputdev, SW_TABLET_MODE, !val);
338	}
339}
340
341static void cmpc_tablet_idev_init(struct input_dev *inputdev)
342{
343	unsigned long long val = 0;
344	struct acpi_device *acpi;
345
346	set_bit(EV_SW, inputdev->evbit);
347	set_bit(SW_TABLET_MODE, inputdev->swbit);
348
349	acpi = to_acpi_device(inputdev->dev.parent);
350	if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
351		input_report_switch(inputdev, SW_TABLET_MODE, !val);
352}
353
354static int cmpc_tablet_add(struct acpi_device *acpi)
355{
356	return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet",
357					   cmpc_tablet_idev_init);
358}
359
360static int cmpc_tablet_remove(struct acpi_device *acpi, int type)
361{
362	return cmpc_remove_acpi_notify_device(acpi);
363}
364
365static int cmpc_tablet_resume(struct acpi_device *acpi)
366{
367	struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
368	unsigned long long val = 0;
369	if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
370		input_report_switch(inputdev, SW_TABLET_MODE, !val);
371	return 0;
372}
373
374static const struct acpi_device_id cmpc_tablet_device_ids[] = {
375	{CMPC_TABLET_HID, 0},
376	{"", 0}
377};
378
379static struct acpi_driver cmpc_tablet_acpi_driver = {
380	.owner = THIS_MODULE,
381	.name = "cmpc_tablet",
382	.class = "cmpc_tablet",
383	.ids = cmpc_tablet_device_ids,
384	.ops = {
385		.add = cmpc_tablet_add,
386		.remove = cmpc_tablet_remove,
387		.resume = cmpc_tablet_resume,
388		.notify = cmpc_tablet_handler,
389	}
390};
391
392
393/*
394 * Backlight code.
395 */
396
397static acpi_status cmpc_get_brightness(acpi_handle handle,
398				       unsigned long long *value)
399{
400	union acpi_object param;
401	struct acpi_object_list input;
402	unsigned long long output;
403	acpi_status status;
404
405	param.type = ACPI_TYPE_INTEGER;
406	param.integer.value = 0xC0;
407	input.count = 1;
408	input.pointer = &param;
409	status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
410	if (ACPI_SUCCESS(status))
411		*value = output;
412	return status;
413}
414
415static acpi_status cmpc_set_brightness(acpi_handle handle,
416				       unsigned long long value)
417{
418	union acpi_object param[2];
419	struct acpi_object_list input;
420	acpi_status status;
421	unsigned long long output;
422
423	param[0].type = ACPI_TYPE_INTEGER;
424	param[0].integer.value = 0xC0;
425	param[1].type = ACPI_TYPE_INTEGER;
426	param[1].integer.value = value;
427	input.count = 2;
428	input.pointer = param;
429	status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
430	return status;
431}
432
433static int cmpc_bl_get_brightness(struct backlight_device *bd)
434{
435	acpi_status status;
436	acpi_handle handle;
437	unsigned long long brightness;
438
439	handle = bl_get_data(bd);
440	status = cmpc_get_brightness(handle, &brightness);
441	if (ACPI_SUCCESS(status))
442		return brightness;
443	else
444		return -1;
445}
446
447static int cmpc_bl_update_status(struct backlight_device *bd)
448{
449	acpi_status status;
450	acpi_handle handle;
451
452	handle = bl_get_data(bd);
453	status = cmpc_set_brightness(handle, bd->props.brightness);
454	if (ACPI_SUCCESS(status))
455		return 0;
456	else
457		return -1;
458}
459
460static const struct backlight_ops cmpc_bl_ops = {
461	.get_brightness = cmpc_bl_get_brightness,
462	.update_status = cmpc_bl_update_status
463};
464
465/*
466 * RFKILL code.
467 */
468
469static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle,
470					unsigned long long *value)
471{
472	union acpi_object param;
473	struct acpi_object_list input;
474	unsigned long long output;
475	acpi_status status;
476
477	param.type = ACPI_TYPE_INTEGER;
478	param.integer.value = 0xC1;
479	input.count = 1;
480	input.pointer = &param;
481	status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
482	if (ACPI_SUCCESS(status))
483		*value = output;
484	return status;
485}
486
487static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle,
488					unsigned long long value)
489{
490	union acpi_object param[2];
491	struct acpi_object_list input;
492	acpi_status status;
493	unsigned long long output;
494
495	param[0].type = ACPI_TYPE_INTEGER;
496	param[0].integer.value = 0xC1;
497	param[1].type = ACPI_TYPE_INTEGER;
498	param[1].integer.value = value;
499	input.count = 2;
500	input.pointer = param;
501	status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
502	return status;
503}
504
505static void cmpc_rfkill_query(struct rfkill *rfkill, void *data)
506{
507	acpi_status status;
508	acpi_handle handle;
509	unsigned long long state;
510	bool blocked;
511
512	handle = data;
513	status = cmpc_get_rfkill_wlan(handle, &state);
514	if (ACPI_SUCCESS(status)) {
515		blocked = state & 1 ? false : true;
516		rfkill_set_sw_state(rfkill, blocked);
517	}
518}
519
520static int cmpc_rfkill_block(void *data, bool blocked)
521{
522	acpi_status status;
523	acpi_handle handle;
524	unsigned long long state;
525
526	handle = data;
527	status = cmpc_get_rfkill_wlan(handle, &state);
528	if (ACPI_FAILURE(status))
529		return -ENODEV;
530	if (blocked)
531		state &= ~1;
532	else
533		state |= 1;
534	status = cmpc_set_rfkill_wlan(handle, state);
535	if (ACPI_FAILURE(status))
536		return -ENODEV;
537	return 0;
538}
539
540static const struct rfkill_ops cmpc_rfkill_ops = {
541	.query = cmpc_rfkill_query,
542	.set_block = cmpc_rfkill_block,
543};
544
545/*
546 * Common backlight and rfkill code.
547 */
548
549struct ipml200_dev {
550	struct backlight_device *bd;
551	struct rfkill *rf;
552};
553
554static int cmpc_ipml_add(struct acpi_device *acpi)
555{
556	int retval;
557	struct ipml200_dev *ipml;
558	struct backlight_properties props;
559
560	ipml = kmalloc(sizeof(*ipml), GFP_KERNEL);
561	if (ipml == NULL)
562		return -ENOMEM;
563
564	memset(&props, 0, sizeof(struct backlight_properties));
565	props.max_brightness = 7;
566	ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev,
567					     acpi->handle, &cmpc_bl_ops,
568					     &props);
569	if (IS_ERR(ipml->bd)) {
570		retval = PTR_ERR(ipml->bd);
571		goto out_bd;
572	}
573
574	ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN,
575				&cmpc_rfkill_ops, acpi->handle);
576	/*
577	 * If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV).
578	 * This is OK, however, since all other uses of the device will not
579	 * derefence it.
580	 */
581	if (ipml->rf) {
582		retval = rfkill_register(ipml->rf);
583		if (retval) {
584			rfkill_destroy(ipml->rf);
585			ipml->rf = NULL;
586		}
587	}
588
589	dev_set_drvdata(&acpi->dev, ipml);
590	return 0;
591
592out_bd:
593	kfree(ipml);
594	return retval;
595}
596
597static int cmpc_ipml_remove(struct acpi_device *acpi, int type)
598{
599	struct ipml200_dev *ipml;
600
601	ipml = dev_get_drvdata(&acpi->dev);
602
603	backlight_device_unregister(ipml->bd);
604
605	if (ipml->rf) {
606		rfkill_unregister(ipml->rf);
607		rfkill_destroy(ipml->rf);
608	}
609
610	kfree(ipml);
611
612	return 0;
613}
614
615static const struct acpi_device_id cmpc_ipml_device_ids[] = {
616	{CMPC_IPML_HID, 0},
617	{"", 0}
618};
619
620static struct acpi_driver cmpc_ipml_acpi_driver = {
621	.owner = THIS_MODULE,
622	.name = "cmpc",
623	.class = "cmpc",
624	.ids = cmpc_ipml_device_ids,
625	.ops = {
626		.add = cmpc_ipml_add,
627		.remove = cmpc_ipml_remove
628	}
629};
630
631
632/*
633 * Extra keys code.
634 */
635static int cmpc_keys_codes[] = {
636	KEY_UNKNOWN,
637	KEY_WLAN,
638	KEY_SWITCHVIDEOMODE,
639	KEY_BRIGHTNESSDOWN,
640	KEY_BRIGHTNESSUP,
641	KEY_VENDOR,
642	KEY_UNKNOWN,
643	KEY_CAMERA,
644	KEY_BACK,
645	KEY_FORWARD,
646	KEY_MAX
647};
648
649static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
650{
651	struct input_dev *inputdev;
652	int code = KEY_MAX;
653
654	if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
655		code = cmpc_keys_codes[event & 0x0F];
656	inputdev = dev_get_drvdata(&dev->dev);;
657	input_report_key(inputdev, code, !(event & 0x10));
658}
659
660static void cmpc_keys_idev_init(struct input_dev *inputdev)
661{
662	int i;
663
664	set_bit(EV_KEY, inputdev->evbit);
665	for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++)
666		set_bit(cmpc_keys_codes[i], inputdev->keybit);
667}
668
669static int cmpc_keys_add(struct acpi_device *acpi)
670{
671	return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
672					   cmpc_keys_idev_init);
673}
674
675static int cmpc_keys_remove(struct acpi_device *acpi, int type)
676{
677	return cmpc_remove_acpi_notify_device(acpi);
678}
679
680static const struct acpi_device_id cmpc_keys_device_ids[] = {
681	{CMPC_KEYS_HID, 0},
682	{"", 0}
683};
684
685static struct acpi_driver cmpc_keys_acpi_driver = {
686	.owner = THIS_MODULE,
687	.name = "cmpc_keys",
688	.class = "cmpc_keys",
689	.ids = cmpc_keys_device_ids,
690	.ops = {
691		.add = cmpc_keys_add,
692		.remove = cmpc_keys_remove,
693		.notify = cmpc_keys_handler,
694	}
695};
696
697
698/*
699 * General init/exit code.
700 */
701
702static int cmpc_init(void)
703{
704	int r;
705
706	r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
707	if (r)
708		goto failed_keys;
709
710	r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver);
711	if (r)
712		goto failed_bl;
713
714	r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
715	if (r)
716		goto failed_tablet;
717
718	r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
719	if (r)
720		goto failed_accel;
721
722	return r;
723
724failed_accel:
725	acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
726
727failed_tablet:
728	acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
729
730failed_bl:
731	acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
732
733failed_keys:
734	return r;
735}
736
737static void cmpc_exit(void)
738{
739	acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
740	acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
741	acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
742	acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
743}
744
745module_init(cmpc_init);
746module_exit(cmpc_exit);
747
748static const struct acpi_device_id cmpc_device_ids[] = {
749	{CMPC_ACCEL_HID, 0},
750	{CMPC_TABLET_HID, 0},
751	{CMPC_IPML_HID, 0},
752	{CMPC_KEYS_HID, 0},
753	{"", 0}
754};
755
756MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
757