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/slab.h>
23 #include <linux/workqueue.h>
24 #include <acpi/acpi_drivers.h>
25 #include <linux/backlight.h>
26 #include <linux/input.h>
28 MODULE_LICENSE("GPL");
35 #define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
38 #define CMPC_ACCEL_HID "ACCE0000"
39 #define CMPC_TABLET_HID "TBLT0000"
40 #define CMPC_BL_HID "IPML200"
41 #define CMPC_KEYS_HID "FnBT0000"
44 * Generic input device code.
47 typedef void (*input_device_init
)(struct input_dev
*dev
);
49 static int cmpc_add_acpi_notify_device(struct acpi_device
*acpi
, char *name
,
50 input_device_init idev_init
)
52 struct input_dev
*inputdev
;
55 inputdev
= input_allocate_device();
58 inputdev
->name
= name
;
59 inputdev
->dev
.parent
= &acpi
->dev
;
61 error
= input_register_device(inputdev
);
63 input_free_device(inputdev
);
66 dev_set_drvdata(&acpi
->dev
, inputdev
);
70 static int cmpc_remove_acpi_notify_device(struct acpi_device
*acpi
)
72 struct input_dev
*inputdev
= dev_get_drvdata(&acpi
->dev
);
73 input_unregister_device(inputdev
);
80 static acpi_status
cmpc_start_accel(acpi_handle handle
)
82 union acpi_object param
[2];
83 struct acpi_object_list input
;
86 param
[0].type
= ACPI_TYPE_INTEGER
;
87 param
[0].integer
.value
= 0x3;
88 param
[1].type
= ACPI_TYPE_INTEGER
;
90 input
.pointer
= param
;
91 status
= acpi_evaluate_object(handle
, "ACMD", &input
, NULL
);
95 static acpi_status
cmpc_stop_accel(acpi_handle handle
)
97 union acpi_object param
[2];
98 struct acpi_object_list input
;
101 param
[0].type
= ACPI_TYPE_INTEGER
;
102 param
[0].integer
.value
= 0x4;
103 param
[1].type
= ACPI_TYPE_INTEGER
;
105 input
.pointer
= param
;
106 status
= acpi_evaluate_object(handle
, "ACMD", &input
, NULL
);
110 static acpi_status
cmpc_accel_set_sensitivity(acpi_handle handle
, int val
)
112 union acpi_object param
[2];
113 struct acpi_object_list input
;
115 param
[0].type
= ACPI_TYPE_INTEGER
;
116 param
[0].integer
.value
= 0x02;
117 param
[1].type
= ACPI_TYPE_INTEGER
;
118 param
[1].integer
.value
= val
;
120 input
.pointer
= param
;
121 return acpi_evaluate_object(handle
, "ACMD", &input
, NULL
);
124 static acpi_status
cmpc_get_accel(acpi_handle handle
,
129 union acpi_object param
[2];
130 struct acpi_object_list input
;
131 struct acpi_buffer output
= { ACPI_ALLOCATE_BUFFER
, 0 };
135 param
[0].type
= ACPI_TYPE_INTEGER
;
136 param
[0].integer
.value
= 0x01;
137 param
[1].type
= ACPI_TYPE_INTEGER
;
139 input
.pointer
= param
;
140 status
= acpi_evaluate_object(handle
, "ACMD", &input
, &output
);
141 if (ACPI_SUCCESS(status
)) {
142 union acpi_object
*obj
;
143 obj
= output
.pointer
;
144 locs
= obj
->buffer
.pointer
;
148 kfree(output
.pointer
);
153 static void cmpc_accel_handler(struct acpi_device
*dev
, u32 event
)
156 unsigned char x
, y
, z
;
159 status
= cmpc_get_accel(dev
->handle
, &x
, &y
, &z
);
160 if (ACPI_SUCCESS(status
)) {
161 struct input_dev
*inputdev
= dev_get_drvdata(&dev
->dev
);
163 input_report_abs(inputdev
, ABS_X
, x
);
164 input_report_abs(inputdev
, ABS_Y
, y
);
165 input_report_abs(inputdev
, ABS_Z
, z
);
166 input_sync(inputdev
);
171 static ssize_t
cmpc_accel_sensitivity_show(struct device
*dev
,
172 struct device_attribute
*attr
,
175 struct acpi_device
*acpi
;
176 struct input_dev
*inputdev
;
177 struct cmpc_accel
*accel
;
179 acpi
= to_acpi_device(dev
);
180 inputdev
= dev_get_drvdata(&acpi
->dev
);
181 accel
= dev_get_drvdata(&inputdev
->dev
);
183 return sprintf(buf
, "%d\n", accel
->sensitivity
);
186 static ssize_t
cmpc_accel_sensitivity_store(struct device
*dev
,
187 struct device_attribute
*attr
,
188 const char *buf
, size_t count
)
190 struct acpi_device
*acpi
;
191 struct input_dev
*inputdev
;
192 struct cmpc_accel
*accel
;
193 unsigned long sensitivity
;
196 acpi
= to_acpi_device(dev
);
197 inputdev
= dev_get_drvdata(&acpi
->dev
);
198 accel
= dev_get_drvdata(&inputdev
->dev
);
200 r
= strict_strtoul(buf
, 0, &sensitivity
);
204 accel
->sensitivity
= sensitivity
;
205 cmpc_accel_set_sensitivity(acpi
->handle
, sensitivity
);
207 return strnlen(buf
, count
);
210 struct device_attribute cmpc_accel_sensitivity_attr
= {
211 .attr
= { .name
= "sensitivity", .mode
= 0660 },
212 .show
= cmpc_accel_sensitivity_show
,
213 .store
= cmpc_accel_sensitivity_store
216 static int cmpc_accel_open(struct input_dev
*input
)
218 struct acpi_device
*acpi
;
220 acpi
= to_acpi_device(input
->dev
.parent
);
221 if (ACPI_SUCCESS(cmpc_start_accel(acpi
->handle
)))
226 static void cmpc_accel_close(struct input_dev
*input
)
228 struct acpi_device
*acpi
;
230 acpi
= to_acpi_device(input
->dev
.parent
);
231 cmpc_stop_accel(acpi
->handle
);
234 static void cmpc_accel_idev_init(struct input_dev
*inputdev
)
236 set_bit(EV_ABS
, inputdev
->evbit
);
237 input_set_abs_params(inputdev
, ABS_X
, 0, 255, 8, 0);
238 input_set_abs_params(inputdev
, ABS_Y
, 0, 255, 8, 0);
239 input_set_abs_params(inputdev
, ABS_Z
, 0, 255, 8, 0);
240 inputdev
->open
= cmpc_accel_open
;
241 inputdev
->close
= cmpc_accel_close
;
244 static int cmpc_accel_add(struct acpi_device
*acpi
)
247 struct input_dev
*inputdev
;
248 struct cmpc_accel
*accel
;
250 accel
= kmalloc(sizeof(*accel
), GFP_KERNEL
);
254 accel
->sensitivity
= CMPC_ACCEL_SENSITIVITY_DEFAULT
;
255 cmpc_accel_set_sensitivity(acpi
->handle
, accel
->sensitivity
);
257 error
= device_create_file(&acpi
->dev
, &cmpc_accel_sensitivity_attr
);
261 error
= cmpc_add_acpi_notify_device(acpi
, "cmpc_accel",
262 cmpc_accel_idev_init
);
266 inputdev
= dev_get_drvdata(&acpi
->dev
);
267 dev_set_drvdata(&inputdev
->dev
, accel
);
272 device_remove_file(&acpi
->dev
, &cmpc_accel_sensitivity_attr
);
278 static int cmpc_accel_remove(struct acpi_device
*acpi
, int type
)
280 struct input_dev
*inputdev
;
281 struct cmpc_accel
*accel
;
283 inputdev
= dev_get_drvdata(&acpi
->dev
);
284 accel
= dev_get_drvdata(&inputdev
->dev
);
286 device_remove_file(&acpi
->dev
, &cmpc_accel_sensitivity_attr
);
287 return cmpc_remove_acpi_notify_device(acpi
);
290 static const struct acpi_device_id cmpc_accel_device_ids
[] = {
295 static struct acpi_driver cmpc_accel_acpi_driver
= {
296 .owner
= THIS_MODULE
,
297 .name
= "cmpc_accel",
298 .class = "cmpc_accel",
299 .ids
= cmpc_accel_device_ids
,
301 .add
= cmpc_accel_add
,
302 .remove
= cmpc_accel_remove
,
303 .notify
= cmpc_accel_handler
,
311 static acpi_status
cmpc_get_tablet(acpi_handle handle
,
312 unsigned long long *value
)
314 union acpi_object param
;
315 struct acpi_object_list input
;
316 unsigned long long output
;
319 param
.type
= ACPI_TYPE_INTEGER
;
320 param
.integer
.value
= 0x01;
322 input
.pointer
= ¶m
;
323 status
= acpi_evaluate_integer(handle
, "TCMD", &input
, &output
);
324 if (ACPI_SUCCESS(status
))
329 static void cmpc_tablet_handler(struct acpi_device
*dev
, u32 event
)
331 unsigned long long val
= 0;
332 struct input_dev
*inputdev
= dev_get_drvdata(&dev
->dev
);
335 if (ACPI_SUCCESS(cmpc_get_tablet(dev
->handle
, &val
)))
336 input_report_switch(inputdev
, SW_TABLET_MODE
, !val
);
340 static void cmpc_tablet_idev_init(struct input_dev
*inputdev
)
342 unsigned long long val
= 0;
343 struct acpi_device
*acpi
;
345 set_bit(EV_SW
, inputdev
->evbit
);
346 set_bit(SW_TABLET_MODE
, inputdev
->swbit
);
348 acpi
= to_acpi_device(inputdev
->dev
.parent
);
349 if (ACPI_SUCCESS(cmpc_get_tablet(acpi
->handle
, &val
)))
350 input_report_switch(inputdev
, SW_TABLET_MODE
, !val
);
353 static int cmpc_tablet_add(struct acpi_device
*acpi
)
355 return cmpc_add_acpi_notify_device(acpi
, "cmpc_tablet",
356 cmpc_tablet_idev_init
);
359 static int cmpc_tablet_remove(struct acpi_device
*acpi
, int type
)
361 return cmpc_remove_acpi_notify_device(acpi
);
364 static int cmpc_tablet_resume(struct acpi_device
*acpi
)
366 struct input_dev
*inputdev
= dev_get_drvdata(&acpi
->dev
);
367 unsigned long long val
= 0;
368 if (ACPI_SUCCESS(cmpc_get_tablet(acpi
->handle
, &val
)))
369 input_report_switch(inputdev
, SW_TABLET_MODE
, !val
);
373 static const struct acpi_device_id cmpc_tablet_device_ids
[] = {
374 {CMPC_TABLET_HID
, 0},
378 static struct acpi_driver cmpc_tablet_acpi_driver
= {
379 .owner
= THIS_MODULE
,
380 .name
= "cmpc_tablet",
381 .class = "cmpc_tablet",
382 .ids
= cmpc_tablet_device_ids
,
384 .add
= cmpc_tablet_add
,
385 .remove
= cmpc_tablet_remove
,
386 .resume
= cmpc_tablet_resume
,
387 .notify
= cmpc_tablet_handler
,
396 static acpi_status
cmpc_get_brightness(acpi_handle handle
,
397 unsigned long long *value
)
399 union acpi_object param
;
400 struct acpi_object_list input
;
401 unsigned long long output
;
404 param
.type
= ACPI_TYPE_INTEGER
;
405 param
.integer
.value
= 0xC0;
407 input
.pointer
= ¶m
;
408 status
= acpi_evaluate_integer(handle
, "GRDI", &input
, &output
);
409 if (ACPI_SUCCESS(status
))
414 static acpi_status
cmpc_set_brightness(acpi_handle handle
,
415 unsigned long long value
)
417 union acpi_object param
[2];
418 struct acpi_object_list input
;
420 unsigned long long output
;
422 param
[0].type
= ACPI_TYPE_INTEGER
;
423 param
[0].integer
.value
= 0xC0;
424 param
[1].type
= ACPI_TYPE_INTEGER
;
425 param
[1].integer
.value
= value
;
427 input
.pointer
= param
;
428 status
= acpi_evaluate_integer(handle
, "GWRI", &input
, &output
);
432 static int cmpc_bl_get_brightness(struct backlight_device
*bd
)
436 unsigned long long brightness
;
438 handle
= bl_get_data(bd
);
439 status
= cmpc_get_brightness(handle
, &brightness
);
440 if (ACPI_SUCCESS(status
))
446 static int cmpc_bl_update_status(struct backlight_device
*bd
)
451 handle
= bl_get_data(bd
);
452 status
= cmpc_set_brightness(handle
, bd
->props
.brightness
);
453 if (ACPI_SUCCESS(status
))
459 static const struct backlight_ops cmpc_bl_ops
= {
460 .get_brightness
= cmpc_bl_get_brightness
,
461 .update_status
= cmpc_bl_update_status
464 static int cmpc_bl_add(struct acpi_device
*acpi
)
466 struct backlight_properties props
;
467 struct backlight_device
*bd
;
469 memset(&props
, 0, sizeof(struct backlight_properties
));
470 props
.max_brightness
= 7;
471 bd
= backlight_device_register("cmpc_bl", &acpi
->dev
, acpi
->handle
,
472 &cmpc_bl_ops
, &props
);
475 dev_set_drvdata(&acpi
->dev
, bd
);
479 static int cmpc_bl_remove(struct acpi_device
*acpi
, int type
)
481 struct backlight_device
*bd
;
483 bd
= dev_get_drvdata(&acpi
->dev
);
484 backlight_device_unregister(bd
);
488 static const struct acpi_device_id cmpc_bl_device_ids
[] = {
493 static struct acpi_driver cmpc_bl_acpi_driver
= {
494 .owner
= THIS_MODULE
,
497 .ids
= cmpc_bl_device_ids
,
500 .remove
= cmpc_bl_remove
508 static int cmpc_keys_codes
[] = {
522 static void cmpc_keys_handler(struct acpi_device
*dev
, u32 event
)
524 struct input_dev
*inputdev
;
527 if ((event
& 0x0F) < ARRAY_SIZE(cmpc_keys_codes
))
528 code
= cmpc_keys_codes
[event
& 0x0F];
529 inputdev
= dev_get_drvdata(&dev
->dev
);;
530 input_report_key(inputdev
, code
, !(event
& 0x10));
533 static void cmpc_keys_idev_init(struct input_dev
*inputdev
)
537 set_bit(EV_KEY
, inputdev
->evbit
);
538 for (i
= 0; cmpc_keys_codes
[i
] != KEY_MAX
; i
++)
539 set_bit(cmpc_keys_codes
[i
], inputdev
->keybit
);
542 static int cmpc_keys_add(struct acpi_device
*acpi
)
544 return cmpc_add_acpi_notify_device(acpi
, "cmpc_keys",
545 cmpc_keys_idev_init
);
548 static int cmpc_keys_remove(struct acpi_device
*acpi
, int type
)
550 return cmpc_remove_acpi_notify_device(acpi
);
553 static const struct acpi_device_id cmpc_keys_device_ids
[] = {
558 static struct acpi_driver cmpc_keys_acpi_driver
= {
559 .owner
= THIS_MODULE
,
561 .class = "cmpc_keys",
562 .ids
= cmpc_keys_device_ids
,
564 .add
= cmpc_keys_add
,
565 .remove
= cmpc_keys_remove
,
566 .notify
= cmpc_keys_handler
,
572 * General init/exit code.
575 static int cmpc_init(void)
579 r
= acpi_bus_register_driver(&cmpc_keys_acpi_driver
);
583 r
= acpi_bus_register_driver(&cmpc_bl_acpi_driver
);
587 r
= acpi_bus_register_driver(&cmpc_tablet_acpi_driver
);
591 r
= acpi_bus_register_driver(&cmpc_accel_acpi_driver
);
598 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver
);
601 acpi_bus_unregister_driver(&cmpc_bl_acpi_driver
);
604 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver
);
610 static void cmpc_exit(void)
612 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver
);
613 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver
);
614 acpi_bus_unregister_driver(&cmpc_bl_acpi_driver
);
615 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver
);
618 module_init(cmpc_init
);
619 module_exit(cmpc_exit
);
621 static const struct acpi_device_id cmpc_device_ids
[] = {
623 {CMPC_TABLET_HID
, 0},
629 MODULE_DEVICE_TABLE(acpi
, cmpc_device_ids
);