1142 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Panasonic HotKey and LCD brightness control driver4 * (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 Belmonte11 *12 *---------------------------------------------------------------------------13 *14 * ChangeLog:15 * Aug.18, 2020 Kenneth Chan <kenneth.t.chan@gmail.com>16 * -v0.98 add platform devices for firmware brightness registers17 * add support for battery charging threshold (eco mode)18 * resolve hotkey double trigger19 * add write support to mute20 * fix sticky_key init bug21 * fix naming of platform files for consistency with other22 * modules23 * split MODULE_AUTHOR() by one author per macro call24 * replace ACPI prints with pr_*() macros25 * -v0.97 add support for cdpower hardware switch26 * -v0.96 merge Lucina's enhancement27 * Jan.13, 2009 Martin Lucina <mato@kotelna.sk>28 * - add support for optical driver power in29 * Y and W series30 *31 * Sep.23, 2008 Harald Welte <laforge@gnumonks.org>32 * -v0.95 rename driver from drivers/acpi/pcc_acpi.c to33 * drivers/misc/panasonic-laptop.c34 *35 * Jul.04, 2008 Harald Welte <laforge@gnumonks.org>36 * -v0.94 replace /proc interface with device attributes37 * support {set,get}keycode on th input device38 *39 * Jun.27, 2008 Harald Welte <laforge@gnumonks.org>40 * -v0.92 merge with 2.6.26-rc6 input API changes41 * remove broken <= 2.6.15 kernel support42 * resolve all compiler warnings43 * various coding style fixes (checkpatch.pl)44 * add support for backlight api45 * major code restructuring46 *47 * Dac.28, 2007 Harald Welte <laforge@gnumonks.org>48 * -v0.91 merge with 2.6.24-rc6 ACPI changes49 *50 * Nov.04, 2006 Hiroshi Miura <miura@da-cha.org>51 * -v0.9 remove warning about section reference.52 * remove acpi_os_free53 * add /proc/acpi/pcc/brightness interface for HAL access54 * merge dbronaugh's enhancement55 * Aug.17, 2004 David Bronaugh (dbronaugh)56 * - Added screen brightness setting interface57 * Thanks to FreeBSD crew (acpi_panasonic.c)58 * for the ideas I needed to accomplish it59 *60 * May.29, 2006 Hiroshi Miura <miura@da-cha.org>61 * -v0.8.4 follow to change keyinput structure62 * thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,63 * Jacob Bower <jacob.bower@ic.ac.uk> and64 * Hiroshi Yokota for providing solutions.65 *66 * Oct.02, 2004 Hiroshi Miura <miura@da-cha.org>67 * -v0.8.2 merge code of YOKOTA Hiroshi68 * <yokota@netlab.is.tsukuba.ac.jp>.69 * Add sticky key mode interface.70 * Refactoring acpi_pcc_generate_keyinput().71 *72 * Sep.15, 2004 Hiroshi Miura <miura@da-cha.org>73 * -v0.8 Generate key input event on input subsystem.74 * This is based on yet another driver written by75 * Ryuta Nakanishi.76 *77 * Sep.10, 2004 Hiroshi Miura <miura@da-cha.org>78 * -v0.7 Change proc interface functions using seq_file79 * facility as same as other ACPI drivers.80 *81 * Aug.28, 2004 Hiroshi Miura <miura@da-cha.org>82 * -v0.6.4 Fix a silly error with status checking83 *84 * Aug.25, 2004 Hiroshi Miura <miura@da-cha.org>85 * -v0.6.3 replace read_acpi_int by standard function86 * acpi_evaluate_integer87 * some clean up and make smart copyright notice.88 * fix return value of pcc_acpi_get_key()89 * fix checking return value of acpi_bus_register_driver()90 *91 * Aug.22, 2004 David Bronaugh <dbronaugh@linuxboxen.org>92 * -v0.6.2 Add check on ACPI data (num_sifr)93 * Coding style cleanups, better error messages/handling94 * Fixed an off-by-one error in memory allocation95 *96 * Aug.21, 2004 David Bronaugh <dbronaugh@linuxboxen.org>97 * -v0.6.1 Fix a silly error with status checking98 *99 * Aug.20, 2004 David Bronaugh <dbronaugh@linuxboxen.org>100 * - v0.6 Correct brightness controls to reflect reality101 * based on information gleaned by Hiroshi Miura102 * and discussions with Hiroshi Miura103 *104 * Aug.10, 2004 Hiroshi Miura <miura@da-cha.org>105 * - v0.5 support LCD brightness control106 * based on the disclosed information by MEI.107 *108 * Jul.25, 2004 Hiroshi Miura <miura@da-cha.org>109 * - v0.4 first post version110 * add function to retrive SIFR111 *112 * Jul.24, 2004 Hiroshi Miura <miura@da-cha.org>113 * - v0.3 get proper status of hotkey114 *115 * Jul.22, 2004 Hiroshi Miura <miura@da-cha.org>116 * - v0.2 add HotKey handler117 *118 * Jul.17, 2004 Hiroshi Miura <miura@da-cha.org>119 * - v0.1 start from toshiba_acpi driver written by John Belmonte120 */121 122#include <linux/acpi.h>123#include <linux/backlight.h>124#include <linux/bits.h>125#include <linux/ctype.h>126#include <linux/i8042.h>127#include <linux/init.h>128#include <linux/input.h>129#include <linux/input/sparse-keymap.h>130#include <linux/kernel.h>131#include <linux/module.h>132#include <linux/platform_device.h>133#include <linux/seq_file.h>134#include <linux/serio.h>135#include <linux/slab.h>136#include <linux/types.h>137#include <linux/uaccess.h>138#include <acpi/video.h>139 140MODULE_AUTHOR("Hiroshi Miura <miura@da-cha.org>");141MODULE_AUTHOR("David Bronaugh <dbronaugh@linuxboxen.org>");142MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");143MODULE_AUTHOR("Martin Lucina <mato@kotelna.sk>");144MODULE_AUTHOR("Kenneth Chan <kenneth.t.chan@gmail.com>");145MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops");146MODULE_LICENSE("GPL");147 148#define LOGPREFIX "pcc_acpi: "149 150/* Define ACPI PATHs */151/* Lets note hotkeys */152#define METHOD_HKEY_QUERY "HINF"153#define METHOD_HKEY_SQTY "SQTY"154#define METHOD_HKEY_SINF "SINF"155#define METHOD_HKEY_SSET "SSET"156#define METHOD_ECWR "\\_SB.ECWR"157#define HKEY_NOTIFY 0x80158#define ECO_MODE_OFF 0x00159#define ECO_MODE_ON 0x80160 161#define ACPI_PCC_DRIVER_NAME "Panasonic Laptop Support"162#define ACPI_PCC_DEVICE_NAME "Hotkey"163#define ACPI_PCC_CLASS "pcc"164 165#define ACPI_PCC_INPUT_PHYS "panasonic/hkey0"166 167/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent168 ECO_MODEs: 0x03 = off, 0x83 = on169*/170enum SINF_BITS { SINF_NUM_BATTERIES = 0,171 SINF_LCD_TYPE,172 SINF_AC_MAX_BRIGHT,173 SINF_AC_MIN_BRIGHT,174 SINF_AC_CUR_BRIGHT,175 SINF_DC_MAX_BRIGHT,176 SINF_DC_MIN_BRIGHT,177 SINF_DC_CUR_BRIGHT,178 SINF_MUTE,179 SINF_RESERVED,180 SINF_ECO_MODE = 0x0A,181 SINF_CUR_BRIGHT = 0x0D,182 SINF_STICKY_KEY = 0x80,183 };184/* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */185 186static int acpi_pcc_hotkey_add(struct acpi_device *device);187static void acpi_pcc_hotkey_remove(struct acpi_device *device);188static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event);189 190static const struct acpi_device_id pcc_device_ids[] = {191 { "MAT0012", 0},192 { "MAT0013", 0},193 { "MAT0018", 0},194 { "MAT0019", 0},195 { "", 0},196};197MODULE_DEVICE_TABLE(acpi, pcc_device_ids);198 199#ifdef CONFIG_PM_SLEEP200static int acpi_pcc_hotkey_resume(struct device *dev);201#endif202static SIMPLE_DEV_PM_OPS(acpi_pcc_hotkey_pm, NULL, acpi_pcc_hotkey_resume);203 204static struct acpi_driver acpi_pcc_driver = {205 .name = ACPI_PCC_DRIVER_NAME,206 .class = ACPI_PCC_CLASS,207 .ids = pcc_device_ids,208 .ops = {209 .add = acpi_pcc_hotkey_add,210 .remove = acpi_pcc_hotkey_remove,211 .notify = acpi_pcc_hotkey_notify,212 },213 .drv.pm = &acpi_pcc_hotkey_pm,214};215 216static const struct key_entry panasonic_keymap[] = {217 { KE_KEY, 0, { KEY_RESERVED } },218 { KE_KEY, 1, { KEY_BRIGHTNESSDOWN } },219 { KE_KEY, 2, { KEY_BRIGHTNESSUP } },220 { KE_KEY, 3, { KEY_DISPLAYTOGGLE } },221 { KE_KEY, 4, { KEY_MUTE } },222 { KE_KEY, 5, { KEY_VOLUMEDOWN } },223 { KE_KEY, 6, { KEY_VOLUMEUP } },224 { KE_KEY, 7, { KEY_SLEEP } },225 { KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */226 { KE_KEY, 9, { KEY_BATTERY } },227 { KE_KEY, 10, { KEY_SUSPEND } },228 { KE_KEY, 21, { KEY_MACRO1 } },229 { KE_KEY, 22, { KEY_MACRO2 } },230 { KE_KEY, 24, { KEY_MACRO3 } },231 { KE_KEY, 25, { KEY_MACRO4 } },232 { KE_KEY, 34, { KEY_MACRO5 } },233 { KE_KEY, 35, { KEY_MACRO6 } },234 { KE_KEY, 36, { KEY_MACRO7 } },235 { KE_KEY, 37, { KEY_MACRO8 } },236 { KE_KEY, 41, { KEY_MACRO9 } },237 { KE_KEY, 42, { KEY_MACRO10 } },238 { KE_KEY, 43, { KEY_MACRO11 } },239 { KE_END, 0 }240};241 242struct pcc_acpi {243 acpi_handle handle;244 unsigned long num_sifr;245 int sticky_key;246 int eco_mode;247 int mute;248 int ac_brightness;249 int dc_brightness;250 int current_brightness;251 u32 *sinf;252 struct acpi_device *device;253 struct input_dev *input_dev;254 struct backlight_device *backlight;255 struct platform_device *platform;256};257 258/*259 * On some Panasonic models the volume up / down / mute keys send duplicate260 * keypress events over the PS/2 kbd interface, filter these out.261 */262static bool panasonic_i8042_filter(unsigned char data, unsigned char str,263 struct serio *port)264{265 static bool extended;266 267 if (str & I8042_STR_AUXDATA)268 return false;269 270 if (data == 0xe0) {271 extended = true;272 return true;273 } else if (extended) {274 extended = false;275 276 switch (data & 0x7f) {277 case 0x20: /* e0 20 / e0 a0, Volume Mute press / release */278 case 0x2e: /* e0 2e / e0 ae, Volume Down press / release */279 case 0x30: /* e0 30 / e0 b0, Volume Up press / release */280 return true;281 default:282 /*283 * Report the previously filtered e0 before continuing284 * with the next non-filtered byte.285 */286 serio_interrupt(port, 0xe0, 0);287 return false;288 }289 }290 291 return false;292}293 294/* method access functions */295static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)296{297 union acpi_object in_objs[] = {298 { .integer.type = ACPI_TYPE_INTEGER,299 .integer.value = func, },300 { .integer.type = ACPI_TYPE_INTEGER,301 .integer.value = val, },302 };303 struct acpi_object_list params = {304 .count = ARRAY_SIZE(in_objs),305 .pointer = in_objs,306 };307 acpi_status status = AE_OK;308 309 status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET,310 ¶ms, NULL);311 312 return (status == AE_OK) ? 0 : -EIO;313}314 315static inline int acpi_pcc_get_sqty(struct acpi_device *device)316{317 unsigned long long s;318 acpi_status status;319 320 status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,321 NULL, &s);322 if (ACPI_SUCCESS(status))323 return s;324 else {325 pr_err("evaluation error HKEY.SQTY\n");326 return -EINVAL;327 }328}329 330static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc)331{332 acpi_status status;333 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};334 union acpi_object *hkey = NULL;335 int i;336 337 status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, NULL,338 &buffer);339 if (ACPI_FAILURE(status)) {340 pr_err("evaluation error HKEY.SINF\n");341 return 0;342 }343 344 hkey = buffer.pointer;345 if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {346 pr_err("Invalid HKEY.SINF\n");347 status = AE_ERROR;348 goto end;349 }350 351 if (pcc->num_sifr < hkey->package.count) {352 pr_err("SQTY reports bad SINF length SQTY: %lu SINF-pkg-count: %u\n",353 pcc->num_sifr, hkey->package.count);354 status = AE_ERROR;355 goto end;356 }357 358 for (i = 0; i < hkey->package.count; i++) {359 union acpi_object *element = &(hkey->package.elements[i]);360 if (likely(element->type == ACPI_TYPE_INTEGER)) {361 pcc->sinf[i] = element->integer.value;362 } else363 pr_err("Invalid HKEY.SINF data\n");364 }365 pcc->sinf[hkey->package.count] = -1;366 367end:368 kfree(buffer.pointer);369 return status == AE_OK;370}371 372/* backlight API interface functions */373 374/* This driver currently treats AC and DC brightness identical,375 * since we don't need to invent an interface to the core ACPI376 * logic to receive events in case a power supply is plugged in377 * or removed */378 379static int bl_get(struct backlight_device *bd)380{381 struct pcc_acpi *pcc = bl_get_data(bd);382 383 if (!acpi_pcc_retrieve_biosdata(pcc))384 return -EIO;385 386 return pcc->sinf[SINF_AC_CUR_BRIGHT];387}388 389static int bl_set_status(struct backlight_device *bd)390{391 struct pcc_acpi *pcc = bl_get_data(bd);392 int bright = bd->props.brightness;393 int rc;394 395 if (!acpi_pcc_retrieve_biosdata(pcc))396 return -EIO;397 398 if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])399 bright = pcc->sinf[SINF_AC_MIN_BRIGHT];400 401 if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT])402 bright = pcc->sinf[SINF_DC_MIN_BRIGHT];403 404 if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] ||405 bright > pcc->sinf[SINF_AC_MAX_BRIGHT])406 return -EINVAL;407 408 rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright);409 if (rc < 0)410 return rc;411 412 return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright);413}414 415static const struct backlight_ops pcc_backlight_ops = {416 .get_brightness = bl_get,417 .update_status = bl_set_status,418};419 420 421/* returns ACPI_SUCCESS if methods to control optical drive are present */422 423static acpi_status check_optd_present(void)424{425 acpi_status status = AE_OK;426 acpi_handle handle;427 428 status = acpi_get_handle(NULL, "\\_SB.STAT", &handle);429 if (ACPI_FAILURE(status))430 goto out;431 status = acpi_get_handle(NULL, "\\_SB.FBAY", &handle);432 if (ACPI_FAILURE(status))433 goto out;434 status = acpi_get_handle(NULL, "\\_SB.CDDI", &handle);435 if (ACPI_FAILURE(status))436 goto out;437 438out:439 return status;440}441 442/* get optical driver power state */443 444static int get_optd_power_state(void)445{446 acpi_status status;447 unsigned long long state;448 int result;449 450 status = acpi_evaluate_integer(NULL, "\\_SB.STAT", NULL, &state);451 if (ACPI_FAILURE(status)) {452 pr_err("evaluation error _SB.STAT\n");453 result = -EIO;454 goto out;455 }456 switch (state) {457 case 0: /* power off */458 result = 0;459 break;460 case 0x0f: /* power on */461 result = 1;462 break;463 default:464 result = -EIO;465 break;466 }467 468out:469 return result;470}471 472/* set optical drive power state */473 474static int set_optd_power_state(int new_state)475{476 int result;477 acpi_status status;478 479 result = get_optd_power_state();480 if (result < 0)481 goto out;482 if (new_state == result)483 goto out;484 485 switch (new_state) {486 case 0: /* power off */487 /* Call CDDR instead, since they both call the same method488 * while CDDI takes 1 arg and we are not quite sure what it is.489 */490 status = acpi_evaluate_object(NULL, "\\_SB.CDDR", NULL, NULL);491 if (ACPI_FAILURE(status)) {492 pr_err("evaluation error _SB.CDDR\n");493 result = -EIO;494 }495 break;496 case 1: /* power on */497 status = acpi_evaluate_object(NULL, "\\_SB.FBAY", NULL, NULL);498 if (ACPI_FAILURE(status)) {499 pr_err("evaluation error _SB.FBAY\n");500 result = -EIO;501 }502 break;503 default:504 result = -EINVAL;505 break;506 }507 508out:509 return result;510}511 512 513/* sysfs user interface functions */514 515static ssize_t numbatt_show(struct device *dev, struct device_attribute *attr,516 char *buf)517{518 struct acpi_device *acpi = to_acpi_device(dev);519 struct pcc_acpi *pcc = acpi_driver_data(acpi);520 521 if (!acpi_pcc_retrieve_biosdata(pcc))522 return -EIO;523 524 return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);525}526 527static ssize_t lcdtype_show(struct device *dev, struct device_attribute *attr,528 char *buf)529{530 struct acpi_device *acpi = to_acpi_device(dev);531 struct pcc_acpi *pcc = acpi_driver_data(acpi);532 533 if (!acpi_pcc_retrieve_biosdata(pcc))534 return -EIO;535 536 return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_LCD_TYPE]);537}538 539static ssize_t mute_show(struct device *dev, struct device_attribute *attr,540 char *buf)541{542 struct acpi_device *acpi = to_acpi_device(dev);543 struct pcc_acpi *pcc = acpi_driver_data(acpi);544 545 if (!acpi_pcc_retrieve_biosdata(pcc))546 return -EIO;547 548 return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_MUTE]);549}550 551static ssize_t mute_store(struct device *dev, struct device_attribute *attr,552 const char *buf, size_t count)553{554 struct acpi_device *acpi = to_acpi_device(dev);555 struct pcc_acpi *pcc = acpi_driver_data(acpi);556 int err, val;557 558 err = kstrtoint(buf, 0, &val);559 if (err)560 return err;561 if (val == 0 || val == 1) {562 acpi_pcc_write_sset(pcc, SINF_MUTE, val);563 pcc->mute = val;564 }565 566 return count;567}568 569static ssize_t sticky_key_show(struct device *dev, struct device_attribute *attr,570 char *buf)571{572 struct acpi_device *acpi = to_acpi_device(dev);573 struct pcc_acpi *pcc = acpi_driver_data(acpi);574 575 if (!acpi_pcc_retrieve_biosdata(pcc))576 return -EIO;577 578 return sysfs_emit(buf, "%u\n", pcc->sticky_key);579}580 581static ssize_t sticky_key_store(struct device *dev, struct device_attribute *attr,582 const char *buf, size_t count)583{584 struct acpi_device *acpi = to_acpi_device(dev);585 struct pcc_acpi *pcc = acpi_driver_data(acpi);586 int err, val;587 588 err = kstrtoint(buf, 0, &val);589 if (err)590 return err;591 if (val == 0 || val == 1) {592 acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val);593 pcc->sticky_key = val;594 }595 596 return count;597}598 599static ssize_t eco_mode_show(struct device *dev, struct device_attribute *attr,600 char *buf)601{602 struct acpi_device *acpi = to_acpi_device(dev);603 struct pcc_acpi *pcc = acpi_driver_data(acpi);604 int result;605 606 if (!acpi_pcc_retrieve_biosdata(pcc))607 return -EIO;608 609 switch (pcc->sinf[SINF_ECO_MODE]) {610 case (ECO_MODE_OFF + 3):611 result = 0;612 break;613 case (ECO_MODE_ON + 3):614 result = 1;615 break;616 default:617 result = -EIO;618 break;619 }620 return sysfs_emit(buf, "%u\n", result);621}622 623static ssize_t eco_mode_store(struct device *dev, struct device_attribute *attr,624 const char *buf, size_t count)625{626 struct acpi_device *acpi = to_acpi_device(dev);627 struct pcc_acpi *pcc = acpi_driver_data(acpi);628 int err, state;629 630 union acpi_object param[2];631 struct acpi_object_list input;632 acpi_status status;633 634 param[0].type = ACPI_TYPE_INTEGER;635 param[0].integer.value = 0x15;636 param[1].type = ACPI_TYPE_INTEGER;637 input.count = 2;638 input.pointer = param;639 640 err = kstrtoint(buf, 0, &state);641 if (err)642 return err;643 644 switch (state) {645 case 0:646 param[1].integer.value = ECO_MODE_OFF;647 pcc->sinf[SINF_ECO_MODE] = 0;648 pcc->eco_mode = 0;649 break;650 case 1:651 param[1].integer.value = ECO_MODE_ON;652 pcc->sinf[SINF_ECO_MODE] = 1;653 pcc->eco_mode = 1;654 break;655 default:656 /* nothing to do */657 return count;658 }659 660 status = acpi_evaluate_object(NULL, METHOD_ECWR,661 &input, NULL);662 if (ACPI_FAILURE(status)) {663 pr_err("%s evaluation failed\n", METHOD_ECWR);664 return -EINVAL;665 }666 667 return count;668}669 670static ssize_t ac_brightness_show(struct device *dev, struct device_attribute *attr,671 char *buf)672{673 struct acpi_device *acpi = to_acpi_device(dev);674 struct pcc_acpi *pcc = acpi_driver_data(acpi);675 676 if (!acpi_pcc_retrieve_biosdata(pcc))677 return -EIO;678 679 return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_AC_CUR_BRIGHT]);680}681 682static ssize_t ac_brightness_store(struct device *dev, struct device_attribute *attr,683 const char *buf, size_t count)684{685 struct acpi_device *acpi = to_acpi_device(dev);686 struct pcc_acpi *pcc = acpi_driver_data(acpi);687 int err, val;688 689 err = kstrtoint(buf, 0, &val);690 if (err)691 return err;692 if (val >= 0 && val <= 255) {693 acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, val);694 pcc->ac_brightness = val;695 }696 697 return count;698}699 700static ssize_t dc_brightness_show(struct device *dev, struct device_attribute *attr,701 char *buf)702{703 struct acpi_device *acpi = to_acpi_device(dev);704 struct pcc_acpi *pcc = acpi_driver_data(acpi);705 706 if (!acpi_pcc_retrieve_biosdata(pcc))707 return -EIO;708 709 return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_DC_CUR_BRIGHT]);710}711 712static ssize_t dc_brightness_store(struct device *dev, struct device_attribute *attr,713 const char *buf, size_t count)714{715 struct acpi_device *acpi = to_acpi_device(dev);716 struct pcc_acpi *pcc = acpi_driver_data(acpi);717 int err, val;718 719 err = kstrtoint(buf, 0, &val);720 if (err)721 return err;722 if (val >= 0 && val <= 255) {723 acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, val);724 pcc->dc_brightness = val;725 }726 727 return count;728}729 730static ssize_t current_brightness_show(struct device *dev, struct device_attribute *attr,731 char *buf)732{733 struct acpi_device *acpi = to_acpi_device(dev);734 struct pcc_acpi *pcc = acpi_driver_data(acpi);735 736 if (!acpi_pcc_retrieve_biosdata(pcc))737 return -EIO;738 739 return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_CUR_BRIGHT]);740}741 742static ssize_t current_brightness_store(struct device *dev, struct device_attribute *attr,743 const char *buf, size_t count)744{745 struct acpi_device *acpi = to_acpi_device(dev);746 struct pcc_acpi *pcc = acpi_driver_data(acpi);747 int err, val;748 749 err = kstrtoint(buf, 0, &val);750 if (err)751 return err;752 753 if (val >= 0 && val <= 255) {754 err = acpi_pcc_write_sset(pcc, SINF_CUR_BRIGHT, val);755 pcc->current_brightness = val;756 }757 758 return count;759}760 761static ssize_t cdpower_show(struct device *dev, struct device_attribute *attr,762 char *buf)763{764 return sysfs_emit(buf, "%d\n", get_optd_power_state());765}766 767static ssize_t cdpower_store(struct device *dev, struct device_attribute *attr,768 const char *buf, size_t count)769{770 int err, val;771 772 err = kstrtoint(buf, 10, &val);773 if (err)774 return err;775 set_optd_power_state(val);776 return count;777}778 779static DEVICE_ATTR_RO(numbatt);780static DEVICE_ATTR_RO(lcdtype);781static DEVICE_ATTR_RW(mute);782static DEVICE_ATTR_RW(sticky_key);783static DEVICE_ATTR_RW(eco_mode);784static DEVICE_ATTR_RW(ac_brightness);785static DEVICE_ATTR_RW(dc_brightness);786static DEVICE_ATTR_RW(current_brightness);787static DEVICE_ATTR_RW(cdpower);788 789static umode_t pcc_sysfs_is_visible(struct kobject *kobj, struct attribute *attr, int idx)790{791 struct device *dev = kobj_to_dev(kobj);792 struct acpi_device *acpi = to_acpi_device(dev);793 struct pcc_acpi *pcc = acpi_driver_data(acpi);794 795 if (attr == &dev_attr_mute.attr)796 return (pcc->num_sifr > SINF_MUTE) ? attr->mode : 0;797 798 if (attr == &dev_attr_eco_mode.attr)799 return (pcc->num_sifr > SINF_ECO_MODE) ? attr->mode : 0;800 801 if (attr == &dev_attr_current_brightness.attr)802 return (pcc->num_sifr > SINF_CUR_BRIGHT) ? attr->mode : 0;803 804 return attr->mode;805}806 807static struct attribute *pcc_sysfs_entries[] = {808 &dev_attr_numbatt.attr,809 &dev_attr_lcdtype.attr,810 &dev_attr_mute.attr,811 &dev_attr_sticky_key.attr,812 &dev_attr_eco_mode.attr,813 &dev_attr_ac_brightness.attr,814 &dev_attr_dc_brightness.attr,815 &dev_attr_current_brightness.attr,816 &dev_attr_cdpower.attr,817 NULL,818};819 820static const struct attribute_group pcc_attr_group = {821 .name = NULL, /* put in device directory */822 .attrs = pcc_sysfs_entries,823 .is_visible = pcc_sysfs_is_visible,824};825 826 827/* hotkey input device driver */828 829static int sleep_keydown_seen;830static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)831{832 struct input_dev *hotk_input_dev = pcc->input_dev;833 int rc;834 unsigned long long result;835 unsigned int key;836 unsigned int updown;837 838 rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,839 NULL, &result);840 if (ACPI_FAILURE(rc)) {841 pr_err("error getting hotkey status\n");842 return;843 }844 845 key = result & GENMASK(6, 0);846 updown = result & BIT(7); /* 0x80 == key down; 0x00 = key up */847 848 /* hack: some firmware sends no key down for sleep / hibernate */849 if (key == 7 || key == 10) {850 if (updown)851 sleep_keydown_seen = 1;852 if (!sleep_keydown_seen)853 sparse_keymap_report_event(hotk_input_dev,854 key, 0x80, false);855 }856 857 /*858 * Don't report brightness key-presses if they are also reported859 * by the ACPI video bus.860 */861 if ((key == 1 || key == 2) && acpi_video_handles_brightness_key_presses())862 return;863 864 if (!sparse_keymap_report_event(hotk_input_dev, key, updown, false))865 pr_err("Unknown hotkey event: 0x%04llx\n", result);866}867 868static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)869{870 struct pcc_acpi *pcc = acpi_driver_data(device);871 872 switch (event) {873 case HKEY_NOTIFY:874 acpi_pcc_generate_keyinput(pcc);875 break;876 default:877 /* nothing to do */878 break;879 }880}881 882static void pcc_optd_notify(acpi_handle handle, u32 event, void *data)883{884 if (event != ACPI_NOTIFY_EJECT_REQUEST)885 return;886 887 set_optd_power_state(0);888}889 890static int pcc_register_optd_notifier(struct pcc_acpi *pcc, char *node)891{892 acpi_status status;893 acpi_handle handle;894 895 status = acpi_get_handle(NULL, node, &handle);896 897 if (ACPI_SUCCESS(status)) {898 status = acpi_install_notify_handler(handle,899 ACPI_SYSTEM_NOTIFY,900 pcc_optd_notify, pcc);901 if (ACPI_FAILURE(status))902 pr_err("Failed to register notify on %s\n", node);903 } else904 return -ENODEV;905 906 return 0;907}908 909static void pcc_unregister_optd_notifier(struct pcc_acpi *pcc, char *node)910{911 acpi_status status = AE_OK;912 acpi_handle handle;913 914 status = acpi_get_handle(NULL, node, &handle);915 916 if (ACPI_SUCCESS(status)) {917 status = acpi_remove_notify_handler(handle,918 ACPI_SYSTEM_NOTIFY,919 pcc_optd_notify);920 if (ACPI_FAILURE(status))921 pr_err("Error removing optd notify handler %s\n",922 node);923 }924}925 926static int acpi_pcc_init_input(struct pcc_acpi *pcc)927{928 struct input_dev *input_dev;929 int error;930 931 input_dev = input_allocate_device();932 if (!input_dev)933 return -ENOMEM;934 935 input_dev->name = ACPI_PCC_DRIVER_NAME;936 input_dev->phys = ACPI_PCC_INPUT_PHYS;937 input_dev->id.bustype = BUS_HOST;938 input_dev->id.vendor = 0x0001;939 input_dev->id.product = 0x0001;940 input_dev->id.version = 0x0100;941 942 error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL);943 if (error) {944 pr_err("Unable to setup input device keymap\n");945 goto err_free_dev;946 }947 948 error = input_register_device(input_dev);949 if (error) {950 pr_err("Unable to register input device\n");951 goto err_free_dev;952 }953 954 pcc->input_dev = input_dev;955 return 0;956 957 err_free_dev:958 input_free_device(input_dev);959 return error;960}961 962/* kernel module interface */963 964#ifdef CONFIG_PM_SLEEP965static int acpi_pcc_hotkey_resume(struct device *dev)966{967 struct pcc_acpi *pcc;968 969 if (!dev)970 return -EINVAL;971 972 pcc = acpi_driver_data(to_acpi_device(dev));973 if (!pcc)974 return -EINVAL;975 976 if (pcc->num_sifr > SINF_MUTE)977 acpi_pcc_write_sset(pcc, SINF_MUTE, pcc->mute);978 if (pcc->num_sifr > SINF_ECO_MODE)979 acpi_pcc_write_sset(pcc, SINF_ECO_MODE, pcc->eco_mode);980 acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_key);981 acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, pcc->ac_brightness);982 acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, pcc->dc_brightness);983 if (pcc->num_sifr > SINF_CUR_BRIGHT)984 acpi_pcc_write_sset(pcc, SINF_CUR_BRIGHT, pcc->current_brightness);985 986 return 0;987}988#endif989 990static int acpi_pcc_hotkey_add(struct acpi_device *device)991{992 struct backlight_properties props;993 struct pcc_acpi *pcc;994 int num_sifr, result;995 996 if (!device)997 return -EINVAL;998 999 num_sifr = acpi_pcc_get_sqty(device);1000 1001 /*1002 * pcc->sinf is expected to at least have the AC+DC brightness entries.1003 * Accesses to higher SINF entries are checked against num_sifr.1004 */1005 if (num_sifr <= SINF_DC_CUR_BRIGHT || num_sifr > 255) {1006 pr_err("num_sifr %d out of range %d - 255\n", num_sifr, SINF_DC_CUR_BRIGHT + 1);1007 return -ENODEV;1008 }1009 1010 /*1011 * Some DSDT-s have an off-by-one bug where the SINF package count is1012 * one higher than the SQTY reported value, allocate 1 entry extra.1013 */1014 num_sifr++;1015 1016 pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL);1017 if (!pcc) {1018 pr_err("Couldn't allocate mem for pcc");1019 return -ENOMEM;1020 }1021 1022 pcc->sinf = kcalloc(num_sifr + 1, sizeof(u32), GFP_KERNEL);1023 if (!pcc->sinf) {1024 result = -ENOMEM;1025 goto out_hotkey;1026 }1027 1028 pcc->device = device;1029 pcc->handle = device->handle;1030 pcc->num_sifr = num_sifr;1031 device->driver_data = pcc;1032 strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);1033 strcpy(acpi_device_class(device), ACPI_PCC_CLASS);1034 1035 result = acpi_pcc_init_input(pcc);1036 if (result) {1037 pr_err("Error installing keyinput handler\n");1038 goto out_sinf;1039 }1040 1041 if (!acpi_pcc_retrieve_biosdata(pcc)) {1042 result = -EIO;1043 pr_err("Couldn't retrieve BIOS data\n");1044 goto out_input;1045 }1046 1047 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {1048 /* initialize backlight */1049 memset(&props, 0, sizeof(struct backlight_properties));1050 props.type = BACKLIGHT_PLATFORM;1051 props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT];1052 1053 pcc->backlight = backlight_device_register("panasonic", NULL, pcc,1054 &pcc_backlight_ops, &props);1055 if (IS_ERR(pcc->backlight)) {1056 result = PTR_ERR(pcc->backlight);1057 goto out_input;1058 }1059 1060 /* read the initial brightness setting from the hardware */1061 pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];1062 }1063 1064 /* Reset initial sticky key mode since the hardware register state is not consistent */1065 acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, 0);1066 pcc->sticky_key = 0;1067 1068 pcc->ac_brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];1069 pcc->dc_brightness = pcc->sinf[SINF_DC_CUR_BRIGHT];1070 if (pcc->num_sifr > SINF_MUTE)1071 pcc->mute = pcc->sinf[SINF_MUTE];1072 if (pcc->num_sifr > SINF_ECO_MODE)1073 pcc->eco_mode = pcc->sinf[SINF_ECO_MODE];1074 if (pcc->num_sifr > SINF_CUR_BRIGHT)1075 pcc->current_brightness = pcc->sinf[SINF_CUR_BRIGHT];1076 1077 /* add sysfs attributes */1078 result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);1079 if (result)1080 goto out_backlight;1081 1082 /* optical drive initialization */1083 if (ACPI_SUCCESS(check_optd_present())) {1084 pcc->platform = platform_device_register_simple("panasonic",1085 PLATFORM_DEVID_NONE, NULL, 0);1086 if (IS_ERR(pcc->platform)) {1087 result = PTR_ERR(pcc->platform);1088 goto out_backlight;1089 }1090 result = device_create_file(&pcc->platform->dev,1091 &dev_attr_cdpower);1092 pcc_register_optd_notifier(pcc, "\\_SB.PCI0.EHCI.ERHB.OPTD");1093 if (result)1094 goto out_platform;1095 } else {1096 pcc->platform = NULL;1097 }1098 1099 i8042_install_filter(panasonic_i8042_filter);1100 return 0;1101 1102out_platform:1103 platform_device_unregister(pcc->platform);1104out_backlight:1105 backlight_device_unregister(pcc->backlight);1106out_input:1107 input_unregister_device(pcc->input_dev);1108out_sinf:1109 kfree(pcc->sinf);1110out_hotkey:1111 kfree(pcc);1112 1113 return result;1114}1115 1116static void acpi_pcc_hotkey_remove(struct acpi_device *device)1117{1118 struct pcc_acpi *pcc = acpi_driver_data(device);1119 1120 if (!device || !pcc)1121 return;1122 1123 i8042_remove_filter(panasonic_i8042_filter);1124 1125 if (pcc->platform) {1126 device_remove_file(&pcc->platform->dev, &dev_attr_cdpower);1127 platform_device_unregister(pcc->platform);1128 }1129 pcc_unregister_optd_notifier(pcc, "\\_SB.PCI0.EHCI.ERHB.OPTD");1130 1131 sysfs_remove_group(&device->dev.kobj, &pcc_attr_group);1132 1133 backlight_device_unregister(pcc->backlight);1134 1135 input_unregister_device(pcc->input_dev);1136 1137 kfree(pcc->sinf);1138 kfree(pcc);1139}1140 1141module_acpi_driver(acpi_pcc_driver);1142