brintos

brintos / linux-shallow public Read only

0
0
Text · 29.1 KiB · 2bf94d0 Raw
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				      &params, 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