asus-wmi: enable resume on lid open
[deliverable/linux.git] / drivers / platform / x86 / classmate-laptop.c
CommitLineData
529aa8cb
TLSC
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>
5a0e3ad6 22#include <linux/slab.h>
529aa8cb
TLSC
23#include <linux/workqueue.h>
24#include <acpi/acpi_drivers.h>
25#include <linux/backlight.h>
26#include <linux/input.h>
d5c051f1 27#include <linux/rfkill.h>
529aa8cb
TLSC
28
29MODULE_LICENSE("GPL");
30
31
32struct cmpc_accel {
33 int sensitivity;
34};
35
36#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
37
38
02e77a55
TLSC
39#define CMPC_ACCEL_HID "ACCE0000"
40#define CMPC_TABLET_HID "TBLT0000"
d5c051f1 41#define CMPC_IPML_HID "IPML200"
02e77a55
TLSC
42#define CMPC_KEYS_HID "FnBT0000"
43
529aa8cb
TLSC
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
df92754d 211static struct device_attribute cmpc_accel_sensitivity_attr = {
529aa8cb
TLSC
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[] = {
02e77a55 292 {CMPC_ACCEL_HID, 0},
529aa8cb
TLSC
293 {"", 0}
294};
529aa8cb
TLSC
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
81bc4954 365static int cmpc_tablet_resume(struct device *dev)
529aa8cb 366{
81bc4954
RW
367 struct input_dev *inputdev = dev_get_drvdata(dev);
368
529aa8cb 369 unsigned long long val = 0;
81bc4954 370 if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val)))
529aa8cb
TLSC
371 input_report_switch(inputdev, SW_TABLET_MODE, !val);
372 return 0;
373}
374
81bc4954
RW
375static SIMPLE_DEV_PM_OPS(cmpc_tablet_pm, NULL, cmpc_tablet_resume);
376
529aa8cb 377static const struct acpi_device_id cmpc_tablet_device_ids[] = {
02e77a55 378 {CMPC_TABLET_HID, 0},
529aa8cb
TLSC
379 {"", 0}
380};
529aa8cb
TLSC
381
382static struct acpi_driver cmpc_tablet_acpi_driver = {
383 .owner = THIS_MODULE,
384 .name = "cmpc_tablet",
385 .class = "cmpc_tablet",
386 .ids = cmpc_tablet_device_ids,
387 .ops = {
388 .add = cmpc_tablet_add,
389 .remove = cmpc_tablet_remove,
529aa8cb 390 .notify = cmpc_tablet_handler,
81bc4954
RW
391 },
392 .drv.pm = &cmpc_tablet_pm,
529aa8cb
TLSC
393};
394
395
396/*
397 * Backlight code.
398 */
399
400static acpi_status cmpc_get_brightness(acpi_handle handle,
401 unsigned long long *value)
402{
403 union acpi_object param;
404 struct acpi_object_list input;
405 unsigned long long output;
406 acpi_status status;
407
408 param.type = ACPI_TYPE_INTEGER;
409 param.integer.value = 0xC0;
410 input.count = 1;
411 input.pointer = &param;
412 status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
413 if (ACPI_SUCCESS(status))
414 *value = output;
415 return status;
416}
417
418static acpi_status cmpc_set_brightness(acpi_handle handle,
419 unsigned long long value)
420{
421 union acpi_object param[2];
422 struct acpi_object_list input;
423 acpi_status status;
424 unsigned long long output;
425
426 param[0].type = ACPI_TYPE_INTEGER;
427 param[0].integer.value = 0xC0;
428 param[1].type = ACPI_TYPE_INTEGER;
429 param[1].integer.value = value;
430 input.count = 2;
431 input.pointer = param;
432 status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
433 return status;
434}
435
436static int cmpc_bl_get_brightness(struct backlight_device *bd)
437{
438 acpi_status status;
439 acpi_handle handle;
440 unsigned long long brightness;
441
442 handle = bl_get_data(bd);
443 status = cmpc_get_brightness(handle, &brightness);
444 if (ACPI_SUCCESS(status))
445 return brightness;
446 else
447 return -1;
448}
449
450static int cmpc_bl_update_status(struct backlight_device *bd)
451{
452 acpi_status status;
453 acpi_handle handle;
454
455 handle = bl_get_data(bd);
456 status = cmpc_set_brightness(handle, bd->props.brightness);
457 if (ACPI_SUCCESS(status))
458 return 0;
459 else
460 return -1;
461}
462
f0af7899 463static const struct backlight_ops cmpc_bl_ops = {
529aa8cb
TLSC
464 .get_brightness = cmpc_bl_get_brightness,
465 .update_status = cmpc_bl_update_status
466};
467
d5c051f1
TLSC
468/*
469 * RFKILL code.
470 */
471
472static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle,
473 unsigned long long *value)
529aa8cb 474{
d5c051f1
TLSC
475 union acpi_object param;
476 struct acpi_object_list input;
477 unsigned long long output;
478 acpi_status status;
479
480 param.type = ACPI_TYPE_INTEGER;
481 param.integer.value = 0xC1;
482 input.count = 1;
483 input.pointer = &param;
484 status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
485 if (ACPI_SUCCESS(status))
486 *value = output;
487 return status;
488}
489
490static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle,
491 unsigned long long value)
492{
493 union acpi_object param[2];
494 struct acpi_object_list input;
495 acpi_status status;
496 unsigned long long output;
497
498 param[0].type = ACPI_TYPE_INTEGER;
499 param[0].integer.value = 0xC1;
500 param[1].type = ACPI_TYPE_INTEGER;
501 param[1].integer.value = value;
502 input.count = 2;
503 input.pointer = param;
504 status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
505 return status;
506}
507
508static void cmpc_rfkill_query(struct rfkill *rfkill, void *data)
509{
510 acpi_status status;
511 acpi_handle handle;
512 unsigned long long state;
513 bool blocked;
514
515 handle = data;
516 status = cmpc_get_rfkill_wlan(handle, &state);
517 if (ACPI_SUCCESS(status)) {
518 blocked = state & 1 ? false : true;
519 rfkill_set_sw_state(rfkill, blocked);
520 }
521}
522
523static int cmpc_rfkill_block(void *data, bool blocked)
524{
525 acpi_status status;
526 acpi_handle handle;
527 unsigned long long state;
698e1641 528 bool is_blocked;
d5c051f1
TLSC
529
530 handle = data;
531 status = cmpc_get_rfkill_wlan(handle, &state);
532 if (ACPI_FAILURE(status))
533 return -ENODEV;
698e1641
HRK
534 /* Check if we really need to call cmpc_set_rfkill_wlan */
535 is_blocked = state & 1 ? false : true;
536 if (is_blocked != blocked) {
537 state = blocked ? 0 : 1;
538 status = cmpc_set_rfkill_wlan(handle, state);
539 if (ACPI_FAILURE(status))
540 return -ENODEV;
541 }
d5c051f1
TLSC
542 return 0;
543}
544
545static const struct rfkill_ops cmpc_rfkill_ops = {
546 .query = cmpc_rfkill_query,
547 .set_block = cmpc_rfkill_block,
548};
549
550/*
551 * Common backlight and rfkill code.
552 */
553
554struct ipml200_dev {
529aa8cb 555 struct backlight_device *bd;
d5c051f1
TLSC
556 struct rfkill *rf;
557};
558
559static int cmpc_ipml_add(struct acpi_device *acpi)
560{
561 int retval;
562 struct ipml200_dev *ipml;
563 struct backlight_properties props;
564
565 ipml = kmalloc(sizeof(*ipml), GFP_KERNEL);
566 if (ipml == NULL)
567 return -ENOMEM;
529aa8cb 568
a19a6ee6 569 memset(&props, 0, sizeof(struct backlight_properties));
bb7ca747 570 props.type = BACKLIGHT_PLATFORM;
a19a6ee6 571 props.max_brightness = 7;
d5c051f1
TLSC
572 ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev,
573 acpi->handle, &cmpc_bl_ops,
574 &props);
575 if (IS_ERR(ipml->bd)) {
576 retval = PTR_ERR(ipml->bd);
577 goto out_bd;
578 }
579
580 ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN,
581 &cmpc_rfkill_ops, acpi->handle);
b95d13ea
TLSC
582 /*
583 * If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV).
584 * This is OK, however, since all other uses of the device will not
585 * derefence it.
586 */
587 if (ipml->rf) {
d5c051f1
TLSC
588 retval = rfkill_register(ipml->rf);
589 if (retval) {
590 rfkill_destroy(ipml->rf);
591 ipml->rf = NULL;
592 }
d5c051f1
TLSC
593 }
594
595 dev_set_drvdata(&acpi->dev, ipml);
529aa8cb 596 return 0;
d5c051f1
TLSC
597
598out_bd:
599 kfree(ipml);
600 return retval;
529aa8cb
TLSC
601}
602
d5c051f1 603static int cmpc_ipml_remove(struct acpi_device *acpi, int type)
529aa8cb 604{
d5c051f1
TLSC
605 struct ipml200_dev *ipml;
606
607 ipml = dev_get_drvdata(&acpi->dev);
608
609 backlight_device_unregister(ipml->bd);
610
611 if (ipml->rf) {
612 rfkill_unregister(ipml->rf);
613 rfkill_destroy(ipml->rf);
614 }
615
616 kfree(ipml);
529aa8cb 617
529aa8cb
TLSC
618 return 0;
619}
620
d5c051f1
TLSC
621static const struct acpi_device_id cmpc_ipml_device_ids[] = {
622 {CMPC_IPML_HID, 0},
529aa8cb
TLSC
623 {"", 0}
624};
529aa8cb 625
d5c051f1 626static struct acpi_driver cmpc_ipml_acpi_driver = {
529aa8cb
TLSC
627 .owner = THIS_MODULE,
628 .name = "cmpc",
629 .class = "cmpc",
d5c051f1 630 .ids = cmpc_ipml_device_ids,
529aa8cb 631 .ops = {
d5c051f1
TLSC
632 .add = cmpc_ipml_add,
633 .remove = cmpc_ipml_remove
529aa8cb
TLSC
634 }
635};
636
637
638/*
639 * Extra keys code.
640 */
641static int cmpc_keys_codes[] = {
642 KEY_UNKNOWN,
643 KEY_WLAN,
644 KEY_SWITCHVIDEOMODE,
645 KEY_BRIGHTNESSDOWN,
646 KEY_BRIGHTNESSUP,
647 KEY_VENDOR,
881a6c25
TLSC
648 KEY_UNKNOWN,
649 KEY_CAMERA,
650 KEY_BACK,
651 KEY_FORWARD,
529aa8cb
TLSC
652 KEY_MAX
653};
654
655static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
656{
657 struct input_dev *inputdev;
658 int code = KEY_MAX;
659
660 if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
661 code = cmpc_keys_codes[event & 0x0F];
3098064d 662 inputdev = dev_get_drvdata(&dev->dev);
529aa8cb 663 input_report_key(inputdev, code, !(event & 0x10));
72135d21 664 input_sync(inputdev);
529aa8cb
TLSC
665}
666
667static void cmpc_keys_idev_init(struct input_dev *inputdev)
668{
669 int i;
670
671 set_bit(EV_KEY, inputdev->evbit);
672 for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++)
673 set_bit(cmpc_keys_codes[i], inputdev->keybit);
674}
675
676static int cmpc_keys_add(struct acpi_device *acpi)
677{
678 return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
679 cmpc_keys_idev_init);
680}
681
682static int cmpc_keys_remove(struct acpi_device *acpi, int type)
683{
684 return cmpc_remove_acpi_notify_device(acpi);
685}
686
687static const struct acpi_device_id cmpc_keys_device_ids[] = {
02e77a55 688 {CMPC_KEYS_HID, 0},
529aa8cb
TLSC
689 {"", 0}
690};
529aa8cb
TLSC
691
692static struct acpi_driver cmpc_keys_acpi_driver = {
693 .owner = THIS_MODULE,
694 .name = "cmpc_keys",
695 .class = "cmpc_keys",
696 .ids = cmpc_keys_device_ids,
697 .ops = {
698 .add = cmpc_keys_add,
699 .remove = cmpc_keys_remove,
700 .notify = cmpc_keys_handler,
701 }
702};
703
704
705/*
706 * General init/exit code.
707 */
708
709static int cmpc_init(void)
710{
711 int r;
712
713 r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
714 if (r)
715 goto failed_keys;
716
d5c051f1 717 r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver);
529aa8cb
TLSC
718 if (r)
719 goto failed_bl;
720
721 r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
722 if (r)
723 goto failed_tablet;
724
725 r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
726 if (r)
727 goto failed_accel;
728
729 return r;
730
731failed_accel:
732 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
733
734failed_tablet:
d5c051f1 735 acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
529aa8cb
TLSC
736
737failed_bl:
738 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
739
740failed_keys:
741 return r;
742}
743
744static void cmpc_exit(void)
745{
746 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
747 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
d5c051f1 748 acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
529aa8cb
TLSC
749 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
750}
751
752module_init(cmpc_init);
753module_exit(cmpc_exit);
02e77a55
TLSC
754
755static const struct acpi_device_id cmpc_device_ids[] = {
756 {CMPC_ACCEL_HID, 0},
757 {CMPC_TABLET_HID, 0},
d5c051f1 758 {CMPC_IPML_HID, 0},
02e77a55
TLSC
759 {CMPC_KEYS_HID, 0},
760 {"", 0}
761};
762
763MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
This page took 0.250925 seconds and 5 git commands to generate.