1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  Panasonic HotKey and LCD brightness control driver
4  *  (C) 2004 Hiroshi Miura <miura@da-cha.org>
5  *  (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/
6  *  (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>
7  *  (C) 2004 David Bronaugh <dbronaugh>
8  *  (C) 2006-2008 Harald Welte <laforge@gnumonks.org>
9  *
10  *  derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte
11  *
12  *---------------------------------------------------------------------------
13  *
14  * ChangeLog:
15  *	Sep.23, 2008	Harald Welte <laforge@gnumonks.org>
16  *		-v0.95	rename driver from drivers/acpi/pcc_acpi.c to
17  *			drivers/misc/panasonic-laptop.c
18  *
19  * 	Jul.04, 2008	Harald Welte <laforge@gnumonks.org>
20  * 		-v0.94	replace /proc interface with device attributes
21  * 			support {set,get}keycode on th input device
22  *
23  *      Jun.27, 2008	Harald Welte <laforge@gnumonks.org>
24  *      	-v0.92	merge with 2.6.26-rc6 input API changes
25  *      		remove broken <= 2.6.15 kernel support
26  *      		resolve all compiler warnings
27  *      		various coding style fixes (checkpatch.pl)
28  *      		add support for backlight api
29  *      		major code restructuring
30  *
31  * 	Dac.28, 2007	Harald Welte <laforge@gnumonks.org>
32  * 		-v0.91	merge with 2.6.24-rc6 ACPI changes
33  *
34  * 	Nov.04, 2006	Hiroshi Miura <miura@da-cha.org>
35  * 		-v0.9	remove warning about section reference.
36  * 			remove acpi_os_free
37  * 			add /proc/acpi/pcc/brightness interface for HAL access
38  * 			merge dbronaugh's enhancement
39  * 			Aug.17, 2004 David Bronaugh (dbronaugh)
40  *  				- Added screen brightness setting interface
41  *				  Thanks to FreeBSD crew (acpi_panasonic.c)
42  * 				  for the ideas I needed to accomplish it
43  *
44  *	May.29, 2006	Hiroshi Miura <miura@da-cha.org>
45  *		-v0.8.4 follow to change keyinput structure
46  *			thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,
47  *			Jacob Bower <jacob.bower@ic.ac.uk> and
48  *			Hiroshi Yokota for providing solutions.
49  *
50  *	Oct.02, 2004	Hiroshi Miura <miura@da-cha.org>
51  *		-v0.8.2	merge code of YOKOTA Hiroshi
52  *					<yokota@netlab.is.tsukuba.ac.jp>.
53  *			Add sticky key mode interface.
54  *			Refactoring acpi_pcc_generate_keyinput().
55  *
56  *	Sep.15, 2004	Hiroshi Miura <miura@da-cha.org>
57  *		-v0.8	Generate key input event on input subsystem.
58  *			This is based on yet another driver written by
59  *							Ryuta Nakanishi.
60  *
61  *	Sep.10, 2004	Hiroshi Miura <miura@da-cha.org>
62  *		-v0.7	Change proc interface functions using seq_file
63  *			facility as same as other ACPI drivers.
64  *
65  *	Aug.28, 2004	Hiroshi Miura <miura@da-cha.org>
66  *		-v0.6.4 Fix a silly error with status checking
67  *
68  *	Aug.25, 2004	Hiroshi Miura <miura@da-cha.org>
69  *		-v0.6.3 replace read_acpi_int by standard function
70  *							acpi_evaluate_integer
71  *			some clean up and make smart copyright notice.
72  *			fix return value of pcc_acpi_get_key()
73  *			fix checking return value of acpi_bus_register_driver()
74  *
75  *      Aug.22, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
76  *              -v0.6.2 Add check on ACPI data (num_sifr)
77  *                      Coding style cleanups, better error messages/handling
78  *			Fixed an off-by-one error in memory allocation
79  *
80  *      Aug.21, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
81  *              -v0.6.1 Fix a silly error with status checking
82  *
83  *      Aug.20, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
84  *              - v0.6  Correct brightness controls to reflect reality
85  *                      based on information gleaned by Hiroshi Miura
86  *                      and discussions with Hiroshi Miura
87  *
88  *	Aug.10, 2004	Hiroshi Miura <miura@da-cha.org>
89  *		- v0.5  support LCD brightness control
90  *			based on the disclosed information by MEI.
91  *
92  *	Jul.25, 2004	Hiroshi Miura <miura@da-cha.org>
93  *		- v0.4  first post version
94  *		        add function to retrive SIFR
95  *
96  *	Jul.24, 2004	Hiroshi Miura <miura@da-cha.org>
97  *		- v0.3  get proper status of hotkey
98  *
99  *      Jul.22, 2004	Hiroshi Miura <miura@da-cha.org>
100  *		- v0.2  add HotKey handler
101  *
102  *      Jul.17, 2004	Hiroshi Miura <miura@da-cha.org>
103  *		- v0.1  start from toshiba_acpi driver written by John Belmonte
104  */
105 
106 #include <linux/kernel.h>
107 #include <linux/module.h>
108 #include <linux/init.h>
109 #include <linux/types.h>
110 #include <linux/backlight.h>
111 #include <linux/ctype.h>
112 #include <linux/seq_file.h>
113 #include <linux/uaccess.h>
114 #include <linux/slab.h>
115 #include <linux/acpi.h>
116 #include <linux/input.h>
117 #include <linux/input/sparse-keymap.h>
118 
119 #ifndef ACPI_HOTKEY_COMPONENT
120 #define ACPI_HOTKEY_COMPONENT	0x10000000
121 #endif
122 
123 #define _COMPONENT		ACPI_HOTKEY_COMPONENT
124 
125 MODULE_AUTHOR("Hiroshi Miura, David Bronaugh and Harald Welte");
126 MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops");
127 MODULE_LICENSE("GPL");
128 
129 #define LOGPREFIX "pcc_acpi: "
130 
131 /* Define ACPI PATHs */
132 /* Lets note hotkeys */
133 #define METHOD_HKEY_QUERY	"HINF"
134 #define METHOD_HKEY_SQTY	"SQTY"
135 #define METHOD_HKEY_SINF	"SINF"
136 #define METHOD_HKEY_SSET	"SSET"
137 #define HKEY_NOTIFY		 0x80
138 
139 #define ACPI_PCC_DRIVER_NAME	"Panasonic Laptop Support"
140 #define ACPI_PCC_DEVICE_NAME	"Hotkey"
141 #define ACPI_PCC_CLASS		"pcc"
142 
143 #define ACPI_PCC_INPUT_PHYS	"panasonic/hkey0"
144 
145 /* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
146    ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
147 */
148 enum SINF_BITS { SINF_NUM_BATTERIES = 0,
149 		 SINF_LCD_TYPE,
150 		 SINF_AC_MAX_BRIGHT,
151 		 SINF_AC_MIN_BRIGHT,
152 		 SINF_AC_CUR_BRIGHT,
153 		 SINF_DC_MAX_BRIGHT,
154 		 SINF_DC_MIN_BRIGHT,
155 		 SINF_DC_CUR_BRIGHT,
156 		 SINF_MUTE,
157 		 SINF_RESERVED,
158 		 SINF_ENV_STATE,
159 		 SINF_STICKY_KEY = 0x80,
160 	};
161 /* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
162 
163 static int acpi_pcc_hotkey_add(struct acpi_device *device);
164 static int acpi_pcc_hotkey_remove(struct acpi_device *device);
165 static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event);
166 
167 static const struct acpi_device_id pcc_device_ids[] = {
168 	{ "MAT0012", 0},
169 	{ "MAT0013", 0},
170 	{ "MAT0018", 0},
171 	{ "MAT0019", 0},
172 	{ "", 0},
173 };
174 MODULE_DEVICE_TABLE(acpi, pcc_device_ids);
175 
176 #ifdef CONFIG_PM_SLEEP
177 static int acpi_pcc_hotkey_resume(struct device *dev);
178 #endif
179 static SIMPLE_DEV_PM_OPS(acpi_pcc_hotkey_pm, NULL, acpi_pcc_hotkey_resume);
180 
181 static struct acpi_driver acpi_pcc_driver = {
182 	.name =		ACPI_PCC_DRIVER_NAME,
183 	.class =	ACPI_PCC_CLASS,
184 	.ids =		pcc_device_ids,
185 	.ops =		{
186 				.add =		acpi_pcc_hotkey_add,
187 				.remove =	acpi_pcc_hotkey_remove,
188 				.notify =	acpi_pcc_hotkey_notify,
189 			},
190 	.drv.pm =	&acpi_pcc_hotkey_pm,
191 };
192 
193 static const struct key_entry panasonic_keymap[] = {
194 	{ KE_KEY, 0, { KEY_RESERVED } },
195 	{ KE_KEY, 1, { KEY_BRIGHTNESSDOWN } },
196 	{ KE_KEY, 2, { KEY_BRIGHTNESSUP } },
197 	{ KE_KEY, 3, { KEY_DISPLAYTOGGLE } },
198 	{ KE_KEY, 4, { KEY_MUTE } },
199 	{ KE_KEY, 5, { KEY_VOLUMEDOWN } },
200 	{ KE_KEY, 6, { KEY_VOLUMEUP } },
201 	{ KE_KEY, 7, { KEY_SLEEP } },
202 	{ KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */
203 	{ KE_KEY, 9, { KEY_BATTERY } },
204 	{ KE_KEY, 10, { KEY_SUSPEND } },
205 	{ KE_END, 0 }
206 };
207 
208 struct pcc_acpi {
209 	acpi_handle		handle;
210 	unsigned long		num_sifr;
211 	int			sticky_mode;
212 	u32			*sinf;
213 	struct acpi_device	*device;
214 	struct input_dev	*input_dev;
215 	struct backlight_device	*backlight;
216 };
217 
218 /* method access functions */
219 static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)
220 {
221 	union acpi_object in_objs[] = {
222 		{ .integer.type  = ACPI_TYPE_INTEGER,
223 		  .integer.value = func, },
224 		{ .integer.type  = ACPI_TYPE_INTEGER,
225 		  .integer.value = val, },
226 	};
227 	struct acpi_object_list params = {
228 		.count   = ARRAY_SIZE(in_objs),
229 		.pointer = in_objs,
230 	};
231 	acpi_status status = AE_OK;
232 
233 	status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET,
234 				      &params, NULL);
235 
236 	return (status == AE_OK) ? 0 : -EIO;
237 }
238 
239 static inline int acpi_pcc_get_sqty(struct acpi_device *device)
240 {
241 	unsigned long long s;
242 	acpi_status status;
243 
244 	status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,
245 				       NULL, &s);
246 	if (ACPI_SUCCESS(status))
247 		return s;
248 	else {
249 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
250 				  "evaluation error HKEY.SQTY\n"));
251 		return -EINVAL;
252 	}
253 }
254 
255 static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc)
256 {
257 	acpi_status status;
258 	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
259 	union acpi_object *hkey = NULL;
260 	int i;
261 
262 	status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, NULL,
263 				      &buffer);
264 	if (ACPI_FAILURE(status)) {
265 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
266 				  "evaluation error HKEY.SINF\n"));
267 		return 0;
268 	}
269 
270 	hkey = buffer.pointer;
271 	if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
272 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
273 		status = AE_ERROR;
274 		goto end;
275 	}
276 
277 	if (pcc->num_sifr < hkey->package.count) {
278 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
279 				 "SQTY reports bad SINF length\n"));
280 		status = AE_ERROR;
281 		goto end;
282 	}
283 
284 	for (i = 0; i < hkey->package.count; i++) {
285 		union acpi_object *element = &(hkey->package.elements[i]);
286 		if (likely(element->type == ACPI_TYPE_INTEGER)) {
287 			pcc->sinf[i] = element->integer.value;
288 		} else
289 			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
290 					 "Invalid HKEY.SINF data\n"));
291 	}
292 	pcc->sinf[hkey->package.count] = -1;
293 
294 end:
295 	kfree(buffer.pointer);
296 	return status == AE_OK;
297 }
298 
299 /* backlight API interface functions */
300 
301 /* This driver currently treats AC and DC brightness identical,
302  * since we don't need to invent an interface to the core ACPI
303  * logic to receive events in case a power supply is plugged in
304  * or removed */
305 
306 static int bl_get(struct backlight_device *bd)
307 {
308 	struct pcc_acpi *pcc = bl_get_data(bd);
309 
310 	if (!acpi_pcc_retrieve_biosdata(pcc))
311 		return -EIO;
312 
313 	return pcc->sinf[SINF_AC_CUR_BRIGHT];
314 }
315 
316 static int bl_set_status(struct backlight_device *bd)
317 {
318 	struct pcc_acpi *pcc = bl_get_data(bd);
319 	int bright = bd->props.brightness;
320 	int rc;
321 
322 	if (!acpi_pcc_retrieve_biosdata(pcc))
323 		return -EIO;
324 
325 	if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
326 		bright = pcc->sinf[SINF_AC_MIN_BRIGHT];
327 
328 	if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT])
329 		bright = pcc->sinf[SINF_DC_MIN_BRIGHT];
330 
331 	if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] ||
332 	    bright > pcc->sinf[SINF_AC_MAX_BRIGHT])
333 		return -EINVAL;
334 
335 	rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright);
336 	if (rc < 0)
337 		return rc;
338 
339 	return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright);
340 }
341 
342 static const struct backlight_ops pcc_backlight_ops = {
343 	.get_brightness	= bl_get,
344 	.update_status	= bl_set_status,
345 };
346 
347 
348 /* sysfs user interface functions */
349 
350 static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
351 			    char *buf)
352 {
353 	struct acpi_device *acpi = to_acpi_device(dev);
354 	struct pcc_acpi *pcc = acpi_driver_data(acpi);
355 
356 	if (!acpi_pcc_retrieve_biosdata(pcc))
357 		return -EIO;
358 
359 	return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
360 }
361 
362 static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
363 			    char *buf)
364 {
365 	struct acpi_device *acpi = to_acpi_device(dev);
366 	struct pcc_acpi *pcc = acpi_driver_data(acpi);
367 
368 	if (!acpi_pcc_retrieve_biosdata(pcc))
369 		return -EIO;
370 
371 	return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
372 }
373 
374 static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
375 			 char *buf)
376 {
377 	struct acpi_device *acpi = to_acpi_device(dev);
378 	struct pcc_acpi *pcc = acpi_driver_data(acpi);
379 
380 	if (!acpi_pcc_retrieve_biosdata(pcc))
381 		return -EIO;
382 
383 	return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]);
384 }
385 
386 static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
387 			   char *buf)
388 {
389 	struct acpi_device *acpi = to_acpi_device(dev);
390 	struct pcc_acpi *pcc = acpi_driver_data(acpi);
391 
392 	if (!acpi_pcc_retrieve_biosdata(pcc))
393 		return -EIO;
394 
395 	return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
396 }
397 
398 static ssize_t set_sticky(struct device *dev, struct device_attribute *attr,
399 			  const char *buf, size_t count)
400 {
401 	struct acpi_device *acpi = to_acpi_device(dev);
402 	struct pcc_acpi *pcc = acpi_driver_data(acpi);
403 	int val;
404 
405 	if (count && sscanf(buf, "%i", &val) == 1 &&
406 	    (val == 0 || val == 1)) {
407 		acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val);
408 		pcc->sticky_mode = val;
409 	}
410 
411 	return count;
412 }
413 
414 static DEVICE_ATTR(numbatt, S_IRUGO, show_numbatt, NULL);
415 static DEVICE_ATTR(lcdtype, S_IRUGO, show_lcdtype, NULL);
416 static DEVICE_ATTR(mute, S_IRUGO, show_mute, NULL);
417 static DEVICE_ATTR(sticky_key, S_IRUGO | S_IWUSR, show_sticky, set_sticky);
418 
419 static struct attribute *pcc_sysfs_entries[] = {
420 	&dev_attr_numbatt.attr,
421 	&dev_attr_lcdtype.attr,
422 	&dev_attr_mute.attr,
423 	&dev_attr_sticky_key.attr,
424 	NULL,
425 };
426 
427 static const struct attribute_group pcc_attr_group = {
428 	.name	= NULL,		/* put in device directory */
429 	.attrs	= pcc_sysfs_entries,
430 };
431 
432 
433 /* hotkey input device driver */
434 
435 static int sleep_keydown_seen;
436 static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
437 {
438 	struct input_dev *hotk_input_dev = pcc->input_dev;
439 	int rc;
440 	unsigned long long result;
441 
442 	rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
443 				   NULL, &result);
444 	if (ACPI_FAILURE(rc)) {
445 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
446 				 "error getting hotkey status\n"));
447 		return;
448 	}
449 
450 	/* hack: some firmware sends no key down for sleep / hibernate */
451 	if ((result & 0xf) == 0x7 || (result & 0xf) == 0xa) {
452 		if (result & 0x80)
453 			sleep_keydown_seen = 1;
454 		if (!sleep_keydown_seen)
455 			sparse_keymap_report_event(hotk_input_dev,
456 					result & 0xf, 0x80, false);
457 	}
458 
459 	if (!sparse_keymap_report_event(hotk_input_dev,
460 					result & 0xf, result & 0x80, false))
461 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
462 				  "Unknown hotkey event: %d\n", result));
463 }
464 
465 static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)
466 {
467 	struct pcc_acpi *pcc = acpi_driver_data(device);
468 
469 	switch (event) {
470 	case HKEY_NOTIFY:
471 		acpi_pcc_generate_keyinput(pcc);
472 		break;
473 	default:
474 		/* nothing to do */
475 		break;
476 	}
477 }
478 
479 static int acpi_pcc_init_input(struct pcc_acpi *pcc)
480 {
481 	struct input_dev *input_dev;
482 	int error;
483 
484 	input_dev = input_allocate_device();
485 	if (!input_dev)
486 		return -ENOMEM;
487 
488 	input_dev->name = ACPI_PCC_DRIVER_NAME;
489 	input_dev->phys = ACPI_PCC_INPUT_PHYS;
490 	input_dev->id.bustype = BUS_HOST;
491 	input_dev->id.vendor = 0x0001;
492 	input_dev->id.product = 0x0001;
493 	input_dev->id.version = 0x0100;
494 
495 	error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL);
496 	if (error) {
497 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
498 				  "Unable to setup input device keymap\n"));
499 		goto err_free_dev;
500 	}
501 
502 	error = input_register_device(input_dev);
503 	if (error) {
504 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
505 				  "Unable to register input device\n"));
506 		goto err_free_dev;
507 	}
508 
509 	pcc->input_dev = input_dev;
510 	return 0;
511 
512  err_free_dev:
513 	input_free_device(input_dev);
514 	return error;
515 }
516 
517 /* kernel module interface */
518 
519 #ifdef CONFIG_PM_SLEEP
520 static int acpi_pcc_hotkey_resume(struct device *dev)
521 {
522 	struct pcc_acpi *pcc;
523 
524 	if (!dev)
525 		return -EINVAL;
526 
527 	pcc = acpi_driver_data(to_acpi_device(dev));
528 	if (!pcc)
529 		return -EINVAL;
530 
531 	ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n",
532 			  pcc->sticky_mode));
533 
534 	return acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
535 }
536 #endif
537 
538 static int acpi_pcc_hotkey_add(struct acpi_device *device)
539 {
540 	struct backlight_properties props;
541 	struct pcc_acpi *pcc;
542 	int num_sifr, result;
543 
544 	if (!device)
545 		return -EINVAL;
546 
547 	num_sifr = acpi_pcc_get_sqty(device);
548 
549 	if (num_sifr < 0 || num_sifr > 255) {
550 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr out of range"));
551 		return -ENODEV;
552 	}
553 
554 	pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL);
555 	if (!pcc) {
556 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
557 				  "Couldn't allocate mem for pcc"));
558 		return -ENOMEM;
559 	}
560 
561 	pcc->sinf = kcalloc(num_sifr + 1, sizeof(u32), GFP_KERNEL);
562 	if (!pcc->sinf) {
563 		result = -ENOMEM;
564 		goto out_hotkey;
565 	}
566 
567 	pcc->device = device;
568 	pcc->handle = device->handle;
569 	pcc->num_sifr = num_sifr;
570 	device->driver_data = pcc;
571 	strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);
572 	strcpy(acpi_device_class(device), ACPI_PCC_CLASS);
573 
574 	result = acpi_pcc_init_input(pcc);
575 	if (result) {
576 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
577 				  "Error installing keyinput handler\n"));
578 		goto out_sinf;
579 	}
580 
581 	if (!acpi_pcc_retrieve_biosdata(pcc)) {
582 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
583 				 "Couldn't retrieve BIOS data\n"));
584 		result = -EIO;
585 		goto out_input;
586 	}
587 	/* initialize backlight */
588 	memset(&props, 0, sizeof(struct backlight_properties));
589 	props.type = BACKLIGHT_PLATFORM;
590 	props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT];
591 	pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
592 						   &pcc_backlight_ops, &props);
593 	if (IS_ERR(pcc->backlight)) {
594 		result = PTR_ERR(pcc->backlight);
595 		goto out_input;
596 	}
597 
598 	/* read the initial brightness setting from the hardware */
599 	pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
600 
601 	/* read the initial sticky key mode from the hardware */
602 	pcc->sticky_mode = pcc->sinf[SINF_STICKY_KEY];
603 
604 	/* add sysfs attributes */
605 	result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);
606 	if (result)
607 		goto out_backlight;
608 
609 	return 0;
610 
611 out_backlight:
612 	backlight_device_unregister(pcc->backlight);
613 out_input:
614 	input_unregister_device(pcc->input_dev);
615 out_sinf:
616 	kfree(pcc->sinf);
617 out_hotkey:
618 	kfree(pcc);
619 
620 	return result;
621 }
622 
623 static int acpi_pcc_hotkey_remove(struct acpi_device *device)
624 {
625 	struct pcc_acpi *pcc = acpi_driver_data(device);
626 
627 	if (!device || !pcc)
628 		return -EINVAL;
629 
630 	sysfs_remove_group(&device->dev.kobj, &pcc_attr_group);
631 
632 	backlight_device_unregister(pcc->backlight);
633 
634 	input_unregister_device(pcc->input_dev);
635 
636 	kfree(pcc->sinf);
637 	kfree(pcc);
638 
639 	return 0;
640 }
641 
642 module_acpi_driver(acpi_pcc_driver);
643