1 // SPDX-License-Identifier: GPL-2.0-only
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>
10 * derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte
12 *---------------------------------------------------------------------------
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
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
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
31 * Dac.28, 2007 Harald Welte <laforge@gnumonks.org>
32 * -v0.91 merge with 2.6.24-rc6 ACPI changes
34 * Nov.04, 2006 Hiroshi Miura <miura@da-cha.org>
35 * -v0.9 remove warning about section reference.
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
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.
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().
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
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.
65 * Aug.28, 2004 Hiroshi Miura <miura@da-cha.org>
66 * -v0.6.4 Fix a silly error with status checking
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()
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
80 * Aug.21, 2004 David Bronaugh <dbronaugh@linuxboxen.org>
81 * -v0.6.1 Fix a silly error with status checking
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
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.
92 * Jul.25, 2004 Hiroshi Miura <miura@da-cha.org>
93 * - v0.4 first post version
94 * add function to retrive SIFR
96 * Jul.24, 2004 Hiroshi Miura <miura@da-cha.org>
97 * - v0.3 get proper status of hotkey
99 * Jul.22, 2004 Hiroshi Miura <miura@da-cha.org>
100 * - v0.2 add HotKey handler
102 * Jul.17, 2004 Hiroshi Miura <miura@da-cha.org>
103 * - v0.1 start from toshiba_acpi driver written by John Belmonte
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>
119 #ifndef ACPI_HOTKEY_COMPONENT
120 #define ACPI_HOTKEY_COMPONENT 0x10000000
123 #define _COMPONENT ACPI_HOTKEY_COMPONENT
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");
129 #define LOGPREFIX "pcc_acpi: "
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
139 #define ACPI_PCC_DRIVER_NAME "Panasonic Laptop Support"
140 #define ACPI_PCC_DEVICE_NAME "Hotkey"
141 #define ACPI_PCC_CLASS "pcc"
143 #define ACPI_PCC_INPUT_PHYS "panasonic/hkey0"
145 /* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
146 ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
148 enum SINF_BITS { SINF_NUM_BATTERIES = 0,
159 SINF_STICKY_KEY = 0x80,
161 /* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
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);
167 static const struct acpi_device_id pcc_device_ids[] = {
174 MODULE_DEVICE_TABLE(acpi, pcc_device_ids);
176 #ifdef CONFIG_PM_SLEEP
177 static int acpi_pcc_hotkey_resume(struct device *dev);
179 static SIMPLE_DEV_PM_OPS(acpi_pcc_hotkey_pm, NULL, acpi_pcc_hotkey_resume);
181 static struct acpi_driver acpi_pcc_driver = {
182 .name = ACPI_PCC_DRIVER_NAME,
183 .class = ACPI_PCC_CLASS,
184 .ids = pcc_device_ids,
186 .add = acpi_pcc_hotkey_add,
187 .remove = acpi_pcc_hotkey_remove,
188 .notify = acpi_pcc_hotkey_notify,
190 .drv.pm = &acpi_pcc_hotkey_pm,
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 } },
210 unsigned long num_sifr;
213 struct acpi_device *device;
214 struct input_dev *input_dev;
215 struct backlight_device *backlight;
218 /* method access functions */
219 static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)
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, },
227 struct acpi_object_list params = {
228 .count = ARRAY_SIZE(in_objs),
231 acpi_status status = AE_OK;
233 status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET,
236 return (status == AE_OK) ? 0 : -EIO;
239 static inline int acpi_pcc_get_sqty(struct acpi_device *device)
241 unsigned long long s;
244 status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,
246 if (ACPI_SUCCESS(status))
249 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
250 "evaluation error HKEY.SQTY\n"));
255 static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc)
258 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
259 union acpi_object *hkey = NULL;
262 status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, NULL,
264 if (ACPI_FAILURE(status)) {
265 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
266 "evaluation error HKEY.SINF\n"));
270 hkey = buffer.pointer;
271 if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
272 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
277 if (pcc->num_sifr < hkey->package.count) {
278 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
279 "SQTY reports bad SINF length\n"));
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;
289 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
290 "Invalid HKEY.SINF data\n"));
292 pcc->sinf[hkey->package.count] = -1;
295 kfree(buffer.pointer);
296 return status == AE_OK;
299 /* backlight API interface functions */
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
306 static int bl_get(struct backlight_device *bd)
308 struct pcc_acpi *pcc = bl_get_data(bd);
310 if (!acpi_pcc_retrieve_biosdata(pcc))
313 return pcc->sinf[SINF_AC_CUR_BRIGHT];
316 static int bl_set_status(struct backlight_device *bd)
318 struct pcc_acpi *pcc = bl_get_data(bd);
319 int bright = bd->props.brightness;
322 if (!acpi_pcc_retrieve_biosdata(pcc))
325 if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
326 bright = pcc->sinf[SINF_AC_MIN_BRIGHT];
328 if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT])
329 bright = pcc->sinf[SINF_DC_MIN_BRIGHT];
331 if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] ||
332 bright > pcc->sinf[SINF_AC_MAX_BRIGHT])
335 rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright);
339 return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright);
342 static const struct backlight_ops pcc_backlight_ops = {
343 .get_brightness = bl_get,
344 .update_status = bl_set_status,
348 /* sysfs user interface functions */
350 static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
353 struct acpi_device *acpi = to_acpi_device(dev);
354 struct pcc_acpi *pcc = acpi_driver_data(acpi);
356 if (!acpi_pcc_retrieve_biosdata(pcc))
359 return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
362 static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
365 struct acpi_device *acpi = to_acpi_device(dev);
366 struct pcc_acpi *pcc = acpi_driver_data(acpi);
368 if (!acpi_pcc_retrieve_biosdata(pcc))
371 return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
374 static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
377 struct acpi_device *acpi = to_acpi_device(dev);
378 struct pcc_acpi *pcc = acpi_driver_data(acpi);
380 if (!acpi_pcc_retrieve_biosdata(pcc))
383 return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]);
386 static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
389 struct acpi_device *acpi = to_acpi_device(dev);
390 struct pcc_acpi *pcc = acpi_driver_data(acpi);
392 if (!acpi_pcc_retrieve_biosdata(pcc))
395 return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
398 static ssize_t set_sticky(struct device *dev, struct device_attribute *attr,
399 const char *buf, size_t count)
401 struct acpi_device *acpi = to_acpi_device(dev);
402 struct pcc_acpi *pcc = acpi_driver_data(acpi);
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;
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);
419 static struct attribute *pcc_sysfs_entries[] = {
420 &dev_attr_numbatt.attr,
421 &dev_attr_lcdtype.attr,
423 &dev_attr_sticky_key.attr,
427 static const struct attribute_group pcc_attr_group = {
428 .name = NULL, /* put in device directory */
429 .attrs = pcc_sysfs_entries,
433 /* hotkey input device driver */
435 static int sleep_keydown_seen;
436 static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
438 struct input_dev *hotk_input_dev = pcc->input_dev;
440 unsigned long long result;
442 rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
444 if (ACPI_FAILURE(rc)) {
445 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
446 "error getting hotkey status\n"));
450 /* hack: some firmware sends no key down for sleep / hibernate */
451 if ((result & 0xf) == 0x7 || (result & 0xf) == 0xa) {
453 sleep_keydown_seen = 1;
454 if (!sleep_keydown_seen)
455 sparse_keymap_report_event(hotk_input_dev,
456 result & 0xf, 0x80, false);
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));
465 static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)
467 struct pcc_acpi *pcc = acpi_driver_data(device);
471 acpi_pcc_generate_keyinput(pcc);
479 static int acpi_pcc_init_input(struct pcc_acpi *pcc)
481 struct input_dev *input_dev;
484 input_dev = input_allocate_device();
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;
495 error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL);
497 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
498 "Unable to setup input device keymap\n"));
502 error = input_register_device(input_dev);
504 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
505 "Unable to register input device\n"));
509 pcc->input_dev = input_dev;
513 input_free_device(input_dev);
517 /* kernel module interface */
519 #ifdef CONFIG_PM_SLEEP
520 static int acpi_pcc_hotkey_resume(struct device *dev)
522 struct pcc_acpi *pcc;
527 pcc = acpi_driver_data(to_acpi_device(dev));
531 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n",
534 return acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
538 static int acpi_pcc_hotkey_add(struct acpi_device *device)
540 struct backlight_properties props;
541 struct pcc_acpi *pcc;
542 int num_sifr, result;
547 num_sifr = acpi_pcc_get_sqty(device);
549 if (num_sifr < 0 || num_sifr > 255) {
550 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr out of range"));
554 pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL);
556 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
557 "Couldn't allocate mem for pcc"));
561 pcc->sinf = kcalloc(num_sifr + 1, sizeof(u32), GFP_KERNEL);
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);
574 result = acpi_pcc_init_input(pcc);
576 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
577 "Error installing keyinput handler\n"));
581 if (!acpi_pcc_retrieve_biosdata(pcc)) {
582 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
583 "Couldn't retrieve BIOS data\n"));
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);
598 /* read the initial brightness setting from the hardware */
599 pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
601 /* read the initial sticky key mode from the hardware */
602 pcc->sticky_mode = pcc->sinf[SINF_STICKY_KEY];
604 /* add sysfs attributes */
605 result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);
612 backlight_device_unregister(pcc->backlight);
614 input_unregister_device(pcc->input_dev);
623 static int acpi_pcc_hotkey_remove(struct acpi_device *device)
625 struct pcc_acpi *pcc = acpi_driver_data(device);
630 sysfs_remove_group(&device->dev.kobj, &pcc_attr_group);
632 backlight_device_unregister(pcc->backlight);
634 input_unregister_device(pcc->input_dev);
642 module_acpi_driver(acpi_pcc_driver);