xref: /OK3568_Linux_fs/kernel/drivers/platform/x86/panasonic-laptop.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  Panasonic HotKey and LCD brightness control driver
4*4882a593Smuzhiyun  *  (C) 2004 Hiroshi Miura <miura@da-cha.org>
5*4882a593Smuzhiyun  *  (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/
6*4882a593Smuzhiyun  *  (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>
7*4882a593Smuzhiyun  *  (C) 2004 David Bronaugh <dbronaugh>
8*4882a593Smuzhiyun  *  (C) 2006-2008 Harald Welte <laforge@gnumonks.org>
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  *  derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  *---------------------------------------------------------------------------
13*4882a593Smuzhiyun  *
14*4882a593Smuzhiyun  * ChangeLog:
15*4882a593Smuzhiyun  *	Sep.23, 2008	Harald Welte <laforge@gnumonks.org>
16*4882a593Smuzhiyun  *		-v0.95	rename driver from drivers/acpi/pcc_acpi.c to
17*4882a593Smuzhiyun  *			drivers/misc/panasonic-laptop.c
18*4882a593Smuzhiyun  *
19*4882a593Smuzhiyun  * 	Jul.04, 2008	Harald Welte <laforge@gnumonks.org>
20*4882a593Smuzhiyun  * 		-v0.94	replace /proc interface with device attributes
21*4882a593Smuzhiyun  * 			support {set,get}keycode on th input device
22*4882a593Smuzhiyun  *
23*4882a593Smuzhiyun  *      Jun.27, 2008	Harald Welte <laforge@gnumonks.org>
24*4882a593Smuzhiyun  *      	-v0.92	merge with 2.6.26-rc6 input API changes
25*4882a593Smuzhiyun  *      		remove broken <= 2.6.15 kernel support
26*4882a593Smuzhiyun  *      		resolve all compiler warnings
27*4882a593Smuzhiyun  *      		various coding style fixes (checkpatch.pl)
28*4882a593Smuzhiyun  *      		add support for backlight api
29*4882a593Smuzhiyun  *      		major code restructuring
30*4882a593Smuzhiyun  *
31*4882a593Smuzhiyun  * 	Dac.28, 2007	Harald Welte <laforge@gnumonks.org>
32*4882a593Smuzhiyun  * 		-v0.91	merge with 2.6.24-rc6 ACPI changes
33*4882a593Smuzhiyun  *
34*4882a593Smuzhiyun  * 	Nov.04, 2006	Hiroshi Miura <miura@da-cha.org>
35*4882a593Smuzhiyun  * 		-v0.9	remove warning about section reference.
36*4882a593Smuzhiyun  * 			remove acpi_os_free
37*4882a593Smuzhiyun  * 			add /proc/acpi/pcc/brightness interface for HAL access
38*4882a593Smuzhiyun  * 			merge dbronaugh's enhancement
39*4882a593Smuzhiyun  * 			Aug.17, 2004 David Bronaugh (dbronaugh)
40*4882a593Smuzhiyun  *  				- Added screen brightness setting interface
41*4882a593Smuzhiyun  *				  Thanks to FreeBSD crew (acpi_panasonic.c)
42*4882a593Smuzhiyun  * 				  for the ideas I needed to accomplish it
43*4882a593Smuzhiyun  *
44*4882a593Smuzhiyun  *	May.29, 2006	Hiroshi Miura <miura@da-cha.org>
45*4882a593Smuzhiyun  *		-v0.8.4 follow to change keyinput structure
46*4882a593Smuzhiyun  *			thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,
47*4882a593Smuzhiyun  *			Jacob Bower <jacob.bower@ic.ac.uk> and
48*4882a593Smuzhiyun  *			Hiroshi Yokota for providing solutions.
49*4882a593Smuzhiyun  *
50*4882a593Smuzhiyun  *	Oct.02, 2004	Hiroshi Miura <miura@da-cha.org>
51*4882a593Smuzhiyun  *		-v0.8.2	merge code of YOKOTA Hiroshi
52*4882a593Smuzhiyun  *					<yokota@netlab.is.tsukuba.ac.jp>.
53*4882a593Smuzhiyun  *			Add sticky key mode interface.
54*4882a593Smuzhiyun  *			Refactoring acpi_pcc_generate_keyinput().
55*4882a593Smuzhiyun  *
56*4882a593Smuzhiyun  *	Sep.15, 2004	Hiroshi Miura <miura@da-cha.org>
57*4882a593Smuzhiyun  *		-v0.8	Generate key input event on input subsystem.
58*4882a593Smuzhiyun  *			This is based on yet another driver written by
59*4882a593Smuzhiyun  *							Ryuta Nakanishi.
60*4882a593Smuzhiyun  *
61*4882a593Smuzhiyun  *	Sep.10, 2004	Hiroshi Miura <miura@da-cha.org>
62*4882a593Smuzhiyun  *		-v0.7	Change proc interface functions using seq_file
63*4882a593Smuzhiyun  *			facility as same as other ACPI drivers.
64*4882a593Smuzhiyun  *
65*4882a593Smuzhiyun  *	Aug.28, 2004	Hiroshi Miura <miura@da-cha.org>
66*4882a593Smuzhiyun  *		-v0.6.4 Fix a silly error with status checking
67*4882a593Smuzhiyun  *
68*4882a593Smuzhiyun  *	Aug.25, 2004	Hiroshi Miura <miura@da-cha.org>
69*4882a593Smuzhiyun  *		-v0.6.3 replace read_acpi_int by standard function
70*4882a593Smuzhiyun  *							acpi_evaluate_integer
71*4882a593Smuzhiyun  *			some clean up and make smart copyright notice.
72*4882a593Smuzhiyun  *			fix return value of pcc_acpi_get_key()
73*4882a593Smuzhiyun  *			fix checking return value of acpi_bus_register_driver()
74*4882a593Smuzhiyun  *
75*4882a593Smuzhiyun  *      Aug.22, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
76*4882a593Smuzhiyun  *              -v0.6.2 Add check on ACPI data (num_sifr)
77*4882a593Smuzhiyun  *                      Coding style cleanups, better error messages/handling
78*4882a593Smuzhiyun  *			Fixed an off-by-one error in memory allocation
79*4882a593Smuzhiyun  *
80*4882a593Smuzhiyun  *      Aug.21, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
81*4882a593Smuzhiyun  *              -v0.6.1 Fix a silly error with status checking
82*4882a593Smuzhiyun  *
83*4882a593Smuzhiyun  *      Aug.20, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
84*4882a593Smuzhiyun  *              - v0.6  Correct brightness controls to reflect reality
85*4882a593Smuzhiyun  *                      based on information gleaned by Hiroshi Miura
86*4882a593Smuzhiyun  *                      and discussions with Hiroshi Miura
87*4882a593Smuzhiyun  *
88*4882a593Smuzhiyun  *	Aug.10, 2004	Hiroshi Miura <miura@da-cha.org>
89*4882a593Smuzhiyun  *		- v0.5  support LCD brightness control
90*4882a593Smuzhiyun  *			based on the disclosed information by MEI.
91*4882a593Smuzhiyun  *
92*4882a593Smuzhiyun  *	Jul.25, 2004	Hiroshi Miura <miura@da-cha.org>
93*4882a593Smuzhiyun  *		- v0.4  first post version
94*4882a593Smuzhiyun  *		        add function to retrive SIFR
95*4882a593Smuzhiyun  *
96*4882a593Smuzhiyun  *	Jul.24, 2004	Hiroshi Miura <miura@da-cha.org>
97*4882a593Smuzhiyun  *		- v0.3  get proper status of hotkey
98*4882a593Smuzhiyun  *
99*4882a593Smuzhiyun  *      Jul.22, 2004	Hiroshi Miura <miura@da-cha.org>
100*4882a593Smuzhiyun  *		- v0.2  add HotKey handler
101*4882a593Smuzhiyun  *
102*4882a593Smuzhiyun  *      Jul.17, 2004	Hiroshi Miura <miura@da-cha.org>
103*4882a593Smuzhiyun  *		- v0.1  start from toshiba_acpi driver written by John Belmonte
104*4882a593Smuzhiyun  */
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun #include <linux/kernel.h>
107*4882a593Smuzhiyun #include <linux/module.h>
108*4882a593Smuzhiyun #include <linux/init.h>
109*4882a593Smuzhiyun #include <linux/types.h>
110*4882a593Smuzhiyun #include <linux/backlight.h>
111*4882a593Smuzhiyun #include <linux/ctype.h>
112*4882a593Smuzhiyun #include <linux/seq_file.h>
113*4882a593Smuzhiyun #include <linux/uaccess.h>
114*4882a593Smuzhiyun #include <linux/slab.h>
115*4882a593Smuzhiyun #include <linux/acpi.h>
116*4882a593Smuzhiyun #include <linux/input.h>
117*4882a593Smuzhiyun #include <linux/input/sparse-keymap.h>
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun #ifndef ACPI_HOTKEY_COMPONENT
120*4882a593Smuzhiyun #define ACPI_HOTKEY_COMPONENT	0x10000000
121*4882a593Smuzhiyun #endif
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun #define _COMPONENT		ACPI_HOTKEY_COMPONENT
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun MODULE_AUTHOR("Hiroshi Miura, David Bronaugh and Harald Welte");
126*4882a593Smuzhiyun MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops");
127*4882a593Smuzhiyun MODULE_LICENSE("GPL");
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun #define LOGPREFIX "pcc_acpi: "
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun /* Define ACPI PATHs */
132*4882a593Smuzhiyun /* Lets note hotkeys */
133*4882a593Smuzhiyun #define METHOD_HKEY_QUERY	"HINF"
134*4882a593Smuzhiyun #define METHOD_HKEY_SQTY	"SQTY"
135*4882a593Smuzhiyun #define METHOD_HKEY_SINF	"SINF"
136*4882a593Smuzhiyun #define METHOD_HKEY_SSET	"SSET"
137*4882a593Smuzhiyun #define HKEY_NOTIFY		 0x80
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun #define ACPI_PCC_DRIVER_NAME	"Panasonic Laptop Support"
140*4882a593Smuzhiyun #define ACPI_PCC_DEVICE_NAME	"Hotkey"
141*4882a593Smuzhiyun #define ACPI_PCC_CLASS		"pcc"
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun #define ACPI_PCC_INPUT_PHYS	"panasonic/hkey0"
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun /* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
146*4882a593Smuzhiyun    ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
147*4882a593Smuzhiyun */
148*4882a593Smuzhiyun enum SINF_BITS { SINF_NUM_BATTERIES = 0,
149*4882a593Smuzhiyun 		 SINF_LCD_TYPE,
150*4882a593Smuzhiyun 		 SINF_AC_MAX_BRIGHT,
151*4882a593Smuzhiyun 		 SINF_AC_MIN_BRIGHT,
152*4882a593Smuzhiyun 		 SINF_AC_CUR_BRIGHT,
153*4882a593Smuzhiyun 		 SINF_DC_MAX_BRIGHT,
154*4882a593Smuzhiyun 		 SINF_DC_MIN_BRIGHT,
155*4882a593Smuzhiyun 		 SINF_DC_CUR_BRIGHT,
156*4882a593Smuzhiyun 		 SINF_MUTE,
157*4882a593Smuzhiyun 		 SINF_RESERVED,
158*4882a593Smuzhiyun 		 SINF_ENV_STATE,
159*4882a593Smuzhiyun 		 SINF_STICKY_KEY = 0x80,
160*4882a593Smuzhiyun 	};
161*4882a593Smuzhiyun /* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun static int acpi_pcc_hotkey_add(struct acpi_device *device);
164*4882a593Smuzhiyun static int acpi_pcc_hotkey_remove(struct acpi_device *device);
165*4882a593Smuzhiyun static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event);
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun static const struct acpi_device_id pcc_device_ids[] = {
168*4882a593Smuzhiyun 	{ "MAT0012", 0},
169*4882a593Smuzhiyun 	{ "MAT0013", 0},
170*4882a593Smuzhiyun 	{ "MAT0018", 0},
171*4882a593Smuzhiyun 	{ "MAT0019", 0},
172*4882a593Smuzhiyun 	{ "", 0},
173*4882a593Smuzhiyun };
174*4882a593Smuzhiyun MODULE_DEVICE_TABLE(acpi, pcc_device_ids);
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
177*4882a593Smuzhiyun static int acpi_pcc_hotkey_resume(struct device *dev);
178*4882a593Smuzhiyun #endif
179*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(acpi_pcc_hotkey_pm, NULL, acpi_pcc_hotkey_resume);
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun static struct acpi_driver acpi_pcc_driver = {
182*4882a593Smuzhiyun 	.name =		ACPI_PCC_DRIVER_NAME,
183*4882a593Smuzhiyun 	.class =	ACPI_PCC_CLASS,
184*4882a593Smuzhiyun 	.ids =		pcc_device_ids,
185*4882a593Smuzhiyun 	.ops =		{
186*4882a593Smuzhiyun 				.add =		acpi_pcc_hotkey_add,
187*4882a593Smuzhiyun 				.remove =	acpi_pcc_hotkey_remove,
188*4882a593Smuzhiyun 				.notify =	acpi_pcc_hotkey_notify,
189*4882a593Smuzhiyun 			},
190*4882a593Smuzhiyun 	.drv.pm =	&acpi_pcc_hotkey_pm,
191*4882a593Smuzhiyun };
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun static const struct key_entry panasonic_keymap[] = {
194*4882a593Smuzhiyun 	{ KE_KEY, 0, { KEY_RESERVED } },
195*4882a593Smuzhiyun 	{ KE_KEY, 1, { KEY_BRIGHTNESSDOWN } },
196*4882a593Smuzhiyun 	{ KE_KEY, 2, { KEY_BRIGHTNESSUP } },
197*4882a593Smuzhiyun 	{ KE_KEY, 3, { KEY_DISPLAYTOGGLE } },
198*4882a593Smuzhiyun 	{ KE_KEY, 4, { KEY_MUTE } },
199*4882a593Smuzhiyun 	{ KE_KEY, 5, { KEY_VOLUMEDOWN } },
200*4882a593Smuzhiyun 	{ KE_KEY, 6, { KEY_VOLUMEUP } },
201*4882a593Smuzhiyun 	{ KE_KEY, 7, { KEY_SLEEP } },
202*4882a593Smuzhiyun 	{ KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */
203*4882a593Smuzhiyun 	{ KE_KEY, 9, { KEY_BATTERY } },
204*4882a593Smuzhiyun 	{ KE_KEY, 10, { KEY_SUSPEND } },
205*4882a593Smuzhiyun 	{ KE_END, 0 }
206*4882a593Smuzhiyun };
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun struct pcc_acpi {
209*4882a593Smuzhiyun 	acpi_handle		handle;
210*4882a593Smuzhiyun 	unsigned long		num_sifr;
211*4882a593Smuzhiyun 	int			sticky_mode;
212*4882a593Smuzhiyun 	u32			*sinf;
213*4882a593Smuzhiyun 	struct acpi_device	*device;
214*4882a593Smuzhiyun 	struct input_dev	*input_dev;
215*4882a593Smuzhiyun 	struct backlight_device	*backlight;
216*4882a593Smuzhiyun };
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun /* method access functions */
acpi_pcc_write_sset(struct pcc_acpi * pcc,int func,int val)219*4882a593Smuzhiyun static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun 	union acpi_object in_objs[] = {
222*4882a593Smuzhiyun 		{ .integer.type  = ACPI_TYPE_INTEGER,
223*4882a593Smuzhiyun 		  .integer.value = func, },
224*4882a593Smuzhiyun 		{ .integer.type  = ACPI_TYPE_INTEGER,
225*4882a593Smuzhiyun 		  .integer.value = val, },
226*4882a593Smuzhiyun 	};
227*4882a593Smuzhiyun 	struct acpi_object_list params = {
228*4882a593Smuzhiyun 		.count   = ARRAY_SIZE(in_objs),
229*4882a593Smuzhiyun 		.pointer = in_objs,
230*4882a593Smuzhiyun 	};
231*4882a593Smuzhiyun 	acpi_status status = AE_OK;
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET,
234*4882a593Smuzhiyun 				      &params, NULL);
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	return (status == AE_OK) ? 0 : -EIO;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun 
acpi_pcc_get_sqty(struct acpi_device * device)239*4882a593Smuzhiyun static inline int acpi_pcc_get_sqty(struct acpi_device *device)
240*4882a593Smuzhiyun {
241*4882a593Smuzhiyun 	unsigned long long s;
242*4882a593Smuzhiyun 	acpi_status status;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,
245*4882a593Smuzhiyun 				       NULL, &s);
246*4882a593Smuzhiyun 	if (ACPI_SUCCESS(status))
247*4882a593Smuzhiyun 		return s;
248*4882a593Smuzhiyun 	else {
249*4882a593Smuzhiyun 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
250*4882a593Smuzhiyun 				  "evaluation error HKEY.SQTY\n"));
251*4882a593Smuzhiyun 		return -EINVAL;
252*4882a593Smuzhiyun 	}
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun 
acpi_pcc_retrieve_biosdata(struct pcc_acpi * pcc)255*4882a593Smuzhiyun static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc)
256*4882a593Smuzhiyun {
257*4882a593Smuzhiyun 	acpi_status status;
258*4882a593Smuzhiyun 	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
259*4882a593Smuzhiyun 	union acpi_object *hkey = NULL;
260*4882a593Smuzhiyun 	int i;
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, NULL,
263*4882a593Smuzhiyun 				      &buffer);
264*4882a593Smuzhiyun 	if (ACPI_FAILURE(status)) {
265*4882a593Smuzhiyun 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
266*4882a593Smuzhiyun 				  "evaluation error HKEY.SINF\n"));
267*4882a593Smuzhiyun 		return 0;
268*4882a593Smuzhiyun 	}
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	hkey = buffer.pointer;
271*4882a593Smuzhiyun 	if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
272*4882a593Smuzhiyun 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
273*4882a593Smuzhiyun 		status = AE_ERROR;
274*4882a593Smuzhiyun 		goto end;
275*4882a593Smuzhiyun 	}
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	if (pcc->num_sifr < hkey->package.count) {
278*4882a593Smuzhiyun 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
279*4882a593Smuzhiyun 				 "SQTY reports bad SINF length\n"));
280*4882a593Smuzhiyun 		status = AE_ERROR;
281*4882a593Smuzhiyun 		goto end;
282*4882a593Smuzhiyun 	}
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	for (i = 0; i < hkey->package.count; i++) {
285*4882a593Smuzhiyun 		union acpi_object *element = &(hkey->package.elements[i]);
286*4882a593Smuzhiyun 		if (likely(element->type == ACPI_TYPE_INTEGER)) {
287*4882a593Smuzhiyun 			pcc->sinf[i] = element->integer.value;
288*4882a593Smuzhiyun 		} else
289*4882a593Smuzhiyun 			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
290*4882a593Smuzhiyun 					 "Invalid HKEY.SINF data\n"));
291*4882a593Smuzhiyun 	}
292*4882a593Smuzhiyun 	pcc->sinf[hkey->package.count] = -1;
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun end:
295*4882a593Smuzhiyun 	kfree(buffer.pointer);
296*4882a593Smuzhiyun 	return status == AE_OK;
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun /* backlight API interface functions */
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun /* This driver currently treats AC and DC brightness identical,
302*4882a593Smuzhiyun  * since we don't need to invent an interface to the core ACPI
303*4882a593Smuzhiyun  * logic to receive events in case a power supply is plugged in
304*4882a593Smuzhiyun  * or removed */
305*4882a593Smuzhiyun 
bl_get(struct backlight_device * bd)306*4882a593Smuzhiyun static int bl_get(struct backlight_device *bd)
307*4882a593Smuzhiyun {
308*4882a593Smuzhiyun 	struct pcc_acpi *pcc = bl_get_data(bd);
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	if (!acpi_pcc_retrieve_biosdata(pcc))
311*4882a593Smuzhiyun 		return -EIO;
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	return pcc->sinf[SINF_AC_CUR_BRIGHT];
314*4882a593Smuzhiyun }
315*4882a593Smuzhiyun 
bl_set_status(struct backlight_device * bd)316*4882a593Smuzhiyun static int bl_set_status(struct backlight_device *bd)
317*4882a593Smuzhiyun {
318*4882a593Smuzhiyun 	struct pcc_acpi *pcc = bl_get_data(bd);
319*4882a593Smuzhiyun 	int bright = bd->props.brightness;
320*4882a593Smuzhiyun 	int rc;
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	if (!acpi_pcc_retrieve_biosdata(pcc))
323*4882a593Smuzhiyun 		return -EIO;
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun 	if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
326*4882a593Smuzhiyun 		bright = pcc->sinf[SINF_AC_MIN_BRIGHT];
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 	if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT])
329*4882a593Smuzhiyun 		bright = pcc->sinf[SINF_DC_MIN_BRIGHT];
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] ||
332*4882a593Smuzhiyun 	    bright > pcc->sinf[SINF_AC_MAX_BRIGHT])
333*4882a593Smuzhiyun 		return -EINVAL;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright);
336*4882a593Smuzhiyun 	if (rc < 0)
337*4882a593Smuzhiyun 		return rc;
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright);
340*4882a593Smuzhiyun }
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun static const struct backlight_ops pcc_backlight_ops = {
343*4882a593Smuzhiyun 	.get_brightness	= bl_get,
344*4882a593Smuzhiyun 	.update_status	= bl_set_status,
345*4882a593Smuzhiyun };
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun /* sysfs user interface functions */
349*4882a593Smuzhiyun 
show_numbatt(struct device * dev,struct device_attribute * attr,char * buf)350*4882a593Smuzhiyun static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
351*4882a593Smuzhiyun 			    char *buf)
352*4882a593Smuzhiyun {
353*4882a593Smuzhiyun 	struct acpi_device *acpi = to_acpi_device(dev);
354*4882a593Smuzhiyun 	struct pcc_acpi *pcc = acpi_driver_data(acpi);
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	if (!acpi_pcc_retrieve_biosdata(pcc))
357*4882a593Smuzhiyun 		return -EIO;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun 
show_lcdtype(struct device * dev,struct device_attribute * attr,char * buf)362*4882a593Smuzhiyun static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
363*4882a593Smuzhiyun 			    char *buf)
364*4882a593Smuzhiyun {
365*4882a593Smuzhiyun 	struct acpi_device *acpi = to_acpi_device(dev);
366*4882a593Smuzhiyun 	struct pcc_acpi *pcc = acpi_driver_data(acpi);
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	if (!acpi_pcc_retrieve_biosdata(pcc))
369*4882a593Smuzhiyun 		return -EIO;
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun 
show_mute(struct device * dev,struct device_attribute * attr,char * buf)374*4882a593Smuzhiyun static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
375*4882a593Smuzhiyun 			 char *buf)
376*4882a593Smuzhiyun {
377*4882a593Smuzhiyun 	struct acpi_device *acpi = to_acpi_device(dev);
378*4882a593Smuzhiyun 	struct pcc_acpi *pcc = acpi_driver_data(acpi);
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	if (!acpi_pcc_retrieve_biosdata(pcc))
381*4882a593Smuzhiyun 		return -EIO;
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]);
384*4882a593Smuzhiyun }
385*4882a593Smuzhiyun 
show_sticky(struct device * dev,struct device_attribute * attr,char * buf)386*4882a593Smuzhiyun static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
387*4882a593Smuzhiyun 			   char *buf)
388*4882a593Smuzhiyun {
389*4882a593Smuzhiyun 	struct acpi_device *acpi = to_acpi_device(dev);
390*4882a593Smuzhiyun 	struct pcc_acpi *pcc = acpi_driver_data(acpi);
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	if (!acpi_pcc_retrieve_biosdata(pcc))
393*4882a593Smuzhiyun 		return -EIO;
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun 
set_sticky(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)398*4882a593Smuzhiyun static ssize_t set_sticky(struct device *dev, struct device_attribute *attr,
399*4882a593Smuzhiyun 			  const char *buf, size_t count)
400*4882a593Smuzhiyun {
401*4882a593Smuzhiyun 	struct acpi_device *acpi = to_acpi_device(dev);
402*4882a593Smuzhiyun 	struct pcc_acpi *pcc = acpi_driver_data(acpi);
403*4882a593Smuzhiyun 	int val;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	if (count && sscanf(buf, "%i", &val) == 1 &&
406*4882a593Smuzhiyun 	    (val == 0 || val == 1)) {
407*4882a593Smuzhiyun 		acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val);
408*4882a593Smuzhiyun 		pcc->sticky_mode = val;
409*4882a593Smuzhiyun 	}
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	return count;
412*4882a593Smuzhiyun }
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun static DEVICE_ATTR(numbatt, S_IRUGO, show_numbatt, NULL);
415*4882a593Smuzhiyun static DEVICE_ATTR(lcdtype, S_IRUGO, show_lcdtype, NULL);
416*4882a593Smuzhiyun static DEVICE_ATTR(mute, S_IRUGO, show_mute, NULL);
417*4882a593Smuzhiyun static DEVICE_ATTR(sticky_key, S_IRUGO | S_IWUSR, show_sticky, set_sticky);
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun static struct attribute *pcc_sysfs_entries[] = {
420*4882a593Smuzhiyun 	&dev_attr_numbatt.attr,
421*4882a593Smuzhiyun 	&dev_attr_lcdtype.attr,
422*4882a593Smuzhiyun 	&dev_attr_mute.attr,
423*4882a593Smuzhiyun 	&dev_attr_sticky_key.attr,
424*4882a593Smuzhiyun 	NULL,
425*4882a593Smuzhiyun };
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun static const struct attribute_group pcc_attr_group = {
428*4882a593Smuzhiyun 	.name	= NULL,		/* put in device directory */
429*4882a593Smuzhiyun 	.attrs	= pcc_sysfs_entries,
430*4882a593Smuzhiyun };
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun /* hotkey input device driver */
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun static int sleep_keydown_seen;
acpi_pcc_generate_keyinput(struct pcc_acpi * pcc)436*4882a593Smuzhiyun static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
437*4882a593Smuzhiyun {
438*4882a593Smuzhiyun 	struct input_dev *hotk_input_dev = pcc->input_dev;
439*4882a593Smuzhiyun 	int rc;
440*4882a593Smuzhiyun 	unsigned long long result;
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
443*4882a593Smuzhiyun 				   NULL, &result);
444*4882a593Smuzhiyun 	if (ACPI_FAILURE(rc)) {
445*4882a593Smuzhiyun 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
446*4882a593Smuzhiyun 				 "error getting hotkey status\n"));
447*4882a593Smuzhiyun 		return;
448*4882a593Smuzhiyun 	}
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 	/* hack: some firmware sends no key down for sleep / hibernate */
451*4882a593Smuzhiyun 	if ((result & 0xf) == 0x7 || (result & 0xf) == 0xa) {
452*4882a593Smuzhiyun 		if (result & 0x80)
453*4882a593Smuzhiyun 			sleep_keydown_seen = 1;
454*4882a593Smuzhiyun 		if (!sleep_keydown_seen)
455*4882a593Smuzhiyun 			sparse_keymap_report_event(hotk_input_dev,
456*4882a593Smuzhiyun 					result & 0xf, 0x80, false);
457*4882a593Smuzhiyun 	}
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun 	if (!sparse_keymap_report_event(hotk_input_dev,
460*4882a593Smuzhiyun 					result & 0xf, result & 0x80, false))
461*4882a593Smuzhiyun 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
462*4882a593Smuzhiyun 				  "Unknown hotkey event: %d\n", result));
463*4882a593Smuzhiyun }
464*4882a593Smuzhiyun 
acpi_pcc_hotkey_notify(struct acpi_device * device,u32 event)465*4882a593Smuzhiyun static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)
466*4882a593Smuzhiyun {
467*4882a593Smuzhiyun 	struct pcc_acpi *pcc = acpi_driver_data(device);
468*4882a593Smuzhiyun 
469*4882a593Smuzhiyun 	switch (event) {
470*4882a593Smuzhiyun 	case HKEY_NOTIFY:
471*4882a593Smuzhiyun 		acpi_pcc_generate_keyinput(pcc);
472*4882a593Smuzhiyun 		break;
473*4882a593Smuzhiyun 	default:
474*4882a593Smuzhiyun 		/* nothing to do */
475*4882a593Smuzhiyun 		break;
476*4882a593Smuzhiyun 	}
477*4882a593Smuzhiyun }
478*4882a593Smuzhiyun 
acpi_pcc_init_input(struct pcc_acpi * pcc)479*4882a593Smuzhiyun static int acpi_pcc_init_input(struct pcc_acpi *pcc)
480*4882a593Smuzhiyun {
481*4882a593Smuzhiyun 	struct input_dev *input_dev;
482*4882a593Smuzhiyun 	int error;
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun 	input_dev = input_allocate_device();
485*4882a593Smuzhiyun 	if (!input_dev)
486*4882a593Smuzhiyun 		return -ENOMEM;
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 	input_dev->name = ACPI_PCC_DRIVER_NAME;
489*4882a593Smuzhiyun 	input_dev->phys = ACPI_PCC_INPUT_PHYS;
490*4882a593Smuzhiyun 	input_dev->id.bustype = BUS_HOST;
491*4882a593Smuzhiyun 	input_dev->id.vendor = 0x0001;
492*4882a593Smuzhiyun 	input_dev->id.product = 0x0001;
493*4882a593Smuzhiyun 	input_dev->id.version = 0x0100;
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun 	error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL);
496*4882a593Smuzhiyun 	if (error) {
497*4882a593Smuzhiyun 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
498*4882a593Smuzhiyun 				  "Unable to setup input device keymap\n"));
499*4882a593Smuzhiyun 		goto err_free_dev;
500*4882a593Smuzhiyun 	}
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	error = input_register_device(input_dev);
503*4882a593Smuzhiyun 	if (error) {
504*4882a593Smuzhiyun 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
505*4882a593Smuzhiyun 				  "Unable to register input device\n"));
506*4882a593Smuzhiyun 		goto err_free_dev;
507*4882a593Smuzhiyun 	}
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	pcc->input_dev = input_dev;
510*4882a593Smuzhiyun 	return 0;
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun  err_free_dev:
513*4882a593Smuzhiyun 	input_free_device(input_dev);
514*4882a593Smuzhiyun 	return error;
515*4882a593Smuzhiyun }
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun /* kernel module interface */
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
acpi_pcc_hotkey_resume(struct device * dev)520*4882a593Smuzhiyun static int acpi_pcc_hotkey_resume(struct device *dev)
521*4882a593Smuzhiyun {
522*4882a593Smuzhiyun 	struct pcc_acpi *pcc;
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 	if (!dev)
525*4882a593Smuzhiyun 		return -EINVAL;
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun 	pcc = acpi_driver_data(to_acpi_device(dev));
528*4882a593Smuzhiyun 	if (!pcc)
529*4882a593Smuzhiyun 		return -EINVAL;
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun 	ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n",
532*4882a593Smuzhiyun 			  pcc->sticky_mode));
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	return acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
535*4882a593Smuzhiyun }
536*4882a593Smuzhiyun #endif
537*4882a593Smuzhiyun 
acpi_pcc_hotkey_add(struct acpi_device * device)538*4882a593Smuzhiyun static int acpi_pcc_hotkey_add(struct acpi_device *device)
539*4882a593Smuzhiyun {
540*4882a593Smuzhiyun 	struct backlight_properties props;
541*4882a593Smuzhiyun 	struct pcc_acpi *pcc;
542*4882a593Smuzhiyun 	int num_sifr, result;
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun 	if (!device)
545*4882a593Smuzhiyun 		return -EINVAL;
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun 	num_sifr = acpi_pcc_get_sqty(device);
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun 	if (num_sifr < 0 || num_sifr > 255) {
550*4882a593Smuzhiyun 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr out of range"));
551*4882a593Smuzhiyun 		return -ENODEV;
552*4882a593Smuzhiyun 	}
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun 	pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL);
555*4882a593Smuzhiyun 	if (!pcc) {
556*4882a593Smuzhiyun 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
557*4882a593Smuzhiyun 				  "Couldn't allocate mem for pcc"));
558*4882a593Smuzhiyun 		return -ENOMEM;
559*4882a593Smuzhiyun 	}
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	pcc->sinf = kcalloc(num_sifr + 1, sizeof(u32), GFP_KERNEL);
562*4882a593Smuzhiyun 	if (!pcc->sinf) {
563*4882a593Smuzhiyun 		result = -ENOMEM;
564*4882a593Smuzhiyun 		goto out_hotkey;
565*4882a593Smuzhiyun 	}
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	pcc->device = device;
568*4882a593Smuzhiyun 	pcc->handle = device->handle;
569*4882a593Smuzhiyun 	pcc->num_sifr = num_sifr;
570*4882a593Smuzhiyun 	device->driver_data = pcc;
571*4882a593Smuzhiyun 	strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);
572*4882a593Smuzhiyun 	strcpy(acpi_device_class(device), ACPI_PCC_CLASS);
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	result = acpi_pcc_init_input(pcc);
575*4882a593Smuzhiyun 	if (result) {
576*4882a593Smuzhiyun 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
577*4882a593Smuzhiyun 				  "Error installing keyinput handler\n"));
578*4882a593Smuzhiyun 		goto out_sinf;
579*4882a593Smuzhiyun 	}
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	if (!acpi_pcc_retrieve_biosdata(pcc)) {
582*4882a593Smuzhiyun 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
583*4882a593Smuzhiyun 				 "Couldn't retrieve BIOS data\n"));
584*4882a593Smuzhiyun 		result = -EIO;
585*4882a593Smuzhiyun 		goto out_input;
586*4882a593Smuzhiyun 	}
587*4882a593Smuzhiyun 	/* initialize backlight */
588*4882a593Smuzhiyun 	memset(&props, 0, sizeof(struct backlight_properties));
589*4882a593Smuzhiyun 	props.type = BACKLIGHT_PLATFORM;
590*4882a593Smuzhiyun 	props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT];
591*4882a593Smuzhiyun 	pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
592*4882a593Smuzhiyun 						   &pcc_backlight_ops, &props);
593*4882a593Smuzhiyun 	if (IS_ERR(pcc->backlight)) {
594*4882a593Smuzhiyun 		result = PTR_ERR(pcc->backlight);
595*4882a593Smuzhiyun 		goto out_input;
596*4882a593Smuzhiyun 	}
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun 	/* read the initial brightness setting from the hardware */
599*4882a593Smuzhiyun 	pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	/* read the initial sticky key mode from the hardware */
602*4882a593Smuzhiyun 	pcc->sticky_mode = pcc->sinf[SINF_STICKY_KEY];
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun 	/* add sysfs attributes */
605*4882a593Smuzhiyun 	result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);
606*4882a593Smuzhiyun 	if (result)
607*4882a593Smuzhiyun 		goto out_backlight;
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun 	return 0;
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun out_backlight:
612*4882a593Smuzhiyun 	backlight_device_unregister(pcc->backlight);
613*4882a593Smuzhiyun out_input:
614*4882a593Smuzhiyun 	input_unregister_device(pcc->input_dev);
615*4882a593Smuzhiyun out_sinf:
616*4882a593Smuzhiyun 	kfree(pcc->sinf);
617*4882a593Smuzhiyun out_hotkey:
618*4882a593Smuzhiyun 	kfree(pcc);
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun 	return result;
621*4882a593Smuzhiyun }
622*4882a593Smuzhiyun 
acpi_pcc_hotkey_remove(struct acpi_device * device)623*4882a593Smuzhiyun static int acpi_pcc_hotkey_remove(struct acpi_device *device)
624*4882a593Smuzhiyun {
625*4882a593Smuzhiyun 	struct pcc_acpi *pcc = acpi_driver_data(device);
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun 	if (!device || !pcc)
628*4882a593Smuzhiyun 		return -EINVAL;
629*4882a593Smuzhiyun 
630*4882a593Smuzhiyun 	sysfs_remove_group(&device->dev.kobj, &pcc_attr_group);
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 	backlight_device_unregister(pcc->backlight);
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun 	input_unregister_device(pcc->input_dev);
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 	kfree(pcc->sinf);
637*4882a593Smuzhiyun 	kfree(pcc);
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 	return 0;
640*4882a593Smuzhiyun }
641*4882a593Smuzhiyun 
642*4882a593Smuzhiyun module_acpi_driver(acpi_pcc_driver);
643