18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Panasonic HotKey and LCD brightness control driver 48c2ecf20Sopenharmony_ci * (C) 2004 Hiroshi Miura <miura@da-cha.org> 58c2ecf20Sopenharmony_ci * (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/ 68c2ecf20Sopenharmony_ci * (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp> 78c2ecf20Sopenharmony_ci * (C) 2004 David Bronaugh <dbronaugh> 88c2ecf20Sopenharmony_ci * (C) 2006-2008 Harald Welte <laforge@gnumonks.org> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci *--------------------------------------------------------------------------- 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * ChangeLog: 158c2ecf20Sopenharmony_ci * Sep.23, 2008 Harald Welte <laforge@gnumonks.org> 168c2ecf20Sopenharmony_ci * -v0.95 rename driver from drivers/acpi/pcc_acpi.c to 178c2ecf20Sopenharmony_ci * drivers/misc/panasonic-laptop.c 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Jul.04, 2008 Harald Welte <laforge@gnumonks.org> 208c2ecf20Sopenharmony_ci * -v0.94 replace /proc interface with device attributes 218c2ecf20Sopenharmony_ci * support {set,get}keycode on th input device 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Jun.27, 2008 Harald Welte <laforge@gnumonks.org> 248c2ecf20Sopenharmony_ci * -v0.92 merge with 2.6.26-rc6 input API changes 258c2ecf20Sopenharmony_ci * remove broken <= 2.6.15 kernel support 268c2ecf20Sopenharmony_ci * resolve all compiler warnings 278c2ecf20Sopenharmony_ci * various coding style fixes (checkpatch.pl) 288c2ecf20Sopenharmony_ci * add support for backlight api 298c2ecf20Sopenharmony_ci * major code restructuring 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Dac.28, 2007 Harald Welte <laforge@gnumonks.org> 328c2ecf20Sopenharmony_ci * -v0.91 merge with 2.6.24-rc6 ACPI changes 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * Nov.04, 2006 Hiroshi Miura <miura@da-cha.org> 358c2ecf20Sopenharmony_ci * -v0.9 remove warning about section reference. 368c2ecf20Sopenharmony_ci * remove acpi_os_free 378c2ecf20Sopenharmony_ci * add /proc/acpi/pcc/brightness interface for HAL access 388c2ecf20Sopenharmony_ci * merge dbronaugh's enhancement 398c2ecf20Sopenharmony_ci * Aug.17, 2004 David Bronaugh (dbronaugh) 408c2ecf20Sopenharmony_ci * - Added screen brightness setting interface 418c2ecf20Sopenharmony_ci * Thanks to FreeBSD crew (acpi_panasonic.c) 428c2ecf20Sopenharmony_ci * for the ideas I needed to accomplish it 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * May.29, 2006 Hiroshi Miura <miura@da-cha.org> 458c2ecf20Sopenharmony_ci * -v0.8.4 follow to change keyinput structure 468c2ecf20Sopenharmony_ci * thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>, 478c2ecf20Sopenharmony_ci * Jacob Bower <jacob.bower@ic.ac.uk> and 488c2ecf20Sopenharmony_ci * Hiroshi Yokota for providing solutions. 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * Oct.02, 2004 Hiroshi Miura <miura@da-cha.org> 518c2ecf20Sopenharmony_ci * -v0.8.2 merge code of YOKOTA Hiroshi 528c2ecf20Sopenharmony_ci * <yokota@netlab.is.tsukuba.ac.jp>. 538c2ecf20Sopenharmony_ci * Add sticky key mode interface. 548c2ecf20Sopenharmony_ci * Refactoring acpi_pcc_generate_keyinput(). 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * Sep.15, 2004 Hiroshi Miura <miura@da-cha.org> 578c2ecf20Sopenharmony_ci * -v0.8 Generate key input event on input subsystem. 588c2ecf20Sopenharmony_ci * This is based on yet another driver written by 598c2ecf20Sopenharmony_ci * Ryuta Nakanishi. 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * Sep.10, 2004 Hiroshi Miura <miura@da-cha.org> 628c2ecf20Sopenharmony_ci * -v0.7 Change proc interface functions using seq_file 638c2ecf20Sopenharmony_ci * facility as same as other ACPI drivers. 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * Aug.28, 2004 Hiroshi Miura <miura@da-cha.org> 668c2ecf20Sopenharmony_ci * -v0.6.4 Fix a silly error with status checking 678c2ecf20Sopenharmony_ci * 688c2ecf20Sopenharmony_ci * Aug.25, 2004 Hiroshi Miura <miura@da-cha.org> 698c2ecf20Sopenharmony_ci * -v0.6.3 replace read_acpi_int by standard function 708c2ecf20Sopenharmony_ci * acpi_evaluate_integer 718c2ecf20Sopenharmony_ci * some clean up and make smart copyright notice. 728c2ecf20Sopenharmony_ci * fix return value of pcc_acpi_get_key() 738c2ecf20Sopenharmony_ci * fix checking return value of acpi_bus_register_driver() 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * Aug.22, 2004 David Bronaugh <dbronaugh@linuxboxen.org> 768c2ecf20Sopenharmony_ci * -v0.6.2 Add check on ACPI data (num_sifr) 778c2ecf20Sopenharmony_ci * Coding style cleanups, better error messages/handling 788c2ecf20Sopenharmony_ci * Fixed an off-by-one error in memory allocation 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * Aug.21, 2004 David Bronaugh <dbronaugh@linuxboxen.org> 818c2ecf20Sopenharmony_ci * -v0.6.1 Fix a silly error with status checking 828c2ecf20Sopenharmony_ci * 838c2ecf20Sopenharmony_ci * Aug.20, 2004 David Bronaugh <dbronaugh@linuxboxen.org> 848c2ecf20Sopenharmony_ci * - v0.6 Correct brightness controls to reflect reality 858c2ecf20Sopenharmony_ci * based on information gleaned by Hiroshi Miura 868c2ecf20Sopenharmony_ci * and discussions with Hiroshi Miura 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * Aug.10, 2004 Hiroshi Miura <miura@da-cha.org> 898c2ecf20Sopenharmony_ci * - v0.5 support LCD brightness control 908c2ecf20Sopenharmony_ci * based on the disclosed information by MEI. 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * Jul.25, 2004 Hiroshi Miura <miura@da-cha.org> 938c2ecf20Sopenharmony_ci * - v0.4 first post version 948c2ecf20Sopenharmony_ci * add function to retrive SIFR 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * Jul.24, 2004 Hiroshi Miura <miura@da-cha.org> 978c2ecf20Sopenharmony_ci * - v0.3 get proper status of hotkey 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * Jul.22, 2004 Hiroshi Miura <miura@da-cha.org> 1008c2ecf20Sopenharmony_ci * - v0.2 add HotKey handler 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * Jul.17, 2004 Hiroshi Miura <miura@da-cha.org> 1038c2ecf20Sopenharmony_ci * - v0.1 start from toshiba_acpi driver written by John Belmonte 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#include <linux/kernel.h> 1078c2ecf20Sopenharmony_ci#include <linux/module.h> 1088c2ecf20Sopenharmony_ci#include <linux/init.h> 1098c2ecf20Sopenharmony_ci#include <linux/types.h> 1108c2ecf20Sopenharmony_ci#include <linux/backlight.h> 1118c2ecf20Sopenharmony_ci#include <linux/ctype.h> 1128c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 1138c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 1148c2ecf20Sopenharmony_ci#include <linux/slab.h> 1158c2ecf20Sopenharmony_ci#include <linux/acpi.h> 1168c2ecf20Sopenharmony_ci#include <linux/input.h> 1178c2ecf20Sopenharmony_ci#include <linux/input/sparse-keymap.h> 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#ifndef ACPI_HOTKEY_COMPONENT 1208c2ecf20Sopenharmony_ci#define ACPI_HOTKEY_COMPONENT 0x10000000 1218c2ecf20Sopenharmony_ci#endif 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define _COMPONENT ACPI_HOTKEY_COMPONENT 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hiroshi Miura, David Bronaugh and Harald Welte"); 1268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops"); 1278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#define LOGPREFIX "pcc_acpi: " 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* Define ACPI PATHs */ 1328c2ecf20Sopenharmony_ci/* Lets note hotkeys */ 1338c2ecf20Sopenharmony_ci#define METHOD_HKEY_QUERY "HINF" 1348c2ecf20Sopenharmony_ci#define METHOD_HKEY_SQTY "SQTY" 1358c2ecf20Sopenharmony_ci#define METHOD_HKEY_SINF "SINF" 1368c2ecf20Sopenharmony_ci#define METHOD_HKEY_SSET "SSET" 1378c2ecf20Sopenharmony_ci#define HKEY_NOTIFY 0x80 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#define ACPI_PCC_DRIVER_NAME "Panasonic Laptop Support" 1408c2ecf20Sopenharmony_ci#define ACPI_PCC_DEVICE_NAME "Hotkey" 1418c2ecf20Sopenharmony_ci#define ACPI_PCC_CLASS "pcc" 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#define ACPI_PCC_INPUT_PHYS "panasonic/hkey0" 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent 1468c2ecf20Sopenharmony_ci ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00 1478c2ecf20Sopenharmony_ci*/ 1488c2ecf20Sopenharmony_cienum SINF_BITS { SINF_NUM_BATTERIES = 0, 1498c2ecf20Sopenharmony_ci SINF_LCD_TYPE, 1508c2ecf20Sopenharmony_ci SINF_AC_MAX_BRIGHT, 1518c2ecf20Sopenharmony_ci SINF_AC_MIN_BRIGHT, 1528c2ecf20Sopenharmony_ci SINF_AC_CUR_BRIGHT, 1538c2ecf20Sopenharmony_ci SINF_DC_MAX_BRIGHT, 1548c2ecf20Sopenharmony_ci SINF_DC_MIN_BRIGHT, 1558c2ecf20Sopenharmony_ci SINF_DC_CUR_BRIGHT, 1568c2ecf20Sopenharmony_ci SINF_MUTE, 1578c2ecf20Sopenharmony_ci SINF_RESERVED, 1588c2ecf20Sopenharmony_ci SINF_ENV_STATE, 1598c2ecf20Sopenharmony_ci SINF_STICKY_KEY = 0x80, 1608c2ecf20Sopenharmony_ci }; 1618c2ecf20Sopenharmony_ci/* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */ 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int acpi_pcc_hotkey_add(struct acpi_device *device); 1648c2ecf20Sopenharmony_cistatic int acpi_pcc_hotkey_remove(struct acpi_device *device); 1658c2ecf20Sopenharmony_cistatic void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic const struct acpi_device_id pcc_device_ids[] = { 1688c2ecf20Sopenharmony_ci { "MAT0012", 0}, 1698c2ecf20Sopenharmony_ci { "MAT0013", 0}, 1708c2ecf20Sopenharmony_ci { "MAT0018", 0}, 1718c2ecf20Sopenharmony_ci { "MAT0019", 0}, 1728c2ecf20Sopenharmony_ci { "", 0}, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, pcc_device_ids); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 1778c2ecf20Sopenharmony_cistatic int acpi_pcc_hotkey_resume(struct device *dev); 1788c2ecf20Sopenharmony_ci#endif 1798c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(acpi_pcc_hotkey_pm, NULL, acpi_pcc_hotkey_resume); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic struct acpi_driver acpi_pcc_driver = { 1828c2ecf20Sopenharmony_ci .name = ACPI_PCC_DRIVER_NAME, 1838c2ecf20Sopenharmony_ci .class = ACPI_PCC_CLASS, 1848c2ecf20Sopenharmony_ci .ids = pcc_device_ids, 1858c2ecf20Sopenharmony_ci .ops = { 1868c2ecf20Sopenharmony_ci .add = acpi_pcc_hotkey_add, 1878c2ecf20Sopenharmony_ci .remove = acpi_pcc_hotkey_remove, 1888c2ecf20Sopenharmony_ci .notify = acpi_pcc_hotkey_notify, 1898c2ecf20Sopenharmony_ci }, 1908c2ecf20Sopenharmony_ci .drv.pm = &acpi_pcc_hotkey_pm, 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic const struct key_entry panasonic_keymap[] = { 1948c2ecf20Sopenharmony_ci { KE_KEY, 0, { KEY_RESERVED } }, 1958c2ecf20Sopenharmony_ci { KE_KEY, 1, { KEY_BRIGHTNESSDOWN } }, 1968c2ecf20Sopenharmony_ci { KE_KEY, 2, { KEY_BRIGHTNESSUP } }, 1978c2ecf20Sopenharmony_ci { KE_KEY, 3, { KEY_DISPLAYTOGGLE } }, 1988c2ecf20Sopenharmony_ci { KE_KEY, 4, { KEY_MUTE } }, 1998c2ecf20Sopenharmony_ci { KE_KEY, 5, { KEY_VOLUMEDOWN } }, 2008c2ecf20Sopenharmony_ci { KE_KEY, 6, { KEY_VOLUMEUP } }, 2018c2ecf20Sopenharmony_ci { KE_KEY, 7, { KEY_SLEEP } }, 2028c2ecf20Sopenharmony_ci { KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */ 2038c2ecf20Sopenharmony_ci { KE_KEY, 9, { KEY_BATTERY } }, 2048c2ecf20Sopenharmony_ci { KE_KEY, 10, { KEY_SUSPEND } }, 2058c2ecf20Sopenharmony_ci { KE_END, 0 } 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistruct pcc_acpi { 2098c2ecf20Sopenharmony_ci acpi_handle handle; 2108c2ecf20Sopenharmony_ci unsigned long num_sifr; 2118c2ecf20Sopenharmony_ci int sticky_mode; 2128c2ecf20Sopenharmony_ci u32 *sinf; 2138c2ecf20Sopenharmony_ci struct acpi_device *device; 2148c2ecf20Sopenharmony_ci struct input_dev *input_dev; 2158c2ecf20Sopenharmony_ci struct backlight_device *backlight; 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* method access functions */ 2198c2ecf20Sopenharmony_cistatic int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci union acpi_object in_objs[] = { 2228c2ecf20Sopenharmony_ci { .integer.type = ACPI_TYPE_INTEGER, 2238c2ecf20Sopenharmony_ci .integer.value = func, }, 2248c2ecf20Sopenharmony_ci { .integer.type = ACPI_TYPE_INTEGER, 2258c2ecf20Sopenharmony_ci .integer.value = val, }, 2268c2ecf20Sopenharmony_ci }; 2278c2ecf20Sopenharmony_ci struct acpi_object_list params = { 2288c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(in_objs), 2298c2ecf20Sopenharmony_ci .pointer = in_objs, 2308c2ecf20Sopenharmony_ci }; 2318c2ecf20Sopenharmony_ci acpi_status status = AE_OK; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET, 2348c2ecf20Sopenharmony_ci ¶ms, NULL); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return (status == AE_OK) ? 0 : -EIO; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic inline int acpi_pcc_get_sqty(struct acpi_device *device) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci unsigned long long s; 2428c2ecf20Sopenharmony_ci acpi_status status; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY, 2458c2ecf20Sopenharmony_ci NULL, &s); 2468c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status)) 2478c2ecf20Sopenharmony_ci return s; 2488c2ecf20Sopenharmony_ci else { 2498c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 2508c2ecf20Sopenharmony_ci "evaluation error HKEY.SQTY\n")); 2518c2ecf20Sopenharmony_ci return -EINVAL; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci acpi_status status; 2588c2ecf20Sopenharmony_ci struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; 2598c2ecf20Sopenharmony_ci union acpi_object *hkey = NULL; 2608c2ecf20Sopenharmony_ci int i; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, NULL, 2638c2ecf20Sopenharmony_ci &buffer); 2648c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 2658c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 2668c2ecf20Sopenharmony_ci "evaluation error HKEY.SINF\n")); 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci hkey = buffer.pointer; 2718c2ecf20Sopenharmony_ci if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) { 2728c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n")); 2738c2ecf20Sopenharmony_ci status = AE_ERROR; 2748c2ecf20Sopenharmony_ci goto end; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (pcc->num_sifr < hkey->package.count) { 2788c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 2798c2ecf20Sopenharmony_ci "SQTY reports bad SINF length\n")); 2808c2ecf20Sopenharmony_ci status = AE_ERROR; 2818c2ecf20Sopenharmony_ci goto end; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci for (i = 0; i < hkey->package.count; i++) { 2858c2ecf20Sopenharmony_ci union acpi_object *element = &(hkey->package.elements[i]); 2868c2ecf20Sopenharmony_ci if (likely(element->type == ACPI_TYPE_INTEGER)) { 2878c2ecf20Sopenharmony_ci pcc->sinf[i] = element->integer.value; 2888c2ecf20Sopenharmony_ci } else 2898c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 2908c2ecf20Sopenharmony_ci "Invalid HKEY.SINF data\n")); 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci pcc->sinf[hkey->package.count] = -1; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ciend: 2958c2ecf20Sopenharmony_ci kfree(buffer.pointer); 2968c2ecf20Sopenharmony_ci return status == AE_OK; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* backlight API interface functions */ 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* This driver currently treats AC and DC brightness identical, 3028c2ecf20Sopenharmony_ci * since we don't need to invent an interface to the core ACPI 3038c2ecf20Sopenharmony_ci * logic to receive events in case a power supply is plugged in 3048c2ecf20Sopenharmony_ci * or removed */ 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int bl_get(struct backlight_device *bd) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct pcc_acpi *pcc = bl_get_data(bd); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 3118c2ecf20Sopenharmony_ci return -EIO; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return pcc->sinf[SINF_AC_CUR_BRIGHT]; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int bl_set_status(struct backlight_device *bd) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct pcc_acpi *pcc = bl_get_data(bd); 3198c2ecf20Sopenharmony_ci int bright = bd->props.brightness; 3208c2ecf20Sopenharmony_ci int rc; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 3238c2ecf20Sopenharmony_ci return -EIO; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT]) 3268c2ecf20Sopenharmony_ci bright = pcc->sinf[SINF_AC_MIN_BRIGHT]; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT]) 3298c2ecf20Sopenharmony_ci bright = pcc->sinf[SINF_DC_MIN_BRIGHT]; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] || 3328c2ecf20Sopenharmony_ci bright > pcc->sinf[SINF_AC_MAX_BRIGHT]) 3338c2ecf20Sopenharmony_ci return -EINVAL; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright); 3368c2ecf20Sopenharmony_ci if (rc < 0) 3378c2ecf20Sopenharmony_ci return rc; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic const struct backlight_ops pcc_backlight_ops = { 3438c2ecf20Sopenharmony_ci .get_brightness = bl_get, 3448c2ecf20Sopenharmony_ci .update_status = bl_set_status, 3458c2ecf20Sopenharmony_ci}; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci/* sysfs user interface functions */ 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic ssize_t show_numbatt(struct device *dev, struct device_attribute *attr, 3518c2ecf20Sopenharmony_ci char *buf) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 3548c2ecf20Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 3578c2ecf20Sopenharmony_ci return -EIO; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr, 3638c2ecf20Sopenharmony_ci char *buf) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 3668c2ecf20Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 3698c2ecf20Sopenharmony_ci return -EIO; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic ssize_t show_mute(struct device *dev, struct device_attribute *attr, 3758c2ecf20Sopenharmony_ci char *buf) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 3788c2ecf20Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 3818c2ecf20Sopenharmony_ci return -EIO; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic ssize_t show_sticky(struct device *dev, struct device_attribute *attr, 3878c2ecf20Sopenharmony_ci char *buf) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 3908c2ecf20Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) 3938c2ecf20Sopenharmony_ci return -EIO; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic ssize_t set_sticky(struct device *dev, struct device_attribute *attr, 3998c2ecf20Sopenharmony_ci const char *buf, size_t count) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct acpi_device *acpi = to_acpi_device(dev); 4028c2ecf20Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(acpi); 4038c2ecf20Sopenharmony_ci int val; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (count && sscanf(buf, "%i", &val) == 1 && 4068c2ecf20Sopenharmony_ci (val == 0 || val == 1)) { 4078c2ecf20Sopenharmony_ci acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val); 4088c2ecf20Sopenharmony_ci pcc->sticky_mode = val; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return count; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic DEVICE_ATTR(numbatt, S_IRUGO, show_numbatt, NULL); 4158c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lcdtype, S_IRUGO, show_lcdtype, NULL); 4168c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mute, S_IRUGO, show_mute, NULL); 4178c2ecf20Sopenharmony_cistatic DEVICE_ATTR(sticky_key, S_IRUGO | S_IWUSR, show_sticky, set_sticky); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic struct attribute *pcc_sysfs_entries[] = { 4208c2ecf20Sopenharmony_ci &dev_attr_numbatt.attr, 4218c2ecf20Sopenharmony_ci &dev_attr_lcdtype.attr, 4228c2ecf20Sopenharmony_ci &dev_attr_mute.attr, 4238c2ecf20Sopenharmony_ci &dev_attr_sticky_key.attr, 4248c2ecf20Sopenharmony_ci NULL, 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic const struct attribute_group pcc_attr_group = { 4288c2ecf20Sopenharmony_ci .name = NULL, /* put in device directory */ 4298c2ecf20Sopenharmony_ci .attrs = pcc_sysfs_entries, 4308c2ecf20Sopenharmony_ci}; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci/* hotkey input device driver */ 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic int sleep_keydown_seen; 4368c2ecf20Sopenharmony_cistatic void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct input_dev *hotk_input_dev = pcc->input_dev; 4398c2ecf20Sopenharmony_ci int rc; 4408c2ecf20Sopenharmony_ci unsigned long long result; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY, 4438c2ecf20Sopenharmony_ci NULL, &result); 4448c2ecf20Sopenharmony_ci if (ACPI_FAILURE(rc)) { 4458c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 4468c2ecf20Sopenharmony_ci "error getting hotkey status\n")); 4478c2ecf20Sopenharmony_ci return; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* hack: some firmware sends no key down for sleep / hibernate */ 4518c2ecf20Sopenharmony_ci if ((result & 0xf) == 0x7 || (result & 0xf) == 0xa) { 4528c2ecf20Sopenharmony_ci if (result & 0x80) 4538c2ecf20Sopenharmony_ci sleep_keydown_seen = 1; 4548c2ecf20Sopenharmony_ci if (!sleep_keydown_seen) 4558c2ecf20Sopenharmony_ci sparse_keymap_report_event(hotk_input_dev, 4568c2ecf20Sopenharmony_ci result & 0xf, 0x80, false); 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (!sparse_keymap_report_event(hotk_input_dev, 4608c2ecf20Sopenharmony_ci result & 0xf, result & 0x80, false)) 4618c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 4628c2ecf20Sopenharmony_ci "Unknown hotkey event: %d\n", result)); 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(device); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci switch (event) { 4708c2ecf20Sopenharmony_ci case HKEY_NOTIFY: 4718c2ecf20Sopenharmony_ci acpi_pcc_generate_keyinput(pcc); 4728c2ecf20Sopenharmony_ci break; 4738c2ecf20Sopenharmony_ci default: 4748c2ecf20Sopenharmony_ci /* nothing to do */ 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic int acpi_pcc_init_input(struct pcc_acpi *pcc) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct input_dev *input_dev; 4828c2ecf20Sopenharmony_ci int error; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci input_dev = input_allocate_device(); 4858c2ecf20Sopenharmony_ci if (!input_dev) 4868c2ecf20Sopenharmony_ci return -ENOMEM; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci input_dev->name = ACPI_PCC_DRIVER_NAME; 4898c2ecf20Sopenharmony_ci input_dev->phys = ACPI_PCC_INPUT_PHYS; 4908c2ecf20Sopenharmony_ci input_dev->id.bustype = BUS_HOST; 4918c2ecf20Sopenharmony_ci input_dev->id.vendor = 0x0001; 4928c2ecf20Sopenharmony_ci input_dev->id.product = 0x0001; 4938c2ecf20Sopenharmony_ci input_dev->id.version = 0x0100; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL); 4968c2ecf20Sopenharmony_ci if (error) { 4978c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 4988c2ecf20Sopenharmony_ci "Unable to setup input device keymap\n")); 4998c2ecf20Sopenharmony_ci goto err_free_dev; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci error = input_register_device(input_dev); 5038c2ecf20Sopenharmony_ci if (error) { 5048c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 5058c2ecf20Sopenharmony_ci "Unable to register input device\n")); 5068c2ecf20Sopenharmony_ci goto err_free_dev; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci pcc->input_dev = input_dev; 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci err_free_dev: 5138c2ecf20Sopenharmony_ci input_free_device(input_dev); 5148c2ecf20Sopenharmony_ci return error; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci/* kernel module interface */ 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5208c2ecf20Sopenharmony_cistatic int acpi_pcc_hotkey_resume(struct device *dev) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct pcc_acpi *pcc; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (!dev) 5258c2ecf20Sopenharmony_ci return -EINVAL; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci pcc = acpi_driver_data(to_acpi_device(dev)); 5288c2ecf20Sopenharmony_ci if (!pcc) 5298c2ecf20Sopenharmony_ci return -EINVAL; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n", 5328c2ecf20Sopenharmony_ci pcc->sticky_mode)); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci return acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode); 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci#endif 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic int acpi_pcc_hotkey_add(struct acpi_device *device) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct backlight_properties props; 5418c2ecf20Sopenharmony_ci struct pcc_acpi *pcc; 5428c2ecf20Sopenharmony_ci int num_sifr, result; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (!device) 5458c2ecf20Sopenharmony_ci return -EINVAL; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci num_sifr = acpi_pcc_get_sqty(device); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (num_sifr < 0 || num_sifr > 255) { 5508c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr out of range")); 5518c2ecf20Sopenharmony_ci return -ENODEV; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL); 5558c2ecf20Sopenharmony_ci if (!pcc) { 5568c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 5578c2ecf20Sopenharmony_ci "Couldn't allocate mem for pcc")); 5588c2ecf20Sopenharmony_ci return -ENOMEM; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci pcc->sinf = kcalloc(num_sifr + 1, sizeof(u32), GFP_KERNEL); 5628c2ecf20Sopenharmony_ci if (!pcc->sinf) { 5638c2ecf20Sopenharmony_ci result = -ENOMEM; 5648c2ecf20Sopenharmony_ci goto out_hotkey; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci pcc->device = device; 5688c2ecf20Sopenharmony_ci pcc->handle = device->handle; 5698c2ecf20Sopenharmony_ci pcc->num_sifr = num_sifr; 5708c2ecf20Sopenharmony_ci device->driver_data = pcc; 5718c2ecf20Sopenharmony_ci strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME); 5728c2ecf20Sopenharmony_ci strcpy(acpi_device_class(device), ACPI_PCC_CLASS); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci result = acpi_pcc_init_input(pcc); 5758c2ecf20Sopenharmony_ci if (result) { 5768c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 5778c2ecf20Sopenharmony_ci "Error installing keyinput handler\n")); 5788c2ecf20Sopenharmony_ci goto out_sinf; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (!acpi_pcc_retrieve_biosdata(pcc)) { 5828c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 5838c2ecf20Sopenharmony_ci "Couldn't retrieve BIOS data\n")); 5848c2ecf20Sopenharmony_ci result = -EIO; 5858c2ecf20Sopenharmony_ci goto out_input; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci /* initialize backlight */ 5888c2ecf20Sopenharmony_ci memset(&props, 0, sizeof(struct backlight_properties)); 5898c2ecf20Sopenharmony_ci props.type = BACKLIGHT_PLATFORM; 5908c2ecf20Sopenharmony_ci props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT]; 5918c2ecf20Sopenharmony_ci pcc->backlight = backlight_device_register("panasonic", NULL, pcc, 5928c2ecf20Sopenharmony_ci &pcc_backlight_ops, &props); 5938c2ecf20Sopenharmony_ci if (IS_ERR(pcc->backlight)) { 5948c2ecf20Sopenharmony_ci result = PTR_ERR(pcc->backlight); 5958c2ecf20Sopenharmony_ci goto out_input; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci /* read the initial brightness setting from the hardware */ 5998c2ecf20Sopenharmony_ci pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT]; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci /* read the initial sticky key mode from the hardware */ 6028c2ecf20Sopenharmony_ci pcc->sticky_mode = pcc->sinf[SINF_STICKY_KEY]; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* add sysfs attributes */ 6058c2ecf20Sopenharmony_ci result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group); 6068c2ecf20Sopenharmony_ci if (result) 6078c2ecf20Sopenharmony_ci goto out_backlight; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci return 0; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ciout_backlight: 6128c2ecf20Sopenharmony_ci backlight_device_unregister(pcc->backlight); 6138c2ecf20Sopenharmony_ciout_input: 6148c2ecf20Sopenharmony_ci input_unregister_device(pcc->input_dev); 6158c2ecf20Sopenharmony_ciout_sinf: 6168c2ecf20Sopenharmony_ci kfree(pcc->sinf); 6178c2ecf20Sopenharmony_ciout_hotkey: 6188c2ecf20Sopenharmony_ci kfree(pcc); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci return result; 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic int acpi_pcc_hotkey_remove(struct acpi_device *device) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct pcc_acpi *pcc = acpi_driver_data(device); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (!device || !pcc) 6288c2ecf20Sopenharmony_ci return -EINVAL; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci sysfs_remove_group(&device->dev.kobj, &pcc_attr_group); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci backlight_device_unregister(pcc->backlight); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci input_unregister_device(pcc->input_dev); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci kfree(pcc->sinf); 6378c2ecf20Sopenharmony_ci kfree(pcc); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci return 0; 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cimodule_acpi_driver(acpi_pcc_driver); 643