xref: /linux/drivers/platform/x86/classmate-laptop.c (revision b9ccfda293ee6fca9a89a1584f0900e0627b975e)
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>
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>
27 #include <linux/rfkill.h>
28 
29 MODULE_LICENSE("GPL");
30 
31 
32 struct cmpc_accel {
33 	int sensitivity;
34 };
35 
36 #define CMPC_ACCEL_SENSITIVITY_DEFAULT		5
37 
38 
39 #define CMPC_ACCEL_HID		"ACCE0000"
40 #define CMPC_TABLET_HID		"TBLT0000"
41 #define CMPC_IPML_HID	"IPML200"
42 #define CMPC_KEYS_HID		"FnBT0000"
43 
44 /*
45  * Generic input device code.
46  */
47 
48 typedef void (*input_device_init)(struct input_dev *dev);
49 
50 static 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 
71 static 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  */
81 static 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 
96 static 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 
111 static 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 
125 static 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 
154 static 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 
172 static 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 
187 static 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 
211 static struct device_attribute cmpc_accel_sensitivity_attr = {
212 	.attr = { .name = "sensitivity", .mode = 0660 },
213 	.show = cmpc_accel_sensitivity_show,
214 	.store = cmpc_accel_sensitivity_store
215 };
216 
217 static 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 
227 static 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 
235 static 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 
245 static 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 
272 failed_input:
273 	device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
274 failed_file:
275 	kfree(accel);
276 	return error;
277 }
278 
279 static 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 
291 static const struct acpi_device_id cmpc_accel_device_ids[] = {
292 	{CMPC_ACCEL_HID, 0},
293 	{"", 0}
294 };
295 
296 static 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  */
312 static 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 
330 static 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 
341 static 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 
354 static 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 
360 static int cmpc_tablet_remove(struct acpi_device *acpi, int type)
361 {
362 	return cmpc_remove_acpi_notify_device(acpi);
363 }
364 
365 static int cmpc_tablet_resume(struct device *dev)
366 {
367 	struct input_dev *inputdev = dev_get_drvdata(dev);
368 
369 	unsigned long long val = 0;
370 	if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val)))
371 		input_report_switch(inputdev, SW_TABLET_MODE, !val);
372 	return 0;
373 }
374 
375 static SIMPLE_DEV_PM_OPS(cmpc_tablet_pm, NULL, cmpc_tablet_resume);
376 
377 static const struct acpi_device_id cmpc_tablet_device_ids[] = {
378 	{CMPC_TABLET_HID, 0},
379 	{"", 0}
380 };
381 
382 static 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,
390 		.notify = cmpc_tablet_handler,
391 	},
392 	.drv.pm = &cmpc_tablet_pm,
393 };
394 
395 
396 /*
397  * Backlight code.
398  */
399 
400 static 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 
418 static 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 
436 static 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 
450 static 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 
463 static const struct backlight_ops cmpc_bl_ops = {
464 	.get_brightness = cmpc_bl_get_brightness,
465 	.update_status = cmpc_bl_update_status
466 };
467 
468 /*
469  * RFKILL code.
470  */
471 
472 static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle,
473 					unsigned long long *value)
474 {
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 
490 static 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 
508 static 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 
523 static int cmpc_rfkill_block(void *data, bool blocked)
524 {
525 	acpi_status status;
526 	acpi_handle handle;
527 	unsigned long long state;
528 	bool is_blocked;
529 
530 	handle = data;
531 	status = cmpc_get_rfkill_wlan(handle, &state);
532 	if (ACPI_FAILURE(status))
533 		return -ENODEV;
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 	}
542 	return 0;
543 }
544 
545 static 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 
554 struct ipml200_dev {
555 	struct backlight_device *bd;
556 	struct rfkill *rf;
557 };
558 
559 static 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;
568 
569 	memset(&props, 0, sizeof(struct backlight_properties));
570 	props.type = BACKLIGHT_PLATFORM;
571 	props.max_brightness = 7;
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);
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) {
588 		retval = rfkill_register(ipml->rf);
589 		if (retval) {
590 			rfkill_destroy(ipml->rf);
591 			ipml->rf = NULL;
592 		}
593 	}
594 
595 	dev_set_drvdata(&acpi->dev, ipml);
596 	return 0;
597 
598 out_bd:
599 	kfree(ipml);
600 	return retval;
601 }
602 
603 static int cmpc_ipml_remove(struct acpi_device *acpi, int type)
604 {
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);
617 
618 	return 0;
619 }
620 
621 static const struct acpi_device_id cmpc_ipml_device_ids[] = {
622 	{CMPC_IPML_HID, 0},
623 	{"", 0}
624 };
625 
626 static struct acpi_driver cmpc_ipml_acpi_driver = {
627 	.owner = THIS_MODULE,
628 	.name = "cmpc",
629 	.class = "cmpc",
630 	.ids = cmpc_ipml_device_ids,
631 	.ops = {
632 		.add = cmpc_ipml_add,
633 		.remove = cmpc_ipml_remove
634 	}
635 };
636 
637 
638 /*
639  * Extra keys code.
640  */
641 static int cmpc_keys_codes[] = {
642 	KEY_UNKNOWN,
643 	KEY_WLAN,
644 	KEY_SWITCHVIDEOMODE,
645 	KEY_BRIGHTNESSDOWN,
646 	KEY_BRIGHTNESSUP,
647 	KEY_VENDOR,
648 	KEY_UNKNOWN,
649 	KEY_CAMERA,
650 	KEY_BACK,
651 	KEY_FORWARD,
652 	KEY_MAX
653 };
654 
655 static 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];
662 	inputdev = dev_get_drvdata(&dev->dev);
663 	input_report_key(inputdev, code, !(event & 0x10));
664 	input_sync(inputdev);
665 }
666 
667 static 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 
676 static 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 
682 static int cmpc_keys_remove(struct acpi_device *acpi, int type)
683 {
684 	return cmpc_remove_acpi_notify_device(acpi);
685 }
686 
687 static const struct acpi_device_id cmpc_keys_device_ids[] = {
688 	{CMPC_KEYS_HID, 0},
689 	{"", 0}
690 };
691 
692 static 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 
709 static 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 
717 	r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver);
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 
731 failed_accel:
732 	acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
733 
734 failed_tablet:
735 	acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
736 
737 failed_bl:
738 	acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
739 
740 failed_keys:
741 	return r;
742 }
743 
744 static void cmpc_exit(void)
745 {
746 	acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
747 	acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
748 	acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
749 	acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
750 }
751 
752 module_init(cmpc_init);
753 module_exit(cmpc_exit);
754 
755 static const struct acpi_device_id cmpc_device_ids[] = {
756 	{CMPC_ACCEL_HID, 0},
757 	{CMPC_TABLET_HID, 0},
758 	{CMPC_IPML_HID, 0},
759 	{CMPC_KEYS_HID, 0},
760 	{"", 0}
761 };
762 
763 MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
764