2 * Copyright (C) 2009 Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
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.
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.
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.
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>
27 MODULE_LICENSE("GPL");
34 #define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
38 * Generic input device code.
41 typedef void (*input_device_init
)(struct input_dev
*dev
);
43 static int cmpc_add_acpi_notify_device(struct acpi_device
*acpi
, char *name
,
44 input_device_init idev_init
)
46 struct input_dev
*inputdev
;
49 inputdev
= input_allocate_device();
52 inputdev
->name
= name
;
53 inputdev
->dev
.parent
= &acpi
->dev
;
55 error
= input_register_device(inputdev
);
57 input_free_device(inputdev
);
60 dev_set_drvdata(&acpi
->dev
, inputdev
);
64 static int cmpc_remove_acpi_notify_device(struct acpi_device
*acpi
)
66 struct input_dev
*inputdev
= dev_get_drvdata(&acpi
->dev
);
67 input_unregister_device(inputdev
);
74 static acpi_status
cmpc_start_accel(acpi_handle handle
)
76 union acpi_object param
[2];
77 struct acpi_object_list input
;
80 param
[0].type
= ACPI_TYPE_INTEGER
;
81 param
[0].integer
.value
= 0x3;
82 param
[1].type
= ACPI_TYPE_INTEGER
;
84 input
.pointer
= param
;
85 status
= acpi_evaluate_object(handle
, "ACMD", &input
, NULL
);
89 static acpi_status
cmpc_stop_accel(acpi_handle handle
)
91 union acpi_object param
[2];
92 struct acpi_object_list input
;
95 param
[0].type
= ACPI_TYPE_INTEGER
;
96 param
[0].integer
.value
= 0x4;
97 param
[1].type
= ACPI_TYPE_INTEGER
;
99 input
.pointer
= param
;
100 status
= acpi_evaluate_object(handle
, "ACMD", &input
, NULL
);
104 static acpi_status
cmpc_accel_set_sensitivity(acpi_handle handle
, int val
)
106 union acpi_object param
[2];
107 struct acpi_object_list input
;
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
;
114 input
.pointer
= param
;
115 return acpi_evaluate_object(handle
, "ACMD", &input
, NULL
);
118 static acpi_status
cmpc_get_accel(acpi_handle handle
,
123 union acpi_object param
[2];
124 struct acpi_object_list input
;
125 struct acpi_buffer output
= { ACPI_ALLOCATE_BUFFER
, 0 };
129 param
[0].type
= ACPI_TYPE_INTEGER
;
130 param
[0].integer
.value
= 0x01;
131 param
[1].type
= ACPI_TYPE_INTEGER
;
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
;
142 kfree(output
.pointer
);
147 static void cmpc_accel_handler(struct acpi_device
*dev
, u32 event
)
150 unsigned char x
, y
, z
;
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
);
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
);
165 static ssize_t
cmpc_accel_sensitivity_show(struct device
*dev
,
166 struct device_attribute
*attr
,
169 struct acpi_device
*acpi
;
170 struct input_dev
*inputdev
;
171 struct cmpc_accel
*accel
;
173 acpi
= to_acpi_device(dev
);
174 inputdev
= dev_get_drvdata(&acpi
->dev
);
175 accel
= dev_get_drvdata(&inputdev
->dev
);
177 return sprintf(buf
, "%d\n", accel
->sensitivity
);
180 static ssize_t
cmpc_accel_sensitivity_store(struct device
*dev
,
181 struct device_attribute
*attr
,
182 const char *buf
, size_t count
)
184 struct acpi_device
*acpi
;
185 struct input_dev
*inputdev
;
186 struct cmpc_accel
*accel
;
187 unsigned long sensitivity
;
190 acpi
= to_acpi_device(dev
);
191 inputdev
= dev_get_drvdata(&acpi
->dev
);
192 accel
= dev_get_drvdata(&inputdev
->dev
);
194 r
= strict_strtoul(buf
, 0, &sensitivity
);
198 accel
->sensitivity
= sensitivity
;
199 cmpc_accel_set_sensitivity(acpi
->handle
, sensitivity
);
201 return strnlen(buf
, count
);
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
210 static int cmpc_accel_open(struct input_dev
*input
)
212 struct acpi_device
*acpi
;
214 acpi
= to_acpi_device(input
->dev
.parent
);
215 if (ACPI_SUCCESS(cmpc_start_accel(acpi
->handle
)))
220 static void cmpc_accel_close(struct input_dev
*input
)
222 struct acpi_device
*acpi
;
224 acpi
= to_acpi_device(input
->dev
.parent
);
225 cmpc_stop_accel(acpi
->handle
);
228 static void cmpc_accel_idev_init(struct input_dev
*inputdev
)
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
;
238 static int cmpc_accel_add(struct acpi_device
*acpi
)
241 struct input_dev
*inputdev
;
242 struct cmpc_accel
*accel
;
244 accel
= kmalloc(sizeof(*accel
), GFP_KERNEL
);
248 accel
->sensitivity
= CMPC_ACCEL_SENSITIVITY_DEFAULT
;
249 cmpc_accel_set_sensitivity(acpi
->handle
, accel
->sensitivity
);
251 error
= device_create_file(&acpi
->dev
, &cmpc_accel_sensitivity_attr
);
255 error
= cmpc_add_acpi_notify_device(acpi
, "cmpc_accel",
256 cmpc_accel_idev_init
);
260 inputdev
= dev_get_drvdata(&acpi
->dev
);
261 dev_set_drvdata(&inputdev
->dev
, accel
);
266 device_remove_file(&acpi
->dev
, &cmpc_accel_sensitivity_attr
);
272 static int cmpc_accel_remove(struct acpi_device
*acpi
, int type
)
274 struct input_dev
*inputdev
;
275 struct cmpc_accel
*accel
;
277 inputdev
= dev_get_drvdata(&acpi
->dev
);
278 accel
= dev_get_drvdata(&inputdev
->dev
);
280 device_remove_file(&acpi
->dev
, &cmpc_accel_sensitivity_attr
);
281 return cmpc_remove_acpi_notify_device(acpi
);
284 static const struct acpi_device_id cmpc_accel_device_ids
[] = {
288 MODULE_DEVICE_TABLE(acpi
, cmpc_accel_device_ids
);
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
,
296 .add
= cmpc_accel_add
,
297 .remove
= cmpc_accel_remove
,
298 .notify
= cmpc_accel_handler
,
306 static acpi_status
cmpc_get_tablet(acpi_handle handle
,
307 unsigned long long *value
)
309 union acpi_object param
;
310 struct acpi_object_list input
;
311 unsigned long long output
;
314 param
.type
= ACPI_TYPE_INTEGER
;
315 param
.integer
.value
= 0x01;
317 input
.pointer
= ¶m
;
318 status
= acpi_evaluate_integer(handle
, "TCMD", &input
, &output
);
319 if (ACPI_SUCCESS(status
))
324 static void cmpc_tablet_handler(struct acpi_device
*dev
, u32 event
)
326 unsigned long long val
= 0;
327 struct input_dev
*inputdev
= dev_get_drvdata(&dev
->dev
);
330 if (ACPI_SUCCESS(cmpc_get_tablet(dev
->handle
, &val
)))
331 input_report_switch(inputdev
, SW_TABLET_MODE
, !val
);
335 static void cmpc_tablet_idev_init(struct input_dev
*inputdev
)
337 unsigned long long val
= 0;
338 struct acpi_device
*acpi
;
340 set_bit(EV_SW
, inputdev
->evbit
);
341 set_bit(SW_TABLET_MODE
, inputdev
->swbit
);
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
);
348 static int cmpc_tablet_add(struct acpi_device
*acpi
)
350 return cmpc_add_acpi_notify_device(acpi
, "cmpc_tablet",
351 cmpc_tablet_idev_init
);
354 static int cmpc_tablet_remove(struct acpi_device
*acpi
, int type
)
356 return cmpc_remove_acpi_notify_device(acpi
);
359 static int cmpc_tablet_resume(struct acpi_device
*acpi
)
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
);
368 static const struct acpi_device_id cmpc_tablet_device_ids
[] = {
372 MODULE_DEVICE_TABLE(acpi
, cmpc_tablet_device_ids
);
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
,
380 .add
= cmpc_tablet_add
,
381 .remove
= cmpc_tablet_remove
,
382 .resume
= cmpc_tablet_resume
,
383 .notify
= cmpc_tablet_handler
,
392 static acpi_status
cmpc_get_brightness(acpi_handle handle
,
393 unsigned long long *value
)
395 union acpi_object param
;
396 struct acpi_object_list input
;
397 unsigned long long output
;
400 param
.type
= ACPI_TYPE_INTEGER
;
401 param
.integer
.value
= 0xC0;
403 input
.pointer
= ¶m
;
404 status
= acpi_evaluate_integer(handle
, "GRDI", &input
, &output
);
405 if (ACPI_SUCCESS(status
))
410 static acpi_status
cmpc_set_brightness(acpi_handle handle
,
411 unsigned long long value
)
413 union acpi_object param
[2];
414 struct acpi_object_list input
;
416 unsigned long long output
;
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
;
423 input
.pointer
= param
;
424 status
= acpi_evaluate_integer(handle
, "GWRI", &input
, &output
);
428 static int cmpc_bl_get_brightness(struct backlight_device
*bd
)
432 unsigned long long brightness
;
434 handle
= bl_get_data(bd
);
435 status
= cmpc_get_brightness(handle
, &brightness
);
436 if (ACPI_SUCCESS(status
))
442 static int cmpc_bl_update_status(struct backlight_device
*bd
)
447 handle
= bl_get_data(bd
);
448 status
= cmpc_set_brightness(handle
, bd
->props
.brightness
);
449 if (ACPI_SUCCESS(status
))
455 static struct backlight_ops cmpc_bl_ops
= {
456 .get_brightness
= cmpc_bl_get_brightness
,
457 .update_status
= cmpc_bl_update_status
460 static int cmpc_bl_add(struct acpi_device
*acpi
)
462 struct backlight_device
*bd
;
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
);
471 static int cmpc_bl_remove(struct acpi_device
*acpi
, int type
)
473 struct backlight_device
*bd
;
475 bd
= dev_get_drvdata(&acpi
->dev
);
476 backlight_device_unregister(bd
);
480 static const struct acpi_device_id cmpc_device_ids
[] = {
484 MODULE_DEVICE_TABLE(acpi
, cmpc_device_ids
);
486 static struct acpi_driver cmpc_bl_acpi_driver
= {
487 .owner
= THIS_MODULE
,
490 .ids
= cmpc_device_ids
,
493 .remove
= cmpc_bl_remove
501 static int cmpc_keys_codes
[] = {
511 static void cmpc_keys_handler(struct acpi_device
*dev
, u32 event
)
513 struct input_dev
*inputdev
;
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));
522 static void cmpc_keys_idev_init(struct input_dev
*inputdev
)
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
);
531 static int cmpc_keys_add(struct acpi_device
*acpi
)
533 return cmpc_add_acpi_notify_device(acpi
, "cmpc_keys",
534 cmpc_keys_idev_init
);
537 static int cmpc_keys_remove(struct acpi_device
*acpi
, int type
)
539 return cmpc_remove_acpi_notify_device(acpi
);
542 static const struct acpi_device_id cmpc_keys_device_ids
[] = {
546 MODULE_DEVICE_TABLE(acpi
, cmpc_keys_device_ids
);
548 static struct acpi_driver cmpc_keys_acpi_driver
= {
549 .owner
= THIS_MODULE
,
551 .class = "cmpc_keys",
552 .ids
= cmpc_keys_device_ids
,
554 .add
= cmpc_keys_add
,
555 .remove
= cmpc_keys_remove
,
556 .notify
= cmpc_keys_handler
,
562 * General init/exit code.
565 static int cmpc_init(void)
569 r
= acpi_bus_register_driver(&cmpc_keys_acpi_driver
);
573 r
= acpi_bus_register_driver(&cmpc_bl_acpi_driver
);
577 r
= acpi_bus_register_driver(&cmpc_tablet_acpi_driver
);
581 r
= acpi_bus_register_driver(&cmpc_accel_acpi_driver
);
588 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver
);
591 acpi_bus_unregister_driver(&cmpc_bl_acpi_driver
);
594 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver
);
600 static void cmpc_exit(void)
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
);
608 module_init(cmpc_init
);
609 module_exit(cmpc_exit
);