Linux 2.6.33
[pohmelfs.git] / drivers / platform / x86 / classmate-laptop.c
blobed90082cdf1db82ec7ed9d17782369088590e9b5
1 /*
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");
30 struct cmpc_accel {
31 int sensitivity;
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;
47 int error;
49 inputdev = input_allocate_device();
50 if (!inputdev)
51 return -ENOMEM;
52 inputdev->name = name;
53 inputdev->dev.parent = &acpi->dev;
54 idev_init(inputdev);
55 error = input_register_device(inputdev);
56 if (error) {
57 input_free_device(inputdev);
58 return error;
60 dev_set_drvdata(&acpi->dev, inputdev);
61 return 0;
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);
68 return 0;
72 * Accelerometer code.
74 static acpi_status cmpc_start_accel(acpi_handle handle)
76 union acpi_object param[2];
77 struct acpi_object_list input;
78 acpi_status status;
80 param[0].type = ACPI_TYPE_INTEGER;
81 param[0].integer.value = 0x3;
82 param[1].type = ACPI_TYPE_INTEGER;
83 input.count = 2;
84 input.pointer = param;
85 status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
86 return status;
89 static acpi_status cmpc_stop_accel(acpi_handle handle)
91 union acpi_object param[2];
92 struct acpi_object_list input;
93 acpi_status status;
95 param[0].type = ACPI_TYPE_INTEGER;
96 param[0].integer.value = 0x4;
97 param[1].type = ACPI_TYPE_INTEGER;
98 input.count = 2;
99 input.pointer = param;
100 status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
101 return status;
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;
113 input.count = 2;
114 input.pointer = param;
115 return acpi_evaluate_object(handle, "ACMD", &input, NULL);
118 static acpi_status cmpc_get_accel(acpi_handle handle,
119 unsigned char *x,
120 unsigned char *y,
121 unsigned char *z)
123 union acpi_object param[2];
124 struct acpi_object_list input;
125 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, 0 };
126 unsigned char *locs;
127 acpi_status status;
129 param[0].type = ACPI_TYPE_INTEGER;
130 param[0].integer.value = 0x01;
131 param[1].type = ACPI_TYPE_INTEGER;
132 input.count = 2;
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;
139 *x = locs[0];
140 *y = locs[1];
141 *z = locs[2];
142 kfree(output.pointer);
144 return status;
147 static void cmpc_accel_handler(struct acpi_device *dev, u32 event)
149 if (event == 0x81) {
150 unsigned char x, y, z;
151 acpi_status status;
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,
167 char *buf)
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;
188 int r;
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);
195 if (r)
196 return r;
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)))
216 return 0;
217 return -EIO;
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)
240 int error;
241 struct input_dev *inputdev;
242 struct cmpc_accel *accel;
244 accel = kmalloc(sizeof(*accel), GFP_KERNEL);
245 if (!accel)
246 return -ENOMEM;
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);
252 if (error)
253 goto failed_file;
255 error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",
256 cmpc_accel_idev_init);
257 if (error)
258 goto failed_input;
260 inputdev = dev_get_drvdata(&acpi->dev);
261 dev_set_drvdata(&inputdev->dev, accel);
263 return 0;
265 failed_input:
266 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
267 failed_file:
268 kfree(accel);
269 return error;
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[] = {
285 {"ACCE0000", 0},
286 {"", 0}
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,
295 .ops = {
296 .add = cmpc_accel_add,
297 .remove = cmpc_accel_remove,
298 .notify = cmpc_accel_handler,
304 * Tablet mode code.
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;
312 acpi_status status;
314 param.type = ACPI_TYPE_INTEGER;
315 param.integer.value = 0x01;
316 input.count = 1;
317 input.pointer = &param;
318 status = acpi_evaluate_integer(handle, "TCMD", &input, &output);
319 if (ACPI_SUCCESS(status))
320 *value = output;
321 return 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);
329 if (event == 0x81) {
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);
365 return 0;
368 static const struct acpi_device_id cmpc_tablet_device_ids[] = {
369 {"TBLT0000", 0},
370 {"", 0}
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,
379 .ops = {
380 .add = cmpc_tablet_add,
381 .remove = cmpc_tablet_remove,
382 .resume = cmpc_tablet_resume,
383 .notify = cmpc_tablet_handler,
389 * Backlight code.
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;
398 acpi_status status;
400 param.type = ACPI_TYPE_INTEGER;
401 param.integer.value = 0xC0;
402 input.count = 1;
403 input.pointer = &param;
404 status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
405 if (ACPI_SUCCESS(status))
406 *value = output;
407 return 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;
415 acpi_status status;
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;
422 input.count = 2;
423 input.pointer = param;
424 status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
425 return status;
428 static int cmpc_bl_get_brightness(struct backlight_device *bd)
430 acpi_status status;
431 acpi_handle handle;
432 unsigned long long brightness;
434 handle = bl_get_data(bd);
435 status = cmpc_get_brightness(handle, &brightness);
436 if (ACPI_SUCCESS(status))
437 return brightness;
438 else
439 return -1;
442 static int cmpc_bl_update_status(struct backlight_device *bd)
444 acpi_status status;
445 acpi_handle handle;
447 handle = bl_get_data(bd);
448 status = cmpc_set_brightness(handle, bd->props.brightness);
449 if (ACPI_SUCCESS(status))
450 return 0;
451 else
452 return -1;
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);
468 return 0;
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);
477 return 0;
480 static const struct acpi_device_id cmpc_device_ids[] = {
481 {"IPML200", 0},
482 {"", 0}
484 MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
486 static struct acpi_driver cmpc_bl_acpi_driver = {
487 .owner = THIS_MODULE,
488 .name = "cmpc",
489 .class = "cmpc",
490 .ids = cmpc_device_ids,
491 .ops = {
492 .add = cmpc_bl_add,
493 .remove = cmpc_bl_remove
499 * Extra keys code.
501 static int cmpc_keys_codes[] = {
502 KEY_UNKNOWN,
503 KEY_WLAN,
504 KEY_SWITCHVIDEOMODE,
505 KEY_BRIGHTNESSDOWN,
506 KEY_BRIGHTNESSUP,
507 KEY_VENDOR,
508 KEY_MAX
511 static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
513 struct input_dev *inputdev;
514 int code = KEY_MAX;
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)
524 int i;
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[] = {
543 {"FnBT0000", 0},
544 {"", 0}
546 MODULE_DEVICE_TABLE(acpi, cmpc_keys_device_ids);
548 static struct acpi_driver cmpc_keys_acpi_driver = {
549 .owner = THIS_MODULE,
550 .name = "cmpc_keys",
551 .class = "cmpc_keys",
552 .ids = cmpc_keys_device_ids,
553 .ops = {
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)
567 int r;
569 r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
570 if (r)
571 goto failed_keys;
573 r = acpi_bus_register_driver(&cmpc_bl_acpi_driver);
574 if (r)
575 goto failed_bl;
577 r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
578 if (r)
579 goto failed_tablet;
581 r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
582 if (r)
583 goto failed_accel;
585 return r;
587 failed_accel:
588 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
590 failed_tablet:
591 acpi_bus_unregister_driver(&cmpc_bl_acpi_driver);
593 failed_bl:
594 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
596 failed_keys:
597 return r;
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);