162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Panasonic HotKey and LCD brightness control driver 462306a36Sopenharmony_ci * (C) 2004 Hiroshi Miura <miura@da-cha.org> 562306a36Sopenharmony_ci * (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/ 662306a36Sopenharmony_ci * (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp> 762306a36Sopenharmony_ci * (C) 2004 David Bronaugh <dbronaugh> 862306a36Sopenharmony_ci * (C) 2006-2008 Harald Welte <laforge@gnumonks.org> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci *--------------------------------------------------------------------------- 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * ChangeLog: 1562306a36Sopenharmony_ci * Aug.18, 2020 Kenneth Chan <kenneth.t.chan@gmail.com> 1662306a36Sopenharmony_ci * -v0.98 add platform devices for firmware brightness registers 1762306a36Sopenharmony_ci * add support for battery charging threshold (eco mode) 1862306a36Sopenharmony_ci * resolve hotkey double trigger 1962306a36Sopenharmony_ci * add write support to mute 2062306a36Sopenharmony_ci * fix sticky_key init bug 2162306a36Sopenharmony_ci * fix naming of platform files for consistency with other 2262306a36Sopenharmony_ci * modules 2362306a36Sopenharmony_ci * split MODULE_AUTHOR() by one author per macro call 2462306a36Sopenharmony_ci * replace ACPI prints with pr_*() macros 2562306a36Sopenharmony_ci * -v0.97 add support for cdpower hardware switch 2662306a36Sopenharmony_ci * -v0.96 merge Lucina's enhancement 2762306a36Sopenharmony_ci * Jan.13, 2009 Martin Lucina <mato@kotelna.sk> 2862306a36Sopenharmony_ci * - add support for optical driver power in 2962306a36Sopenharmony_ci * Y and W series 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Sep.23, 2008 Harald Welte <laforge@gnumonks.org> 3262306a36Sopenharmony_ci * -v0.95 rename driver from drivers/acpi/pcc_acpi.c to 3362306a36Sopenharmony_ci * drivers/misc/panasonic-laptop.c 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Jul.04, 2008 Harald Welte <laforge@gnumonks.org> 3662306a36Sopenharmony_ci * -v0.94 replace /proc interface with device attributes 3762306a36Sopenharmony_ci * support {set,get}keycode on th input device 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * Jun.27, 2008 Harald Welte <laforge@gnumonks.org> 4062306a36Sopenharmony_ci * -v0.92 merge with 2.6.26-rc6 input API changes 4162306a36Sopenharmony_ci * remove broken <= 2.6.15 kernel support 4262306a36Sopenharmony_ci * resolve all compiler warnings 4362306a36Sopenharmony_ci * various coding style fixes (checkpatch.pl) 4462306a36Sopenharmony_ci * add support for backlight api 4562306a36Sopenharmony_ci * major code restructuring 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * Dac.28, 2007 Harald Welte <laforge@gnumonks.org> 4862306a36Sopenharmony_ci * -v0.91 merge with 2.6.24-rc6 ACPI changes 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * Nov.04, 2006 Hiroshi Miura <miura@da-cha.org> 5162306a36Sopenharmony_ci * -v0.9 remove warning about section reference. 5262306a36Sopenharmony_ci * remove acpi_os_free 5362306a36Sopenharmony_ci * add /proc/acpi/pcc/brightness interface for HAL access 5462306a36Sopenharmony_ci * merge dbronaugh's enhancement 5562306a36Sopenharmony_ci * Aug.17, 2004 David Bronaugh (dbronaugh) 5662306a36Sopenharmony_ci * - Added screen brightness setting interface 5762306a36Sopenharmony_ci * Thanks to FreeBSD crew (acpi_panasonic.c) 5862306a36Sopenharmony_ci * for the ideas I needed to accomplish it 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * May.29, 2006 Hiroshi Miura <miura@da-cha.org> 6162306a36Sopenharmony_ci * -v0.8.4 follow to change keyinput structure 6262306a36Sopenharmony_ci * thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>, 6362306a36Sopenharmony_ci * Jacob Bower <jacob.bower@ic.ac.uk> and 6462306a36Sopenharmony_ci * Hiroshi Yokota for providing solutions. 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * Oct.02, 2004 Hiroshi Miura <miura@da-cha.org> 6762306a36Sopenharmony_ci * -v0.8.2 merge code of YOKOTA Hiroshi 6862306a36Sopenharmony_ci * <yokota@netlab.is.tsukuba.ac.jp>. 6962306a36Sopenharmony_ci * Add sticky key mode interface. 7062306a36Sopenharmony_ci * Refactoring acpi_pcc_generate_keyinput(). 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * Sep.15, 2004 Hiroshi Miura <miura@da-cha.org> 7362306a36Sopenharmony_ci * -v0.8 Generate key input event on input subsystem. 7462306a36Sopenharmony_ci * This is based on yet another driver written by 7562306a36Sopenharmony_ci * Ryuta Nakanishi. 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * Sep.10, 2004 Hiroshi Miura <miura@da-cha.org> 7862306a36Sopenharmony_ci * -v0.7 Change proc interface functions using seq_file 7962306a36Sopenharmony_ci * facility as same as other ACPI drivers. 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci * Aug.28, 2004 Hiroshi Miura <miura@da-cha.org> 8262306a36Sopenharmony_ci * -v0.6.4 Fix a silly error with status checking 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * Aug.25, 2004 Hiroshi Miura <miura@da-cha.org> 8562306a36Sopenharmony_ci * -v0.6.3 replace read_acpi_int by standard function 8662306a36Sopenharmony_ci * acpi_evaluate_integer 8762306a36Sopenharmony_ci * some clean up and make smart copyright notice. 8862306a36Sopenharmony_ci * fix return value of pcc_acpi_get_key() 8962306a36Sopenharmony_ci * fix checking return value of acpi_bus_register_driver() 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * Aug.22, 2004 David Bronaugh <dbronaugh@linuxboxen.org> 9262306a36Sopenharmony_ci * -v0.6.2 Add check on ACPI data (num_sifr) 9362306a36Sopenharmony_ci * Coding style cleanups, better error messages/handling 9462306a36Sopenharmony_ci * Fixed an off-by-one error in memory allocation 9562306a36Sopenharmony_ci * 9662306a36Sopenharmony_ci * Aug.21, 2004 David Bronaugh <dbronaugh@linuxboxen.org> 9762306a36Sopenharmony_ci * -v0.6.1 Fix a silly error with status checking 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * Aug.20, 2004 David Bronaugh <dbronaugh@linuxboxen.org> 10062306a36Sopenharmony_ci * - v0.6 Correct brightness controls to reflect reality 10162306a36Sopenharmony_ci * based on information gleaned by Hiroshi Miura 10262306a36Sopenharmony_ci * and discussions with Hiroshi Miura 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * Aug.10, 2004 Hiroshi Miura <miura@da-cha.org> 10562306a36Sopenharmony_ci * - v0.5 support LCD brightness control 10662306a36Sopenharmony_ci * based on the disclosed information by MEI. 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * Jul.25, 2004 Hiroshi Miura <miura@da-cha.org> 10962306a36Sopenharmony_ci * - v0.4 first post version 11062306a36Sopenharmony_ci * add function to retrive SIFR 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * Jul.24, 2004 Hiroshi Miura <miura@da-cha.org> 11362306a36Sopenharmony_ci * - v0.3 get proper status of hotkey 11462306a36Sopenharmony_ci * 11562306a36Sopenharmony_ci * Jul.22, 2004 Hiroshi Miura <miura@da-cha.org> 11662306a36Sopenharmony_ci * - v0.2 add HotKey handler 11762306a36Sopenharmony_ci * 11862306a36Sopenharmony_ci * Jul.17, 2004 Hiroshi Miura <miura@da-cha.org> 11962306a36Sopenharmony_ci * - v0.1 start from toshiba_acpi driver written by John Belmonte 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#include <linux/acpi.h> 12362306a36Sopenharmony_ci#include <linux/backlight.h> 12462306a36Sopenharmony_ci#include <linux/ctype.h> 12562306a36Sopenharmony_ci#include <linux/i8042.h> 12662306a36Sopenharmony_ci#include <linux/init.h> 12762306a36Sopenharmony_ci#include <linux/input.h> 12862306a36Sopenharmony_ci#include <linux/input/sparse-keymap.h> 12962306a36Sopenharmony_ci#include <linux/kernel.h> 13062306a36Sopenharmony_ci#include <linux/module.h> 13162306a36Sopenharmony_ci#include <linux/platform_device.h> 13262306a36Sopenharmony_ci#include <linux/seq_file.h> 13362306a36Sopenharmony_ci#include <linux/serio.h> 13462306a36Sopenharmony_ci#include <linux/slab.h> 13562306a36Sopenharmony_ci#include <linux/types.h> 13662306a36Sopenharmony_ci#include <linux/uaccess.h> 13762306a36Sopenharmony_ci#include <acpi/video.h> 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ciMODULE_AUTHOR("Hiroshi Miura <miura@da-cha.org>"); 14062306a36Sopenharmony_ciMODULE_AUTHOR("David Bronaugh <dbronaugh@linuxboxen.org>"); 14162306a36Sopenharmony_ciMODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); 14262306a36Sopenharmony_ciMODULE_AUTHOR("Martin Lucina <mato@kotelna.sk>"); 14362306a36Sopenharmony_ciMODULE_AUTHOR("Kenneth Chan <kenneth.t.chan@gmail.com>"); 14462306a36Sopenharmony_ciMODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops"); 14562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#define LOGPREFIX "pcc_acpi: " 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* Define ACPI PATHs */ 15062306a36Sopenharmony_ci/* Lets note hotkeys */ 15162306a36Sopenharmony_ci#define METHOD_HKEY_QUERY "HINF" 15262306a36Sopenharmony_ci#define METHOD_HKEY_SQTY "SQTY" 15362306a36Sopenharmony_ci#define METHOD_HKEY_SINF "SINF" 15462306a36Sopenharmony_ci#define METHOD_HKEY_SSET "SSET" 15562306a36Sopenharmony_ci#define METHOD_ECWR "\\_SB.ECWR" 15662306a36Sopenharmony_ci#define HKEY_NOTIFY 0x80 15762306a36Sopenharmony_ci#define ECO_MODE_OFF 0x00 15862306a36Sopenharmony_ci#define ECO_MODE_ON 0x80 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci#define ACPI_PCC_DRIVER_NAME "Panasonic Laptop Support" 16162306a36Sopenharmony_ci#define ACPI_PCC_DEVICE_NAME "Hotkey" 16262306a36Sopenharmony_ci#define ACPI_PCC_CLASS "pcc" 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#define ACPI_PCC_INPUT_PHYS "panasonic/hkey0" 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent 16762306a36Sopenharmony_ci ECO_MODEs: 0x03 = off, 0x83 = on 16862306a36Sopenharmony_ci*/ 16962306a36Sopenharmony_cienum SINF_BITS { SINF_NUM_BATTERIES = 0, 17062306a36Sopenharmony_ci SINF_LCD_TYPE, 17162306a36Sopenharmony_ci SINF_AC_MAX_BRIGHT, 17262306a36Sopenharmony_ci SINF_AC_MIN_BRIGHT, 17362306a36Sopenharmony_ci SINF_AC_CUR_BRIGHT, 17462306a36Sopenharmony_ci SINF_DC_MAX_BRIGHT, 17562306a36Sopenharmony_ci SINF_DC_MIN_BRIGHT, 17662306a36Sopenharmony_ci SINF_DC_CUR_BRIGHT, 17762306a36Sopenharmony_ci SINF_MUTE, 17862306a36Sopenharmony_ci SINF_RESERVED, 17962306a36Sopenharmony_ci SINF_ECO_MODE = 0x0A, 18062306a36Sopenharmony_ci SINF_CUR_BRIGHT = 0x0D, 18162306a36Sopenharmony_ci SINF_STICKY_KEY = 0x80, 18262306a36Sopenharmony_ci }; 18362306a36Sopenharmony_ci/* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */ 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int acpi_pcc_hotkey_add(struct acpi_device *device); 18662306a36Sopenharmony_cistatic void acpi_pcc_hotkey_remove(struct acpi_device *device); 18762306a36Sopenharmony_cistatic void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic const struct acpi_device_id pcc_device_ids[] = { 19062306a36Sopenharmony_ci { "MAT0012", 0}, 19162306a36Sopenharmony_ci { "MAT0013", 0}, 19262306a36Sopenharmony_ci { "MAT0018", 0}, 19362306a36Sopenharmony_ci { "MAT0019", 0}, 19462306a36Sopenharmony_ci { "", 0}, 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, pcc_device_ids); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 19962306a36Sopenharmony_cistatic int acpi_pcc_hotkey_resume(struct device *dev); 20062306a36Sopenharmony_ci#endif 20162306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(acpi_pcc_hotkey_pm, NULL, acpi_pcc_hotkey_resume); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic struct acpi_driver acpi_pcc_driver = { 20462306a36Sopenharmony_ci .name = ACPI_PCC_DRIVER_NAME, 20562306a36Sopenharmony_ci .class = ACPI_PCC_CLASS, 20662306a36Sopenharmony_ci .ids = pcc_device_ids, 20762306a36Sopenharmony_ci .ops = { 20862306a36Sopenharmony_ci .add = acpi_pcc_hotkey_add, 20962306a36Sopenharmony_ci .remove = acpi_pcc_hotkey_remove, 21062306a36Sopenharmony_ci .notify = acpi_pcc_hotkey_notify, 21162306a36Sopenharmony_ci }, 21262306a36Sopenharmony_ci .drv.pm = &acpi_pcc_hotkey_pm, 21362306a36Sopenharmony_ci}; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic const struct key_entry panasonic_keymap[] = { 21662306a36Sopenharmony_ci { KE_KEY, 0, { KEY_RESERVED } }, 21762306a36Sopenharmony_ci { KE_KEY, 1, { KEY_BRIGHTNESSDOWN } }, 21862306a36Sopenharmony_ci { KE_KEY, 2, { KEY_BRIGHTNESSUP } }, 21962306a36Sopenharmony_ci { KE_KEY, 3, { KEY_DISPLAYTOGGLE } }, 22062306a36Sopenharmony_ci { KE_KEY, 4, { KEY_MUTE } }, 22162306a36Sopenharmony_ci { KE_KEY, 5, { KEY_VOLUMEDOWN } }, 22262306a36Sopenharmony_ci { KE_KEY, 6, { KEY_VOLUMEUP } }, 22362306a36Sopenharmony_ci { KE_KEY, 7, { KEY_SLEEP } }, 22462306a36Sopenharmony_ci { KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */ 22562306a36Sopenharmony_ci { KE_KEY, 9, { KEY_BATTERY } }, 22662306a36Sopenharmony_ci { KE_KEY, 10, { KEY_SUSPEND } }, 22762306a36Sopenharmony_ci { KE_END, 0 } 22862306a36Sopenharmony_ci}; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistruct pcc_acpi { 23162306a36Sopenharmony_ci acpi_handle handle; 23262306a36Sopenharmony_ci unsigned long num_sifr; 23362306a36Sopenharmony_ci int sticky_key; 23462306a36Sopenharmony_ci int eco_mode; 23562306a36Sopenharmony_ci int mute; 23662306a36Sopenharmony_ci int ac_brightness; 23762306a36Sopenharmony_ci int dc_brightness; 23862306a36Sopenharmony_ci int current_brightness; 23962306a36Sopenharmony_ci u32 *sinf; 24062306a36Sopenharmony_ci struct acpi_device *device; 24162306a36Sopenharmony_ci struct input_dev *input_dev; 24262306a36Sopenharmony_ci struct backlight_device *backlight; 24362306a36Sopenharmony_ci struct platform_device *platform; 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci/* 24762306a36Sopenharmony_ci * On some Panasonic models the volume up / down / mute keys send duplicate 24862306a36Sopenharmony_ci * keypress events over the PS/2 kbd interface, filter these out. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_cistatic bool panasonic_i8042_filter(unsigned char data, unsigned char str, 25162306a36Sopenharmony_ci struct serio *port) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci static bool extended; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (str & I8042_STR_AUXDATA) 25662306a36Sopenharmony_ci return false; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (data == 0xe0) { 25962306a36Sopenharmony_ci extended = true; 26062306a36Sopenharmony_ci return true; 26162306a36Sopenharmony_ci } else if (extended) { 26262306a36Sopenharmony_ci extended = false; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci switch (data & 0x7f) { 26562306a36Sopenharmony_ci case 0x20: /* e0 20 / e0 a0, Volume Mute press / release */ 26662306a36Sopenharmony_ci case 0x2e: /* e0 2e / e0 ae, Volume Down press / release */ 26762306a36Sopenharmony_ci case 0x30: /* e0 30 / e0 b0, Volume Up press / release */ 26862306a36Sopenharmony_ci return true; 26962306a36Sopenharmony_ci default: 27062306a36Sopenharmony_ci /* 27162306a36Sopenharmony_ci * Report the previously filtered e0 before continuing 27262306a36Sopenharmony_ci * with the next non-filtered byte. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_ci serio_interrupt(port, 0xe0, 0); 27562306a36Sopenharmony_ci return false; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return false; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/* method access functions */ 28362306a36Sopenharmony_cistatic int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci union acpi_object in_objs[] = { 28662306a36Sopenharmony_ci { .integer.type = ACPI_TYPE_INTEGER, 28762306a36Sopenharmony_ci .integer.value = func, }, 28862306a36Sopenharmony_ci { .integer.type = ACPI_TYPE_INTEGER, 28962306a36Sopenharmony_ci .integer.value = val, }, 29062306a36Sopenharmony_ci }; 29162306a36Sopenharmony_ci struct acpi_object_list params = { 29262306a36Sopenharmony_ci .count = ARRAY_SIZE(in_objs), 29362306a36Sopenharmony_ci .pointer = in_objs, 29462306a36Sopenharmony_ci }; 29562306a36Sopenharmony_ci acpi_status status = AE_OK; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET, 29862306a36Sopenharmony_ci ¶ms, NULL); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return (status == AE_OK) ? 0 : -EIO; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic inline int acpi_pcc_get_sqty(struct acpi_device *device) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci unsigned long long s; 30662306a36Sopenharmony_ci acpi_status status; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY, 30962306a36Sopenharmony_ci NULL, &s); 31062306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) 31162306a36Sopenharmony_ci return s; 31262306a36Sopenharmony_ci else { 31362306a36Sopenharmony_ci pr_err("evaluation error HKEY.SQTY\n"); 31462306a36Sopenharmony_ci return -EINVAL; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci acpi_status status; 32162306a36Sopenharmony_ci struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; 32262306a36Sopenharmony_ci union acpi_object *hkey = NULL; 32362306a36Sopenharmony_ci int i; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, NULL, 32662306a36Sopenharmony_ci &buffer); 32762306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 32862306a36Sopenharmony_ci pr_err("evaluation error HKEY.SINF\n"); 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci hkey = buffer.pointer; 33362306a36Sopenharmony_ci if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) { 33462306a36Sopenharmony_ci pr_err("Invalid HKEY.SINF\n"); 33562306a36Sopenharmony_ci status = AE_ERROR; 33662306a36Sopenharmony_ci goto end; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (pcc->num_sifr < hkey->package.count) { 34062306a36Sopenharmony_ci pr_err("SQTY reports bad SINF length\n"); 34162306a36Sopenharmony_ci status = AE_ERROR; 34262306a36Sopenharmony_ci goto end; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci for (i = 0; i < hkey->package.count; i++) { 34662306a36Sopenharmony_ci union acpi_object *element = &(hkey->package.elements[i]); 34762306a36Sopenharmony_ci if (likely(element->type == ACPI_TYPE_INTEGER)) { 34862306a36Sopenharmony_ci pcc->sinf[i] = element->integer.value; 34962306a36Sopenharmony_ci } else 35062306a36Sopenharmony_ci pr_err("Invalid HKEY.SINF data\n"); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci pcc->sinf[hkey->package.count] = -1; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ciend: 35562306a36Sopenharmony_ci kfree(buffer.pointer); 35662306a36Sopenharmony_ci return status == AE_OK; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/* backlight API interface functions */ 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci/* This driver currently treats AC and DC brightness identical, 36262306a36Sopenharmony_ci * since we don't need to invent an interface to the core ACPI 36362306a36Sopenharmony_ci * logic to receive events in case a power supply is plugged in 36462306a36Sopenharmony_ci * or removed */ 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int bl_get(struct backlight_device *bd) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct pcc_acpi *pcc = bl_get_data(bd); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 37162306a36Sopenharmony_ci return -EIO; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return pcc->sinf[SINF_AC_CUR_BRIGHT]; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int bl_set_status(struct backlight_device *bd) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct pcc_acpi *pcc = bl_get_data(bd); 37962306a36Sopenharmony_ci int bright = bd->props.brightness; 38062306a36Sopenharmony_ci int rc; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 38362306a36Sopenharmony_ci return -EIO; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT]) 38662306a36Sopenharmony_ci bright = pcc->sinf[SINF_AC_MIN_BRIGHT]; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT]) 38962306a36Sopenharmony_ci bright = pcc->sinf[SINF_DC_MIN_BRIGHT]; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] || 39262306a36Sopenharmony_ci bright > pcc->sinf[SINF_AC_MAX_BRIGHT]) 39362306a36Sopenharmony_ci return -EINVAL; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright); 39662306a36Sopenharmony_ci if (rc < 0) 39762306a36Sopenharmony_ci return rc; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic const struct backlight_ops pcc_backlight_ops = { 40362306a36Sopenharmony_ci .get_brightness = bl_get, 40462306a36Sopenharmony_ci .update_status = bl_set_status, 40562306a36Sopenharmony_ci}; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci/* returns ACPI_SUCCESS if methods to control optical drive are present */ 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic acpi_status check_optd_present(void) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci acpi_status status = AE_OK; 41362306a36Sopenharmony_ci acpi_handle handle; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci status = acpi_get_handle(NULL, "\\_SB.STAT", &handle); 41662306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 41762306a36Sopenharmony_ci goto out; 41862306a36Sopenharmony_ci status = acpi_get_handle(NULL, "\\_SB.FBAY", &handle); 41962306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 42062306a36Sopenharmony_ci goto out; 42162306a36Sopenharmony_ci status = acpi_get_handle(NULL, "\\_SB.CDDI", &handle); 42262306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 42362306a36Sopenharmony_ci goto out; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ciout: 42662306a36Sopenharmony_ci return status; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci/* get optical driver power state */ 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic int get_optd_power_state(void) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci acpi_status status; 43462306a36Sopenharmony_ci unsigned long long state; 43562306a36Sopenharmony_ci int result; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci status = acpi_evaluate_integer(NULL, "\\_SB.STAT", NULL, &state); 43862306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 43962306a36Sopenharmony_ci pr_err("evaluation error _SB.STAT\n"); 44062306a36Sopenharmony_ci result = -EIO; 44162306a36Sopenharmony_ci goto out; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci switch (state) { 44462306a36Sopenharmony_ci case 0: /* power off */ 44562306a36Sopenharmony_ci result = 0; 44662306a36Sopenharmony_ci break; 44762306a36Sopenharmony_ci case 0x0f: /* power on */ 44862306a36Sopenharmony_ci result = 1; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci default: 45162306a36Sopenharmony_ci result = -EIO; 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ciout: 45662306a36Sopenharmony_ci return result; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci/* set optical drive power state */ 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int set_optd_power_state(int new_state) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci int result; 46462306a36Sopenharmony_ci acpi_status status; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci result = get_optd_power_state(); 46762306a36Sopenharmony_ci if (result < 0) 46862306a36Sopenharmony_ci goto out; 46962306a36Sopenharmony_ci if (new_state == result) 47062306a36Sopenharmony_ci goto out; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci switch (new_state) { 47362306a36Sopenharmony_ci case 0: /* power off */ 47462306a36Sopenharmony_ci /* Call CDDR instead, since they both call the same method 47562306a36Sopenharmony_ci * while CDDI takes 1 arg and we are not quite sure what it is. 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci status = acpi_evaluate_object(NULL, "\\_SB.CDDR", NULL, NULL); 47862306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 47962306a36Sopenharmony_ci pr_err("evaluation error _SB.CDDR\n"); 48062306a36Sopenharmony_ci result = -EIO; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci case 1: /* power on */ 48462306a36Sopenharmony_ci status = acpi_evaluate_object(NULL, "\\_SB.FBAY", NULL, NULL); 48562306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 48662306a36Sopenharmony_ci pr_err("evaluation error _SB.FBAY\n"); 48762306a36Sopenharmony_ci result = -EIO; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci default: 49162306a36Sopenharmony_ci result = -EINVAL; 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ciout: 49662306a36Sopenharmony_ci return result; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci/* sysfs user interface functions */ 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic ssize_t numbatt_show(struct device *dev, struct device_attribute *attr, 50362306a36Sopenharmony_ci char *buf) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 50662306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 50962306a36Sopenharmony_ci return -EIO; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic ssize_t lcdtype_show(struct device *dev, struct device_attribute *attr, 51562306a36Sopenharmony_ci char *buf) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 51862306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 52162306a36Sopenharmony_ci return -EIO; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_LCD_TYPE]); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic ssize_t mute_show(struct device *dev, struct device_attribute *attr, 52762306a36Sopenharmony_ci char *buf) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 53062306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 53362306a36Sopenharmony_ci return -EIO; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_MUTE]); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic ssize_t mute_store(struct device *dev, struct device_attribute *attr, 53962306a36Sopenharmony_ci const char *buf, size_t count) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 54262306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 54362306a36Sopenharmony_ci int err, val; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci err = kstrtoint(buf, 0, &val); 54662306a36Sopenharmony_ci if (err) 54762306a36Sopenharmony_ci return err; 54862306a36Sopenharmony_ci if (val == 0 || val == 1) { 54962306a36Sopenharmony_ci acpi_pcc_write_sset(pcc, SINF_MUTE, val); 55062306a36Sopenharmony_ci pcc->mute = val; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return count; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic ssize_t sticky_key_show(struct device *dev, struct device_attribute *attr, 55762306a36Sopenharmony_ci char *buf) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 56062306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 56362306a36Sopenharmony_ci return -EIO; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pcc->sticky_key); 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic ssize_t sticky_key_store(struct device *dev, struct device_attribute *attr, 56962306a36Sopenharmony_ci const char *buf, size_t count) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 57262306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 57362306a36Sopenharmony_ci int err, val; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci err = kstrtoint(buf, 0, &val); 57662306a36Sopenharmony_ci if (err) 57762306a36Sopenharmony_ci return err; 57862306a36Sopenharmony_ci if (val == 0 || val == 1) { 57962306a36Sopenharmony_ci acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val); 58062306a36Sopenharmony_ci pcc->sticky_key = val; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return count; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic ssize_t eco_mode_show(struct device *dev, struct device_attribute *attr, 58762306a36Sopenharmony_ci char *buf) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 59062306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 59162306a36Sopenharmony_ci int result; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 59462306a36Sopenharmony_ci return -EIO; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci switch (pcc->sinf[SINF_ECO_MODE]) { 59762306a36Sopenharmony_ci case (ECO_MODE_OFF + 3): 59862306a36Sopenharmony_ci result = 0; 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci case (ECO_MODE_ON + 3): 60162306a36Sopenharmony_ci result = 1; 60262306a36Sopenharmony_ci break; 60362306a36Sopenharmony_ci default: 60462306a36Sopenharmony_ci result = -EIO; 60562306a36Sopenharmony_ci break; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", result); 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic ssize_t eco_mode_store(struct device *dev, struct device_attribute *attr, 61162306a36Sopenharmony_ci const char *buf, size_t count) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 61462306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 61562306a36Sopenharmony_ci int err, state; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci union acpi_object param[2]; 61862306a36Sopenharmony_ci struct acpi_object_list input; 61962306a36Sopenharmony_ci acpi_status status; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci param[0].type = ACPI_TYPE_INTEGER; 62262306a36Sopenharmony_ci param[0].integer.value = 0x15; 62362306a36Sopenharmony_ci param[1].type = ACPI_TYPE_INTEGER; 62462306a36Sopenharmony_ci input.count = 2; 62562306a36Sopenharmony_ci input.pointer = param; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci err = kstrtoint(buf, 0, &state); 62862306a36Sopenharmony_ci if (err) 62962306a36Sopenharmony_ci return err; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci switch (state) { 63262306a36Sopenharmony_ci case 0: 63362306a36Sopenharmony_ci param[1].integer.value = ECO_MODE_OFF; 63462306a36Sopenharmony_ci pcc->sinf[SINF_ECO_MODE] = 0; 63562306a36Sopenharmony_ci pcc->eco_mode = 0; 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci case 1: 63862306a36Sopenharmony_ci param[1].integer.value = ECO_MODE_ON; 63962306a36Sopenharmony_ci pcc->sinf[SINF_ECO_MODE] = 1; 64062306a36Sopenharmony_ci pcc->eco_mode = 1; 64162306a36Sopenharmony_ci break; 64262306a36Sopenharmony_ci default: 64362306a36Sopenharmony_ci /* nothing to do */ 64462306a36Sopenharmony_ci return count; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci status = acpi_evaluate_object(NULL, METHOD_ECWR, 64862306a36Sopenharmony_ci &input, NULL); 64962306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 65062306a36Sopenharmony_ci pr_err("%s evaluation failed\n", METHOD_ECWR); 65162306a36Sopenharmony_ci return -EINVAL; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci return count; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic ssize_t ac_brightness_show(struct device *dev, struct device_attribute *attr, 65862306a36Sopenharmony_ci char *buf) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 66162306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 66462306a36Sopenharmony_ci return -EIO; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_AC_CUR_BRIGHT]); 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic ssize_t ac_brightness_store(struct device *dev, struct device_attribute *attr, 67062306a36Sopenharmony_ci const char *buf, size_t count) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 67362306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 67462306a36Sopenharmony_ci int err, val; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci err = kstrtoint(buf, 0, &val); 67762306a36Sopenharmony_ci if (err) 67862306a36Sopenharmony_ci return err; 67962306a36Sopenharmony_ci if (val >= 0 && val <= 255) { 68062306a36Sopenharmony_ci acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, val); 68162306a36Sopenharmony_ci pcc->ac_brightness = val; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci return count; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic ssize_t dc_brightness_show(struct device *dev, struct device_attribute *attr, 68862306a36Sopenharmony_ci char *buf) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 69162306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 69462306a36Sopenharmony_ci return -EIO; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_DC_CUR_BRIGHT]); 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic ssize_t dc_brightness_store(struct device *dev, struct device_attribute *attr, 70062306a36Sopenharmony_ci const char *buf, size_t count) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 70362306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 70462306a36Sopenharmony_ci int err, val; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci err = kstrtoint(buf, 0, &val); 70762306a36Sopenharmony_ci if (err) 70862306a36Sopenharmony_ci return err; 70962306a36Sopenharmony_ci if (val >= 0 && val <= 255) { 71062306a36Sopenharmony_ci acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, val); 71162306a36Sopenharmony_ci pcc->dc_brightness = val; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return count; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic ssize_t current_brightness_show(struct device *dev, struct device_attribute *attr, 71862306a36Sopenharmony_ci char *buf) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 72162306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 72462306a36Sopenharmony_ci return -EIO; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_CUR_BRIGHT]); 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cistatic ssize_t current_brightness_store(struct device *dev, struct device_attribute *attr, 73062306a36Sopenharmony_ci const char *buf, size_t count) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 73362306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 73462306a36Sopenharmony_ci int err, val; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci err = kstrtoint(buf, 0, &val); 73762306a36Sopenharmony_ci if (err) 73862306a36Sopenharmony_ci return err; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (val >= 0 && val <= 255) { 74162306a36Sopenharmony_ci err = acpi_pcc_write_sset(pcc, SINF_CUR_BRIGHT, val); 74262306a36Sopenharmony_ci pcc->current_brightness = val; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci return count; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic ssize_t cdpower_show(struct device *dev, struct device_attribute *attr, 74962306a36Sopenharmony_ci char *buf) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", get_optd_power_state()); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic ssize_t cdpower_store(struct device *dev, struct device_attribute *attr, 75562306a36Sopenharmony_ci const char *buf, size_t count) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci int err, val; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci err = kstrtoint(buf, 10, &val); 76062306a36Sopenharmony_ci if (err) 76162306a36Sopenharmony_ci return err; 76262306a36Sopenharmony_ci set_optd_power_state(val); 76362306a36Sopenharmony_ci return count; 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(numbatt); 76762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(lcdtype); 76862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(mute); 76962306a36Sopenharmony_cistatic DEVICE_ATTR_RW(sticky_key); 77062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(eco_mode); 77162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(ac_brightness); 77262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(dc_brightness); 77362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(current_brightness); 77462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(cdpower); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic struct attribute *pcc_sysfs_entries[] = { 77762306a36Sopenharmony_ci &dev_attr_numbatt.attr, 77862306a36Sopenharmony_ci &dev_attr_lcdtype.attr, 77962306a36Sopenharmony_ci &dev_attr_mute.attr, 78062306a36Sopenharmony_ci &dev_attr_sticky_key.attr, 78162306a36Sopenharmony_ci &dev_attr_eco_mode.attr, 78262306a36Sopenharmony_ci &dev_attr_ac_brightness.attr, 78362306a36Sopenharmony_ci &dev_attr_dc_brightness.attr, 78462306a36Sopenharmony_ci &dev_attr_current_brightness.attr, 78562306a36Sopenharmony_ci &dev_attr_cdpower.attr, 78662306a36Sopenharmony_ci NULL, 78762306a36Sopenharmony_ci}; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic const struct attribute_group pcc_attr_group = { 79062306a36Sopenharmony_ci .name = NULL, /* put in device directory */ 79162306a36Sopenharmony_ci .attrs = pcc_sysfs_entries, 79262306a36Sopenharmony_ci}; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci/* hotkey input device driver */ 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic int sleep_keydown_seen; 79862306a36Sopenharmony_cistatic void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci struct input_dev *hotk_input_dev = pcc->input_dev; 80162306a36Sopenharmony_ci int rc; 80262306a36Sopenharmony_ci unsigned long long result; 80362306a36Sopenharmony_ci unsigned int key; 80462306a36Sopenharmony_ci unsigned int updown; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY, 80762306a36Sopenharmony_ci NULL, &result); 80862306a36Sopenharmony_ci if (ACPI_FAILURE(rc)) { 80962306a36Sopenharmony_ci pr_err("error getting hotkey status\n"); 81062306a36Sopenharmony_ci return; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci key = result & 0xf; 81462306a36Sopenharmony_ci updown = result & 0x80; /* 0x80 == key down; 0x00 = key up */ 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* hack: some firmware sends no key down for sleep / hibernate */ 81762306a36Sopenharmony_ci if (key == 7 || key == 10) { 81862306a36Sopenharmony_ci if (updown) 81962306a36Sopenharmony_ci sleep_keydown_seen = 1; 82062306a36Sopenharmony_ci if (!sleep_keydown_seen) 82162306a36Sopenharmony_ci sparse_keymap_report_event(hotk_input_dev, 82262306a36Sopenharmony_ci key, 0x80, false); 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* 82662306a36Sopenharmony_ci * Don't report brightness key-presses if they are also reported 82762306a36Sopenharmony_ci * by the ACPI video bus. 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_ci if ((key == 1 || key == 2) && acpi_video_handles_brightness_key_presses()) 83062306a36Sopenharmony_ci return; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (!sparse_keymap_report_event(hotk_input_dev, key, updown, false)) 83362306a36Sopenharmony_ci pr_err("Unknown hotkey event: 0x%04llx\n", result); 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(device); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci switch (event) { 84162306a36Sopenharmony_ci case HKEY_NOTIFY: 84262306a36Sopenharmony_ci acpi_pcc_generate_keyinput(pcc); 84362306a36Sopenharmony_ci break; 84462306a36Sopenharmony_ci default: 84562306a36Sopenharmony_ci /* nothing to do */ 84662306a36Sopenharmony_ci break; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic void pcc_optd_notify(acpi_handle handle, u32 event, void *data) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci if (event != ACPI_NOTIFY_EJECT_REQUEST) 85362306a36Sopenharmony_ci return; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci set_optd_power_state(0); 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic int pcc_register_optd_notifier(struct pcc_acpi *pcc, char *node) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci acpi_status status; 86162306a36Sopenharmony_ci acpi_handle handle; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci status = acpi_get_handle(NULL, node, &handle); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) { 86662306a36Sopenharmony_ci status = acpi_install_notify_handler(handle, 86762306a36Sopenharmony_ci ACPI_SYSTEM_NOTIFY, 86862306a36Sopenharmony_ci pcc_optd_notify, pcc); 86962306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 87062306a36Sopenharmony_ci pr_err("Failed to register notify on %s\n", node); 87162306a36Sopenharmony_ci } else 87262306a36Sopenharmony_ci return -ENODEV; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci return 0; 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_cistatic void pcc_unregister_optd_notifier(struct pcc_acpi *pcc, char *node) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci acpi_status status = AE_OK; 88062306a36Sopenharmony_ci acpi_handle handle; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci status = acpi_get_handle(NULL, node, &handle); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) { 88562306a36Sopenharmony_ci status = acpi_remove_notify_handler(handle, 88662306a36Sopenharmony_ci ACPI_SYSTEM_NOTIFY, 88762306a36Sopenharmony_ci pcc_optd_notify); 88862306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 88962306a36Sopenharmony_ci pr_err("Error removing optd notify handler %s\n", 89062306a36Sopenharmony_ci node); 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_cistatic int acpi_pcc_init_input(struct pcc_acpi *pcc) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci struct input_dev *input_dev; 89762306a36Sopenharmony_ci int error; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci input_dev = input_allocate_device(); 90062306a36Sopenharmony_ci if (!input_dev) 90162306a36Sopenharmony_ci return -ENOMEM; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci input_dev->name = ACPI_PCC_DRIVER_NAME; 90462306a36Sopenharmony_ci input_dev->phys = ACPI_PCC_INPUT_PHYS; 90562306a36Sopenharmony_ci input_dev->id.bustype = BUS_HOST; 90662306a36Sopenharmony_ci input_dev->id.vendor = 0x0001; 90762306a36Sopenharmony_ci input_dev->id.product = 0x0001; 90862306a36Sopenharmony_ci input_dev->id.version = 0x0100; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL); 91162306a36Sopenharmony_ci if (error) { 91262306a36Sopenharmony_ci pr_err("Unable to setup input device keymap\n"); 91362306a36Sopenharmony_ci goto err_free_dev; 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci error = input_register_device(input_dev); 91762306a36Sopenharmony_ci if (error) { 91862306a36Sopenharmony_ci pr_err("Unable to register input device\n"); 91962306a36Sopenharmony_ci goto err_free_dev; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci pcc->input_dev = input_dev; 92362306a36Sopenharmony_ci return 0; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci err_free_dev: 92662306a36Sopenharmony_ci input_free_device(input_dev); 92762306a36Sopenharmony_ci return error; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci/* kernel module interface */ 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 93362306a36Sopenharmony_cistatic int acpi_pcc_hotkey_resume(struct device *dev) 93462306a36Sopenharmony_ci{ 93562306a36Sopenharmony_ci struct pcc_acpi *pcc; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (!dev) 93862306a36Sopenharmony_ci return -EINVAL; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci pcc = acpi_driver_data(to_acpi_device(dev)); 94162306a36Sopenharmony_ci if (!pcc) 94262306a36Sopenharmony_ci return -EINVAL; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci acpi_pcc_write_sset(pcc, SINF_MUTE, pcc->mute); 94562306a36Sopenharmony_ci acpi_pcc_write_sset(pcc, SINF_ECO_MODE, pcc->eco_mode); 94662306a36Sopenharmony_ci acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_key); 94762306a36Sopenharmony_ci acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, pcc->ac_brightness); 94862306a36Sopenharmony_ci acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, pcc->dc_brightness); 94962306a36Sopenharmony_ci acpi_pcc_write_sset(pcc, SINF_CUR_BRIGHT, pcc->current_brightness); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci return 0; 95262306a36Sopenharmony_ci} 95362306a36Sopenharmony_ci#endif 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic int acpi_pcc_hotkey_add(struct acpi_device *device) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci struct backlight_properties props; 95862306a36Sopenharmony_ci struct pcc_acpi *pcc; 95962306a36Sopenharmony_ci int num_sifr, result; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (!device) 96262306a36Sopenharmony_ci return -EINVAL; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci num_sifr = acpi_pcc_get_sqty(device); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (num_sifr < 0 || num_sifr > 255) { 96762306a36Sopenharmony_ci pr_err("num_sifr out of range"); 96862306a36Sopenharmony_ci return -ENODEV; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL); 97262306a36Sopenharmony_ci if (!pcc) { 97362306a36Sopenharmony_ci pr_err("Couldn't allocate mem for pcc"); 97462306a36Sopenharmony_ci return -ENOMEM; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci pcc->sinf = kcalloc(num_sifr + 1, sizeof(u32), GFP_KERNEL); 97862306a36Sopenharmony_ci if (!pcc->sinf) { 97962306a36Sopenharmony_ci result = -ENOMEM; 98062306a36Sopenharmony_ci goto out_hotkey; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci pcc->device = device; 98462306a36Sopenharmony_ci pcc->handle = device->handle; 98562306a36Sopenharmony_ci pcc->num_sifr = num_sifr; 98662306a36Sopenharmony_ci device->driver_data = pcc; 98762306a36Sopenharmony_ci strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME); 98862306a36Sopenharmony_ci strcpy(acpi_device_class(device), ACPI_PCC_CLASS); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci result = acpi_pcc_init_input(pcc); 99162306a36Sopenharmony_ci if (result) { 99262306a36Sopenharmony_ci pr_err("Error installing keyinput handler\n"); 99362306a36Sopenharmony_ci goto out_sinf; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) { 99762306a36Sopenharmony_ci result = -EIO; 99862306a36Sopenharmony_ci pr_err("Couldn't retrieve BIOS data\n"); 99962306a36Sopenharmony_ci goto out_input; 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { 100362306a36Sopenharmony_ci /* initialize backlight */ 100462306a36Sopenharmony_ci memset(&props, 0, sizeof(struct backlight_properties)); 100562306a36Sopenharmony_ci props.type = BACKLIGHT_PLATFORM; 100662306a36Sopenharmony_ci props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT]; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci pcc->backlight = backlight_device_register("panasonic", NULL, pcc, 100962306a36Sopenharmony_ci &pcc_backlight_ops, &props); 101062306a36Sopenharmony_ci if (IS_ERR(pcc->backlight)) { 101162306a36Sopenharmony_ci result = PTR_ERR(pcc->backlight); 101262306a36Sopenharmony_ci goto out_input; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* read the initial brightness setting from the hardware */ 101662306a36Sopenharmony_ci pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT]; 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci /* Reset initial sticky key mode since the hardware register state is not consistent */ 102062306a36Sopenharmony_ci acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, 0); 102162306a36Sopenharmony_ci pcc->sticky_key = 0; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci pcc->eco_mode = pcc->sinf[SINF_ECO_MODE]; 102462306a36Sopenharmony_ci pcc->mute = pcc->sinf[SINF_MUTE]; 102562306a36Sopenharmony_ci pcc->ac_brightness = pcc->sinf[SINF_AC_CUR_BRIGHT]; 102662306a36Sopenharmony_ci pcc->dc_brightness = pcc->sinf[SINF_DC_CUR_BRIGHT]; 102762306a36Sopenharmony_ci pcc->current_brightness = pcc->sinf[SINF_CUR_BRIGHT]; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci /* add sysfs attributes */ 103062306a36Sopenharmony_ci result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group); 103162306a36Sopenharmony_ci if (result) 103262306a36Sopenharmony_ci goto out_backlight; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci /* optical drive initialization */ 103562306a36Sopenharmony_ci if (ACPI_SUCCESS(check_optd_present())) { 103662306a36Sopenharmony_ci pcc->platform = platform_device_register_simple("panasonic", 103762306a36Sopenharmony_ci PLATFORM_DEVID_NONE, NULL, 0); 103862306a36Sopenharmony_ci if (IS_ERR(pcc->platform)) { 103962306a36Sopenharmony_ci result = PTR_ERR(pcc->platform); 104062306a36Sopenharmony_ci goto out_backlight; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci result = device_create_file(&pcc->platform->dev, 104362306a36Sopenharmony_ci &dev_attr_cdpower); 104462306a36Sopenharmony_ci pcc_register_optd_notifier(pcc, "\\_SB.PCI0.EHCI.ERHB.OPTD"); 104562306a36Sopenharmony_ci if (result) 104662306a36Sopenharmony_ci goto out_platform; 104762306a36Sopenharmony_ci } else { 104862306a36Sopenharmony_ci pcc->platform = NULL; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci i8042_install_filter(panasonic_i8042_filter); 105262306a36Sopenharmony_ci return 0; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ciout_platform: 105562306a36Sopenharmony_ci platform_device_unregister(pcc->platform); 105662306a36Sopenharmony_ciout_backlight: 105762306a36Sopenharmony_ci backlight_device_unregister(pcc->backlight); 105862306a36Sopenharmony_ciout_input: 105962306a36Sopenharmony_ci input_unregister_device(pcc->input_dev); 106062306a36Sopenharmony_ciout_sinf: 106162306a36Sopenharmony_ci kfree(pcc->sinf); 106262306a36Sopenharmony_ciout_hotkey: 106362306a36Sopenharmony_ci kfree(pcc); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci return result; 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cistatic void acpi_pcc_hotkey_remove(struct acpi_device *device) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(device); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci if (!device || !pcc) 107362306a36Sopenharmony_ci return; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci i8042_remove_filter(panasonic_i8042_filter); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if (pcc->platform) { 107862306a36Sopenharmony_ci device_remove_file(&pcc->platform->dev, &dev_attr_cdpower); 107962306a36Sopenharmony_ci platform_device_unregister(pcc->platform); 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci pcc_unregister_optd_notifier(pcc, "\\_SB.PCI0.EHCI.ERHB.OPTD"); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci sysfs_remove_group(&device->dev.kobj, &pcc_attr_group); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci backlight_device_unregister(pcc->backlight); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci input_unregister_device(pcc->input_dev); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci kfree(pcc->sinf); 109062306a36Sopenharmony_ci kfree(pcc); 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cimodule_acpi_driver(acpi_pcc_driver); 1094