backlight: classmate-laptop - Fix missing registration failure handling
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / platform / x86 / classmate-laptop.c
blobc696cf1c26167cf7b19a2b63389adb683b300d3e
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
37 #define CMPC_ACCEL_HID "ACCE0000"
38 #define CMPC_TABLET_HID "TBLT0000"
39 #define CMPC_BL_HID "IPML200"
40 #define CMPC_KEYS_HID "FnBT0000"
43 * Generic input device code.
46 typedef void (*input_device_init)(struct input_dev *dev);
48 static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,
49 input_device_init idev_init)
51 struct input_dev *inputdev;
52 int error;
54 inputdev = input_allocate_device();
55 if (!inputdev)
56 return -ENOMEM;
57 inputdev->name = name;
58 inputdev->dev.parent = &acpi->dev;
59 idev_init(inputdev);
60 error = input_register_device(inputdev);
61 if (error) {
62 input_free_device(inputdev);
63 return error;
65 dev_set_drvdata(&acpi->dev, inputdev);
66 return 0;
69 static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
71 struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
72 input_unregister_device(inputdev);
73 return 0;
77 * Accelerometer code.
79 static acpi_status cmpc_start_accel(acpi_handle handle)
81 union acpi_object param[2];
82 struct acpi_object_list input;
83 acpi_status status;
85 param[0].type = ACPI_TYPE_INTEGER;
86 param[0].integer.value = 0x3;
87 param[1].type = ACPI_TYPE_INTEGER;
88 input.count = 2;
89 input.pointer = param;
90 status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
91 return status;
94 static acpi_status cmpc_stop_accel(acpi_handle handle)
96 union acpi_object param[2];
97 struct acpi_object_list input;
98 acpi_status status;
100 param[0].type = ACPI_TYPE_INTEGER;
101 param[0].integer.value = 0x4;
102 param[1].type = ACPI_TYPE_INTEGER;
103 input.count = 2;
104 input.pointer = param;
105 status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
106 return status;
109 static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val)
111 union acpi_object param[2];
112 struct acpi_object_list input;
114 param[0].type = ACPI_TYPE_INTEGER;
115 param[0].integer.value = 0x02;
116 param[1].type = ACPI_TYPE_INTEGER;
117 param[1].integer.value = val;
118 input.count = 2;
119 input.pointer = param;
120 return acpi_evaluate_object(handle, "ACMD", &input, NULL);
123 static acpi_status cmpc_get_accel(acpi_handle handle,
124 unsigned char *x,
125 unsigned char *y,
126 unsigned char *z)
128 union acpi_object param[2];
129 struct acpi_object_list input;
130 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, 0 };
131 unsigned char *locs;
132 acpi_status status;
134 param[0].type = ACPI_TYPE_INTEGER;
135 param[0].integer.value = 0x01;
136 param[1].type = ACPI_TYPE_INTEGER;
137 input.count = 2;
138 input.pointer = param;
139 status = acpi_evaluate_object(handle, "ACMD", &input, &output);
140 if (ACPI_SUCCESS(status)) {
141 union acpi_object *obj;
142 obj = output.pointer;
143 locs = obj->buffer.pointer;
144 *x = locs[0];
145 *y = locs[1];
146 *z = locs[2];
147 kfree(output.pointer);
149 return status;
152 static void cmpc_accel_handler(struct acpi_device *dev, u32 event)
154 if (event == 0x81) {
155 unsigned char x, y, z;
156 acpi_status status;
158 status = cmpc_get_accel(dev->handle, &x, &y, &z);
159 if (ACPI_SUCCESS(status)) {
160 struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
162 input_report_abs(inputdev, ABS_X, x);
163 input_report_abs(inputdev, ABS_Y, y);
164 input_report_abs(inputdev, ABS_Z, z);
165 input_sync(inputdev);
170 static ssize_t cmpc_accel_sensitivity_show(struct device *dev,
171 struct device_attribute *attr,
172 char *buf)
174 struct acpi_device *acpi;
175 struct input_dev *inputdev;
176 struct cmpc_accel *accel;
178 acpi = to_acpi_device(dev);
179 inputdev = dev_get_drvdata(&acpi->dev);
180 accel = dev_get_drvdata(&inputdev->dev);
182 return sprintf(buf, "%d\n", accel->sensitivity);
185 static ssize_t cmpc_accel_sensitivity_store(struct device *dev,
186 struct device_attribute *attr,
187 const char *buf, size_t count)
189 struct acpi_device *acpi;
190 struct input_dev *inputdev;
191 struct cmpc_accel *accel;
192 unsigned long sensitivity;
193 int r;
195 acpi = to_acpi_device(dev);
196 inputdev = dev_get_drvdata(&acpi->dev);
197 accel = dev_get_drvdata(&inputdev->dev);
199 r = strict_strtoul(buf, 0, &sensitivity);
200 if (r)
201 return r;
203 accel->sensitivity = sensitivity;
204 cmpc_accel_set_sensitivity(acpi->handle, sensitivity);
206 return strnlen(buf, count);
209 struct device_attribute cmpc_accel_sensitivity_attr = {
210 .attr = { .name = "sensitivity", .mode = 0660 },
211 .show = cmpc_accel_sensitivity_show,
212 .store = cmpc_accel_sensitivity_store
215 static int cmpc_accel_open(struct input_dev *input)
217 struct acpi_device *acpi;
219 acpi = to_acpi_device(input->dev.parent);
220 if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle)))
221 return 0;
222 return -EIO;
225 static void cmpc_accel_close(struct input_dev *input)
227 struct acpi_device *acpi;
229 acpi = to_acpi_device(input->dev.parent);
230 cmpc_stop_accel(acpi->handle);
233 static void cmpc_accel_idev_init(struct input_dev *inputdev)
235 set_bit(EV_ABS, inputdev->evbit);
236 input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0);
237 input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0);
238 input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0);
239 inputdev->open = cmpc_accel_open;
240 inputdev->close = cmpc_accel_close;
243 static int cmpc_accel_add(struct acpi_device *acpi)
245 int error;
246 struct input_dev *inputdev;
247 struct cmpc_accel *accel;
249 accel = kmalloc(sizeof(*accel), GFP_KERNEL);
250 if (!accel)
251 return -ENOMEM;
253 accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
254 cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity);
256 error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
257 if (error)
258 goto failed_file;
260 error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",
261 cmpc_accel_idev_init);
262 if (error)
263 goto failed_input;
265 inputdev = dev_get_drvdata(&acpi->dev);
266 dev_set_drvdata(&inputdev->dev, accel);
268 return 0;
270 failed_input:
271 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
272 failed_file:
273 kfree(accel);
274 return error;
277 static int cmpc_accel_remove(struct acpi_device *acpi, int type)
279 struct input_dev *inputdev;
280 struct cmpc_accel *accel;
282 inputdev = dev_get_drvdata(&acpi->dev);
283 accel = dev_get_drvdata(&inputdev->dev);
285 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
286 return cmpc_remove_acpi_notify_device(acpi);
289 static const struct acpi_device_id cmpc_accel_device_ids[] = {
290 {CMPC_ACCEL_HID, 0},
291 {"", 0}
294 static struct acpi_driver cmpc_accel_acpi_driver = {
295 .owner = THIS_MODULE,
296 .name = "cmpc_accel",
297 .class = "cmpc_accel",
298 .ids = cmpc_accel_device_ids,
299 .ops = {
300 .add = cmpc_accel_add,
301 .remove = cmpc_accel_remove,
302 .notify = cmpc_accel_handler,
308 * Tablet mode code.
310 static acpi_status cmpc_get_tablet(acpi_handle handle,
311 unsigned long long *value)
313 union acpi_object param;
314 struct acpi_object_list input;
315 unsigned long long output;
316 acpi_status status;
318 param.type = ACPI_TYPE_INTEGER;
319 param.integer.value = 0x01;
320 input.count = 1;
321 input.pointer = &param;
322 status = acpi_evaluate_integer(handle, "TCMD", &input, &output);
323 if (ACPI_SUCCESS(status))
324 *value = output;
325 return status;
328 static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
330 unsigned long long val = 0;
331 struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
333 if (event == 0x81) {
334 if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val)))
335 input_report_switch(inputdev, SW_TABLET_MODE, !val);
339 static void cmpc_tablet_idev_init(struct input_dev *inputdev)
341 unsigned long long val = 0;
342 struct acpi_device *acpi;
344 set_bit(EV_SW, inputdev->evbit);
345 set_bit(SW_TABLET_MODE, inputdev->swbit);
347 acpi = to_acpi_device(inputdev->dev.parent);
348 if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
349 input_report_switch(inputdev, SW_TABLET_MODE, !val);
352 static int cmpc_tablet_add(struct acpi_device *acpi)
354 return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet",
355 cmpc_tablet_idev_init);
358 static int cmpc_tablet_remove(struct acpi_device *acpi, int type)
360 return cmpc_remove_acpi_notify_device(acpi);
363 static int cmpc_tablet_resume(struct acpi_device *acpi)
365 struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
366 unsigned long long val = 0;
367 if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
368 input_report_switch(inputdev, SW_TABLET_MODE, !val);
369 return 0;
372 static const struct acpi_device_id cmpc_tablet_device_ids[] = {
373 {CMPC_TABLET_HID, 0},
374 {"", 0}
377 static struct acpi_driver cmpc_tablet_acpi_driver = {
378 .owner = THIS_MODULE,
379 .name = "cmpc_tablet",
380 .class = "cmpc_tablet",
381 .ids = cmpc_tablet_device_ids,
382 .ops = {
383 .add = cmpc_tablet_add,
384 .remove = cmpc_tablet_remove,
385 .resume = cmpc_tablet_resume,
386 .notify = cmpc_tablet_handler,
392 * Backlight code.
395 static acpi_status cmpc_get_brightness(acpi_handle handle,
396 unsigned long long *value)
398 union acpi_object param;
399 struct acpi_object_list input;
400 unsigned long long output;
401 acpi_status status;
403 param.type = ACPI_TYPE_INTEGER;
404 param.integer.value = 0xC0;
405 input.count = 1;
406 input.pointer = &param;
407 status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
408 if (ACPI_SUCCESS(status))
409 *value = output;
410 return status;
413 static acpi_status cmpc_set_brightness(acpi_handle handle,
414 unsigned long long value)
416 union acpi_object param[2];
417 struct acpi_object_list input;
418 acpi_status status;
419 unsigned long long output;
421 param[0].type = ACPI_TYPE_INTEGER;
422 param[0].integer.value = 0xC0;
423 param[1].type = ACPI_TYPE_INTEGER;
424 param[1].integer.value = value;
425 input.count = 2;
426 input.pointer = param;
427 status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
428 return status;
431 static int cmpc_bl_get_brightness(struct backlight_device *bd)
433 acpi_status status;
434 acpi_handle handle;
435 unsigned long long brightness;
437 handle = bl_get_data(bd);
438 status = cmpc_get_brightness(handle, &brightness);
439 if (ACPI_SUCCESS(status))
440 return brightness;
441 else
442 return -1;
445 static int cmpc_bl_update_status(struct backlight_device *bd)
447 acpi_status status;
448 acpi_handle handle;
450 handle = bl_get_data(bd);
451 status = cmpc_set_brightness(handle, bd->props.brightness);
452 if (ACPI_SUCCESS(status))
453 return 0;
454 else
455 return -1;
458 static const struct backlight_ops cmpc_bl_ops = {
459 .get_brightness = cmpc_bl_get_brightness,
460 .update_status = cmpc_bl_update_status
463 static int cmpc_bl_add(struct acpi_device *acpi)
465 struct backlight_properties props;
466 struct backlight_device *bd;
468 memset(&props, 0, sizeof(struct backlight_properties));
469 props.max_brightness = 7;
470 bd = backlight_device_register("cmpc_bl", &acpi->dev, acpi->handle,
471 &cmpc_bl_ops, &props);
472 if (IS_ERR(bd))
473 return PTR_ERR(bd);
474 dev_set_drvdata(&acpi->dev, bd);
475 return 0;
478 static int cmpc_bl_remove(struct acpi_device *acpi, int type)
480 struct backlight_device *bd;
482 bd = dev_get_drvdata(&acpi->dev);
483 backlight_device_unregister(bd);
484 return 0;
487 static const struct acpi_device_id cmpc_bl_device_ids[] = {
488 {CMPC_BL_HID, 0},
489 {"", 0}
492 static struct acpi_driver cmpc_bl_acpi_driver = {
493 .owner = THIS_MODULE,
494 .name = "cmpc",
495 .class = "cmpc",
496 .ids = cmpc_bl_device_ids,
497 .ops = {
498 .add = cmpc_bl_add,
499 .remove = cmpc_bl_remove
505 * Extra keys code.
507 static int cmpc_keys_codes[] = {
508 KEY_UNKNOWN,
509 KEY_WLAN,
510 KEY_SWITCHVIDEOMODE,
511 KEY_BRIGHTNESSDOWN,
512 KEY_BRIGHTNESSUP,
513 KEY_VENDOR,
514 KEY_UNKNOWN,
515 KEY_CAMERA,
516 KEY_BACK,
517 KEY_FORWARD,
518 KEY_MAX
521 static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
523 struct input_dev *inputdev;
524 int code = KEY_MAX;
526 if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
527 code = cmpc_keys_codes[event & 0x0F];
528 inputdev = dev_get_drvdata(&dev->dev);;
529 input_report_key(inputdev, code, !(event & 0x10));
532 static void cmpc_keys_idev_init(struct input_dev *inputdev)
534 int i;
536 set_bit(EV_KEY, inputdev->evbit);
537 for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++)
538 set_bit(cmpc_keys_codes[i], inputdev->keybit);
541 static int cmpc_keys_add(struct acpi_device *acpi)
543 return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
544 cmpc_keys_idev_init);
547 static int cmpc_keys_remove(struct acpi_device *acpi, int type)
549 return cmpc_remove_acpi_notify_device(acpi);
552 static const struct acpi_device_id cmpc_keys_device_ids[] = {
553 {CMPC_KEYS_HID, 0},
554 {"", 0}
557 static struct acpi_driver cmpc_keys_acpi_driver = {
558 .owner = THIS_MODULE,
559 .name = "cmpc_keys",
560 .class = "cmpc_keys",
561 .ids = cmpc_keys_device_ids,
562 .ops = {
563 .add = cmpc_keys_add,
564 .remove = cmpc_keys_remove,
565 .notify = cmpc_keys_handler,
571 * General init/exit code.
574 static int cmpc_init(void)
576 int r;
578 r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
579 if (r)
580 goto failed_keys;
582 r = acpi_bus_register_driver(&cmpc_bl_acpi_driver);
583 if (r)
584 goto failed_bl;
586 r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
587 if (r)
588 goto failed_tablet;
590 r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
591 if (r)
592 goto failed_accel;
594 return r;
596 failed_accel:
597 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
599 failed_tablet:
600 acpi_bus_unregister_driver(&cmpc_bl_acpi_driver);
602 failed_bl:
603 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
605 failed_keys:
606 return r;
609 static void cmpc_exit(void)
611 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
612 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
613 acpi_bus_unregister_driver(&cmpc_bl_acpi_driver);
614 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
617 module_init(cmpc_init);
618 module_exit(cmpc_exit);
620 static const struct acpi_device_id cmpc_device_ids[] = {
621 {CMPC_ACCEL_HID, 0},
622 {CMPC_TABLET_HID, 0},
623 {CMPC_BL_HID, 0},
624 {CMPC_KEYS_HID, 0},
625 {"", 0}
628 MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);