xref: /freebsd/sys/dev/acpi_support/acpi_panasonic.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1ee6020c9SYoshihiro Takahashi /*-
2ee6020c9SYoshihiro Takahashi  * Copyright (c) 2003 OGAWA Takaya <t-ogawa@triaez.kaisei.org>
3914ee8baSYoshihiro Takahashi  * Copyright (c) 2004 TAKAHASHI Yoshihiro <nyan@FreeBSD.org>
4ee6020c9SYoshihiro Takahashi  * All rights Reserved.
5ee6020c9SYoshihiro Takahashi  *
6ee6020c9SYoshihiro Takahashi  * Redistribution and use in source and binary forms, with or without
7ee6020c9SYoshihiro Takahashi  * modification, are permitted provided that the following conditions
8ee6020c9SYoshihiro Takahashi  * are met:
9ee6020c9SYoshihiro Takahashi  * 1. Redistributions of source code must retain the above copyright
10ee6020c9SYoshihiro Takahashi  *    notice, this list of conditions and the following disclaimer.
11ee6020c9SYoshihiro Takahashi  * 2. Redistributions in binary form must reproduce the above copyright
12ee6020c9SYoshihiro Takahashi  *    notice, this list of conditions and the following disclaimer in the
13ee6020c9SYoshihiro Takahashi  *    documentation and/or other materials provided with the distribution.
14ee6020c9SYoshihiro Takahashi  *
15ee6020c9SYoshihiro Takahashi  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16ee6020c9SYoshihiro Takahashi  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17ee6020c9SYoshihiro Takahashi  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18ee6020c9SYoshihiro Takahashi  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19ee6020c9SYoshihiro Takahashi  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20ee6020c9SYoshihiro Takahashi  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21ee6020c9SYoshihiro Takahashi  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22ee6020c9SYoshihiro Takahashi  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23ee6020c9SYoshihiro Takahashi  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24ee6020c9SYoshihiro Takahashi  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25ee6020c9SYoshihiro Takahashi  * SUCH DAMAGE.
26ee6020c9SYoshihiro Takahashi  *
27ee6020c9SYoshihiro Takahashi  */
28ee6020c9SYoshihiro Takahashi 
29ee6020c9SYoshihiro Takahashi #include <sys/cdefs.h>
30ee6020c9SYoshihiro Takahashi #include "opt_acpi.h"
31ee6020c9SYoshihiro Takahashi #include <sys/param.h>
32e2e050c8SConrad Meyer #include <sys/bus.h>
33e2e050c8SConrad Meyer #include <sys/eventhandler.h>
34ee6020c9SYoshihiro Takahashi #include <sys/kernel.h>
35ee6020c9SYoshihiro Takahashi #include <sys/malloc.h>
36ee6020c9SYoshihiro Takahashi #include <sys/module.h>
37f3118682SNate Lawson #include <sys/power.h>
38ee6020c9SYoshihiro Takahashi 
39129d3046SJung-uk Kim #include <contrib/dev/acpica/include/acpi.h>
40129d3046SJung-uk Kim 
41ee6020c9SYoshihiro Takahashi #include <dev/acpica/acpivar.h>
42ee6020c9SYoshihiro Takahashi 
43276cd921SNate Lawson #define _COMPONENT	ACPI_OEM
44276cd921SNate Lawson ACPI_MODULE_NAME("Panasonic")
45276cd921SNate Lawson 
46ee6020c9SYoshihiro Takahashi /* Debug */
47ee6020c9SYoshihiro Takahashi #undef	ACPI_PANASONIC_DEBUG
48ee6020c9SYoshihiro Takahashi 
49ee6020c9SYoshihiro Takahashi /* Operations */
50ee6020c9SYoshihiro Takahashi #define	HKEY_SET	0
51ee6020c9SYoshihiro Takahashi #define	HKEY_GET	1
52ee6020c9SYoshihiro Takahashi 
53ee6020c9SYoshihiro Takahashi /* Functions */
54f3118682SNate Lawson #define	HKEY_REG_LCD_BRIGHTNESS_MAX_AC	0x02
55f3118682SNate Lawson #define	HKEY_REG_LCD_BRIGHTNESS_MIN_AC	0x03
56f3118682SNate Lawson #define	HKEY_REG_LCD_BRIGHTNESS_AC	0x04
57f3118682SNate Lawson #define	HKEY_REG_LCD_BRIGHTNESS_MAX_DC	0x05
58f3118682SNate Lawson #define	HKEY_REG_LCD_BRIGHTNESS_MIN_DC	0x06
59f3118682SNate Lawson #define	HKEY_REG_LCD_BRIGHTNESS_DC	0x07
60ee6020c9SYoshihiro Takahashi #define	HKEY_REG_SOUND_MUTE		0x08
61ee6020c9SYoshihiro Takahashi 
62ee6020c9SYoshihiro Takahashi /* Field definitions */
63ee6020c9SYoshihiro Takahashi #define	HKEY_LCD_BRIGHTNESS_BITS	4
64ee6020c9SYoshihiro Takahashi #define	HKEY_LCD_BRIGHTNESS_DIV		((1 << HKEY_LCD_BRIGHTNESS_BITS) - 1)
65ee6020c9SYoshihiro Takahashi 
66ee6020c9SYoshihiro Takahashi struct acpi_panasonic_softc {
67ee6020c9SYoshihiro Takahashi 	device_t	dev;
68ee6020c9SYoshihiro Takahashi 	ACPI_HANDLE	handle;
69ee6020c9SYoshihiro Takahashi 
70ee6020c9SYoshihiro Takahashi 	struct sysctl_ctx_list	sysctl_ctx;
71ee6020c9SYoshihiro Takahashi 	struct sysctl_oid	*sysctl_tree;
72f3118682SNate Lawson 
73f3118682SNate Lawson 	eventhandler_tag	power_evh;
74ee6020c9SYoshihiro Takahashi };
75ee6020c9SYoshihiro Takahashi 
76ee6020c9SYoshihiro Takahashi /* Prototype for HKEY functions for getting/setting a value. */
77ee6020c9SYoshihiro Takahashi typedef int hkey_fn_t(ACPI_HANDLE, int, UINT32 *);
78ee6020c9SYoshihiro Takahashi 
79ee6020c9SYoshihiro Takahashi static int	acpi_panasonic_probe(device_t dev);
80ee6020c9SYoshihiro Takahashi static int	acpi_panasonic_attach(device_t dev);
81ee6020c9SYoshihiro Takahashi static int	acpi_panasonic_detach(device_t dev);
82e3545bccSWarner Losh static int	acpi_panasonic_shutdown(device_t dev);
83ee6020c9SYoshihiro Takahashi static int	acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS);
849a179dd8SJung-uk Kim static UINT64	acpi_panasonic_sinf(ACPI_HANDLE h, UINT64 index);
859a179dd8SJung-uk Kim static void	acpi_panasonic_sset(ACPI_HANDLE h, UINT64 index,
869a179dd8SJung-uk Kim 		    UINT64 val);
87ee6020c9SYoshihiro Takahashi static int	acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc,
88ee6020c9SYoshihiro Takahashi 		    ACPI_HANDLE h, UINT32 *arg);
89ee6020c9SYoshihiro Takahashi static void	acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc,
90ee6020c9SYoshihiro Takahashi 		    ACPI_HANDLE h, UINT32 key);
91ee6020c9SYoshihiro Takahashi static void	acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify,
92ee6020c9SYoshihiro Takahashi 		    void *context);
93f3118682SNate Lawson static void	acpi_panasonic_power_profile(void *arg);
94ee6020c9SYoshihiro Takahashi 
95be223480SNate Lawson static hkey_fn_t	hkey_lcd_brightness_max;
96f3118682SNate Lawson static hkey_fn_t	hkey_lcd_brightness_min;
97be223480SNate Lawson static hkey_fn_t	hkey_lcd_brightness;
98be223480SNate Lawson static hkey_fn_t	hkey_sound_mute;
99e8a162f4SNate Lawson ACPI_SERIAL_DECL(panasonic, "ACPI Panasonic extras");
100be223480SNate Lawson 
101ee6020c9SYoshihiro Takahashi /* Table of sysctl names and HKEY functions to call. */
102ee6020c9SYoshihiro Takahashi static struct {
103ee6020c9SYoshihiro Takahashi 	char		*name;
104ee6020c9SYoshihiro Takahashi 	hkey_fn_t	*handler;
105ee6020c9SYoshihiro Takahashi } sysctl_table[] = {
106ee6020c9SYoshihiro Takahashi 	/* name,		handler */
107ee6020c9SYoshihiro Takahashi 	{"lcd_brightness_max",	hkey_lcd_brightness_max},
108f3118682SNate Lawson 	{"lcd_brightness_min",	hkey_lcd_brightness_min},
109ee6020c9SYoshihiro Takahashi 	{"lcd_brightness",	hkey_lcd_brightness},
110ee6020c9SYoshihiro Takahashi 	{"sound_mute",		hkey_sound_mute},
111ee6020c9SYoshihiro Takahashi 	{NULL, NULL}
112ee6020c9SYoshihiro Takahashi };
113ee6020c9SYoshihiro Takahashi 
114ee6020c9SYoshihiro Takahashi static device_method_t acpi_panasonic_methods[] = {
115ee6020c9SYoshihiro Takahashi 	DEVMETHOD(device_probe,		acpi_panasonic_probe),
116ee6020c9SYoshihiro Takahashi 	DEVMETHOD(device_attach,	acpi_panasonic_attach),
117ee6020c9SYoshihiro Takahashi 	DEVMETHOD(device_detach,	acpi_panasonic_detach),
118bb05b80cSNate Lawson 	DEVMETHOD(device_shutdown,	acpi_panasonic_shutdown),
119ee6020c9SYoshihiro Takahashi 
12061bfd867SSofian Brabez 	DEVMETHOD_END
121ee6020c9SYoshihiro Takahashi };
122ee6020c9SYoshihiro Takahashi 
123ee6020c9SYoshihiro Takahashi static driver_t acpi_panasonic_driver = {
124ee6020c9SYoshihiro Takahashi 	"acpi_panasonic",
125ee6020c9SYoshihiro Takahashi 	acpi_panasonic_methods,
126ee6020c9SYoshihiro Takahashi 	sizeof(struct acpi_panasonic_softc),
127ee6020c9SYoshihiro Takahashi };
128ee6020c9SYoshihiro Takahashi 
129*90161e72SJohn Baldwin DRIVER_MODULE(acpi_panasonic, acpi, acpi_panasonic_driver, 0, 0);
130ee6020c9SYoshihiro Takahashi MODULE_DEPEND(acpi_panasonic, acpi, 1, 1, 1);
131ee6020c9SYoshihiro Takahashi 
132ee6020c9SYoshihiro Takahashi static int
acpi_panasonic_probe(device_t dev)133ee6020c9SYoshihiro Takahashi acpi_panasonic_probe(device_t dev)
134ee6020c9SYoshihiro Takahashi {
135ee6020c9SYoshihiro Takahashi 	static char *mat_ids[] = { "MAT0019", NULL };
1365efca36fSTakanori Watanabe 	int rv;
137ee6020c9SYoshihiro Takahashi 
138ee6020c9SYoshihiro Takahashi 	if (acpi_disabled("panasonic") ||
139ee6020c9SYoshihiro Takahashi 	    device_get_unit(dev) != 0)
140ee6020c9SYoshihiro Takahashi 		return (ENXIO);
1415efca36fSTakanori Watanabe 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, mat_ids, NULL);
142ee6020c9SYoshihiro Takahashi 
1435efca36fSTakanori Watanabe 	if (rv <= 0)
144ee6020c9SYoshihiro Takahashi 		device_set_desc(dev, "Panasonic Notebook Hotkeys");
1455efca36fSTakanori Watanabe 	return (rv);
146ee6020c9SYoshihiro Takahashi }
147ee6020c9SYoshihiro Takahashi 
148ee6020c9SYoshihiro Takahashi static int
acpi_panasonic_attach(device_t dev)149ee6020c9SYoshihiro Takahashi acpi_panasonic_attach(device_t dev)
150ee6020c9SYoshihiro Takahashi {
151ee6020c9SYoshihiro Takahashi 	struct acpi_panasonic_softc *sc;
152ee6020c9SYoshihiro Takahashi 	struct acpi_softc *acpi_sc;
153ee6020c9SYoshihiro Takahashi 	ACPI_STATUS status;
154ee6020c9SYoshihiro Takahashi 	int i;
155ee6020c9SYoshihiro Takahashi 
156ee6020c9SYoshihiro Takahashi 	sc = device_get_softc(dev);
157ee6020c9SYoshihiro Takahashi 	sc->dev = dev;
158ee6020c9SYoshihiro Takahashi 	sc->handle = acpi_get_handle(dev);
159ee6020c9SYoshihiro Takahashi 
160ee6020c9SYoshihiro Takahashi 	acpi_sc = acpi_device_get_parent_softc(dev);
161ee6020c9SYoshihiro Takahashi 
162ee6020c9SYoshihiro Takahashi 	/* Build sysctl tree */
163ee6020c9SYoshihiro Takahashi 	sysctl_ctx_init(&sc->sysctl_ctx);
164ee6020c9SYoshihiro Takahashi 	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
165ee6020c9SYoshihiro Takahashi 	    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
1667029da5cSPawel Biernacki 	    "panasonic", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
167ee6020c9SYoshihiro Takahashi 	for (i = 0; sysctl_table[i].name != NULL; i++) {
168ee6020c9SYoshihiro Takahashi 		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
169ee6020c9SYoshihiro Takahashi 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
170ee6020c9SYoshihiro Takahashi 		    sysctl_table[i].name,
1717029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY |
1726237a1ccSAlexander Motin 		    CTLFLAG_MPSAFE, sc, i, acpi_panasonic_sysctl, "I", "");
173ee6020c9SYoshihiro Takahashi 	}
174ee6020c9SYoshihiro Takahashi 
175ee6020c9SYoshihiro Takahashi #if 0
176ee6020c9SYoshihiro Takahashi 	/* Activate hotkeys */
177ee6020c9SYoshihiro Takahashi 	status = AcpiEvaluateObject(sc->handle, "", NULL, NULL);
178ee6020c9SYoshihiro Takahashi 	if (ACPI_FAILURE(status)) {
179ee6020c9SYoshihiro Takahashi 		device_printf(dev, "enable FN keys failed\n");
180ee6020c9SYoshihiro Takahashi 		sysctl_ctx_free(&sc->sysctl_ctx);
181ee6020c9SYoshihiro Takahashi 		return (ENXIO);
182ee6020c9SYoshihiro Takahashi 	}
183ee6020c9SYoshihiro Takahashi #endif
184ee6020c9SYoshihiro Takahashi 
185ee6020c9SYoshihiro Takahashi 	/* Handle notifies */
186ee6020c9SYoshihiro Takahashi 	status = AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
187ee6020c9SYoshihiro Takahashi 	    acpi_panasonic_notify, sc);
188ee6020c9SYoshihiro Takahashi 	if (ACPI_FAILURE(status)) {
189ee6020c9SYoshihiro Takahashi 		device_printf(dev, "couldn't install notify handler - %s\n",
190ee6020c9SYoshihiro Takahashi 		    AcpiFormatException(status));
191ee6020c9SYoshihiro Takahashi 		sysctl_ctx_free(&sc->sysctl_ctx);
192ee6020c9SYoshihiro Takahashi 		return (ENXIO);
193ee6020c9SYoshihiro Takahashi 	}
194ee6020c9SYoshihiro Takahashi 
195f3118682SNate Lawson 	/* Install power profile event handler */
196f3118682SNate Lawson 	sc->power_evh = EVENTHANDLER_REGISTER(power_profile_change,
197f3118682SNate Lawson 	    acpi_panasonic_power_profile, sc->handle, 0);
198f3118682SNate Lawson 
199ee6020c9SYoshihiro Takahashi 	return (0);
200ee6020c9SYoshihiro Takahashi }
201ee6020c9SYoshihiro Takahashi 
202ee6020c9SYoshihiro Takahashi static int
acpi_panasonic_detach(device_t dev)203ee6020c9SYoshihiro Takahashi acpi_panasonic_detach(device_t dev)
204ee6020c9SYoshihiro Takahashi {
205ee6020c9SYoshihiro Takahashi 	struct acpi_panasonic_softc *sc;
206ee6020c9SYoshihiro Takahashi 
207ee6020c9SYoshihiro Takahashi 	sc = device_get_softc(dev);
208ee6020c9SYoshihiro Takahashi 
209f3118682SNate Lawson 	/* Remove power profile event handler */
210f3118682SNate Lawson 	EVENTHANDLER_DEREGISTER(power_profile_change, sc->power_evh);
211f3118682SNate Lawson 
212ee6020c9SYoshihiro Takahashi 	/* Remove notify handler */
213ee6020c9SYoshihiro Takahashi 	AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
214ee6020c9SYoshihiro Takahashi 	    acpi_panasonic_notify);
215ee6020c9SYoshihiro Takahashi 
216ee6020c9SYoshihiro Takahashi 	/* Free sysctl tree */
217ee6020c9SYoshihiro Takahashi 	sysctl_ctx_free(&sc->sysctl_ctx);
218ee6020c9SYoshihiro Takahashi 
219ee6020c9SYoshihiro Takahashi 	return (0);
220ee6020c9SYoshihiro Takahashi }
221ee6020c9SYoshihiro Takahashi 
222e3545bccSWarner Losh static int
acpi_panasonic_shutdown(device_t dev)223bb05b80cSNate Lawson acpi_panasonic_shutdown(device_t dev)
224bb05b80cSNate Lawson {
225bb05b80cSNate Lawson 	struct acpi_panasonic_softc *sc;
226bb05b80cSNate Lawson 	int mute;
227bb05b80cSNate Lawson 
228bb05b80cSNate Lawson 	/* Mute the main audio during reboot to prevent static burst to speaker. */
229bb05b80cSNate Lawson 	sc = device_get_softc(dev);
230bb05b80cSNate Lawson 	mute = 1;
231bb05b80cSNate Lawson 	hkey_sound_mute(sc->handle, HKEY_SET, &mute);
232e3545bccSWarner Losh 	return (0);
233bb05b80cSNate Lawson }
234bb05b80cSNate Lawson 
235ee6020c9SYoshihiro Takahashi static int
acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS)236ee6020c9SYoshihiro Takahashi acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS)
237ee6020c9SYoshihiro Takahashi {
238ee6020c9SYoshihiro Takahashi 	struct acpi_panasonic_softc *sc;
239ee6020c9SYoshihiro Takahashi 	UINT32 arg;
240ee6020c9SYoshihiro Takahashi 	int function, error;
241ee6020c9SYoshihiro Takahashi 	hkey_fn_t *handler;
242ee6020c9SYoshihiro Takahashi 
243ee6020c9SYoshihiro Takahashi 	sc = (struct acpi_panasonic_softc *)oidp->oid_arg1;
244ee6020c9SYoshihiro Takahashi 	function = oidp->oid_arg2;
245ee6020c9SYoshihiro Takahashi 	handler = sysctl_table[function].handler;
246ee6020c9SYoshihiro Takahashi 
247ee6020c9SYoshihiro Takahashi 	/* Get the current value from the appropriate function. */
248e8a162f4SNate Lawson 	ACPI_SERIAL_BEGIN(panasonic);
249ee6020c9SYoshihiro Takahashi 	error = handler(sc->handle, HKEY_GET, &arg);
250ee6020c9SYoshihiro Takahashi 	if (error != 0)
251e8a162f4SNate Lawson 		goto out;
252ee6020c9SYoshihiro Takahashi 
253ee6020c9SYoshihiro Takahashi 	/* Send the current value to the user and return if no new value. */
254ee6020c9SYoshihiro Takahashi 	error = sysctl_handle_int(oidp, &arg, 0, req);
255ee6020c9SYoshihiro Takahashi 	if (error != 0 || req->newptr == NULL)
256e8a162f4SNate Lawson 		goto out;
257ee6020c9SYoshihiro Takahashi 
258ee6020c9SYoshihiro Takahashi 	/* Set the new value via the appropriate function. */
259ee6020c9SYoshihiro Takahashi 	error = handler(sc->handle, HKEY_SET, &arg);
260ee6020c9SYoshihiro Takahashi 
261e8a162f4SNate Lawson out:
2629f65aa03SNate Lawson 	ACPI_SERIAL_END(panasonic);
263ee6020c9SYoshihiro Takahashi 	return (error);
264ee6020c9SYoshihiro Takahashi }
265ee6020c9SYoshihiro Takahashi 
2669a179dd8SJung-uk Kim static UINT64
acpi_panasonic_sinf(ACPI_HANDLE h,UINT64 index)2679a179dd8SJung-uk Kim acpi_panasonic_sinf(ACPI_HANDLE h, UINT64 index)
268ee6020c9SYoshihiro Takahashi {
269ee6020c9SYoshihiro Takahashi 	ACPI_BUFFER buf;
270ee6020c9SYoshihiro Takahashi 	ACPI_OBJECT *res;
2719a179dd8SJung-uk Kim 	UINT64 ret;
272ee6020c9SYoshihiro Takahashi 
273e8a162f4SNate Lawson 	ACPI_SERIAL_ASSERT(panasonic);
274ee6020c9SYoshihiro Takahashi 	ret = -1;
275ee6020c9SYoshihiro Takahashi 	buf.Length = ACPI_ALLOCATE_BUFFER;
276ee6020c9SYoshihiro Takahashi 	buf.Pointer = NULL;
277ee6020c9SYoshihiro Takahashi 	AcpiEvaluateObject(h, "SINF", NULL, &buf);
278ee6020c9SYoshihiro Takahashi 	res = (ACPI_OBJECT *)buf.Pointer;
279ee6020c9SYoshihiro Takahashi 	if (res->Type == ACPI_TYPE_PACKAGE)
280ee6020c9SYoshihiro Takahashi 		ret = res->Package.Elements[index].Integer.Value;
281ee6020c9SYoshihiro Takahashi 	AcpiOsFree(buf.Pointer);
282ee6020c9SYoshihiro Takahashi 
283ee6020c9SYoshihiro Takahashi 	return (ret);
284ee6020c9SYoshihiro Takahashi }
285ee6020c9SYoshihiro Takahashi 
286ee6020c9SYoshihiro Takahashi static void
acpi_panasonic_sset(ACPI_HANDLE h,UINT64 index,UINT64 val)2879a179dd8SJung-uk Kim acpi_panasonic_sset(ACPI_HANDLE h, UINT64 index, UINT64 val)
288ee6020c9SYoshihiro Takahashi {
289ee6020c9SYoshihiro Takahashi 	ACPI_OBJECT_LIST args;
290ee6020c9SYoshihiro Takahashi 	ACPI_OBJECT obj[2];
291ee6020c9SYoshihiro Takahashi 
292e8a162f4SNate Lawson 	ACPI_SERIAL_ASSERT(panasonic);
293ee6020c9SYoshihiro Takahashi 	obj[0].Type = ACPI_TYPE_INTEGER;
294ee6020c9SYoshihiro Takahashi 	obj[0].Integer.Value = index;
295ee6020c9SYoshihiro Takahashi 	obj[1].Type = ACPI_TYPE_INTEGER;
296ee6020c9SYoshihiro Takahashi 	obj[1].Integer.Value = val;
297ee6020c9SYoshihiro Takahashi 	args.Count = 2;
298ee6020c9SYoshihiro Takahashi 	args.Pointer = obj;
299ee6020c9SYoshihiro Takahashi 	AcpiEvaluateObject(h, "SSET", &args, NULL);
300ee6020c9SYoshihiro Takahashi }
301ee6020c9SYoshihiro Takahashi 
302ee6020c9SYoshihiro Takahashi static int
hkey_lcd_brightness_max(ACPI_HANDLE h,int op,UINT32 * val)303ee6020c9SYoshihiro Takahashi hkey_lcd_brightness_max(ACPI_HANDLE h, int op, UINT32 *val)
304ee6020c9SYoshihiro Takahashi {
305f3118682SNate Lawson 	int reg;
306ee6020c9SYoshihiro Takahashi 
307e8a162f4SNate Lawson 	ACPI_SERIAL_ASSERT(panasonic);
308f3118682SNate Lawson 	reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?
309f3118682SNate Lawson 	    HKEY_REG_LCD_BRIGHTNESS_MAX_AC : HKEY_REG_LCD_BRIGHTNESS_MAX_DC;
310f3118682SNate Lawson 
311ee6020c9SYoshihiro Takahashi 	switch (op) {
312ee6020c9SYoshihiro Takahashi 	case HKEY_SET:
313f3118682SNate Lawson 		return (EPERM);
314ee6020c9SYoshihiro Takahashi 		break;
315ee6020c9SYoshihiro Takahashi 	case HKEY_GET:
316f3118682SNate Lawson 		*val = acpi_panasonic_sinf(h, reg);
317f3118682SNate Lawson 		break;
318f3118682SNate Lawson 	}
319f3118682SNate Lawson 
320f3118682SNate Lawson 	return (0);
321f3118682SNate Lawson }
322f3118682SNate Lawson 
323f3118682SNate Lawson static int
hkey_lcd_brightness_min(ACPI_HANDLE h,int op,UINT32 * val)324f3118682SNate Lawson hkey_lcd_brightness_min(ACPI_HANDLE h, int op, UINT32 *val)
325f3118682SNate Lawson {
326f3118682SNate Lawson 	int reg;
327f3118682SNate Lawson 
328f3118682SNate Lawson 	ACPI_SERIAL_ASSERT(panasonic);
329f3118682SNate Lawson 	reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?
330f3118682SNate Lawson 	    HKEY_REG_LCD_BRIGHTNESS_MIN_AC : HKEY_REG_LCD_BRIGHTNESS_MIN_DC;
331f3118682SNate Lawson 
332f3118682SNate Lawson 	switch (op) {
333f3118682SNate Lawson 	case HKEY_SET:
334f3118682SNate Lawson 		return (EPERM);
335f3118682SNate Lawson 		break;
336f3118682SNate Lawson 	case HKEY_GET:
337f3118682SNate Lawson 		*val = acpi_panasonic_sinf(h, reg);
338ee6020c9SYoshihiro Takahashi 		break;
339ee6020c9SYoshihiro Takahashi 	}
340ee6020c9SYoshihiro Takahashi 
341ee6020c9SYoshihiro Takahashi 	return (0);
342ee6020c9SYoshihiro Takahashi }
343ee6020c9SYoshihiro Takahashi 
344ee6020c9SYoshihiro Takahashi static int
hkey_lcd_brightness(ACPI_HANDLE h,int op,UINT32 * val)345ee6020c9SYoshihiro Takahashi hkey_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *val)
346ee6020c9SYoshihiro Takahashi {
347f3118682SNate Lawson 	int reg;
348f3118682SNate Lawson 	UINT32 max, min;
349f3118682SNate Lawson 
350f3118682SNate Lawson 	reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?
351f3118682SNate Lawson 	    HKEY_REG_LCD_BRIGHTNESS_AC : HKEY_REG_LCD_BRIGHTNESS_DC;
352ee6020c9SYoshihiro Takahashi 
353e8a162f4SNate Lawson 	ACPI_SERIAL_ASSERT(panasonic);
354ee6020c9SYoshihiro Takahashi 	switch (op) {
355ee6020c9SYoshihiro Takahashi 	case HKEY_SET:
356f3118682SNate Lawson 		hkey_lcd_brightness_max(h, HKEY_GET, &max);
357f3118682SNate Lawson 		hkey_lcd_brightness_min(h, HKEY_GET, &min);
358f3118682SNate Lawson 		if (*val < min || *val > max)
359ee6020c9SYoshihiro Takahashi 			return (EINVAL);
360f3118682SNate Lawson 		acpi_panasonic_sset(h, reg, *val);
361ee6020c9SYoshihiro Takahashi 		break;
362ee6020c9SYoshihiro Takahashi 	case HKEY_GET:
363f3118682SNate Lawson 		*val = acpi_panasonic_sinf(h, reg);
364ee6020c9SYoshihiro Takahashi 		break;
365ee6020c9SYoshihiro Takahashi 	}
366ee6020c9SYoshihiro Takahashi 
367ee6020c9SYoshihiro Takahashi 	return (0);
368ee6020c9SYoshihiro Takahashi }
369ee6020c9SYoshihiro Takahashi 
370ee6020c9SYoshihiro Takahashi static int
hkey_sound_mute(ACPI_HANDLE h,int op,UINT32 * val)371ee6020c9SYoshihiro Takahashi hkey_sound_mute(ACPI_HANDLE h, int op, UINT32 *val)
372ee6020c9SYoshihiro Takahashi {
373ee6020c9SYoshihiro Takahashi 
374e8a162f4SNate Lawson 	ACPI_SERIAL_ASSERT(panasonic);
375ee6020c9SYoshihiro Takahashi 	switch (op) {
376ee6020c9SYoshihiro Takahashi 	case HKEY_SET:
377ee6020c9SYoshihiro Takahashi 		if (*val != 0 && *val != 1)
378ee6020c9SYoshihiro Takahashi 			return (EINVAL);
379ee6020c9SYoshihiro Takahashi 		acpi_panasonic_sset(h, HKEY_REG_SOUND_MUTE, *val);
380ee6020c9SYoshihiro Takahashi 		break;
381ee6020c9SYoshihiro Takahashi 	case HKEY_GET:
382ee6020c9SYoshihiro Takahashi 		*val = acpi_panasonic_sinf(h, HKEY_REG_SOUND_MUTE);
383ee6020c9SYoshihiro Takahashi 		break;
384ee6020c9SYoshihiro Takahashi 	}
385ee6020c9SYoshihiro Takahashi 
386ee6020c9SYoshihiro Takahashi 	return (0);
387ee6020c9SYoshihiro Takahashi }
388ee6020c9SYoshihiro Takahashi 
389ee6020c9SYoshihiro Takahashi static int
acpi_panasonic_hkey_event(struct acpi_panasonic_softc * sc,ACPI_HANDLE h,UINT32 * arg)390ee6020c9SYoshihiro Takahashi acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, ACPI_HANDLE h,
391ee6020c9SYoshihiro Takahashi     UINT32 *arg)
392ee6020c9SYoshihiro Takahashi {
393ee6020c9SYoshihiro Takahashi 	ACPI_BUFFER buf;
394ee6020c9SYoshihiro Takahashi 	ACPI_OBJECT *res;
3959a179dd8SJung-uk Kim 	UINT64 val;
396ee6020c9SYoshihiro Takahashi 	int status;
397ee6020c9SYoshihiro Takahashi 
398e8a162f4SNate Lawson 	ACPI_SERIAL_ASSERT(panasonic);
399ee6020c9SYoshihiro Takahashi 	status = ENXIO;
400ee6020c9SYoshihiro Takahashi 
401ee6020c9SYoshihiro Takahashi 	buf.Length = ACPI_ALLOCATE_BUFFER;
402ee6020c9SYoshihiro Takahashi 	buf.Pointer = NULL;
403ee6020c9SYoshihiro Takahashi 	AcpiEvaluateObject(h, "HINF", NULL, &buf);
404ee6020c9SYoshihiro Takahashi 	res = (ACPI_OBJECT *)buf.Pointer;
405ee6020c9SYoshihiro Takahashi 	if (res->Type != ACPI_TYPE_INTEGER) {
406ee6020c9SYoshihiro Takahashi 		device_printf(sc->dev, "HINF returned non-integer\n");
407ee6020c9SYoshihiro Takahashi 		goto end;
408ee6020c9SYoshihiro Takahashi 	}
409ee6020c9SYoshihiro Takahashi 	val = res->Integer.Value;
410ee6020c9SYoshihiro Takahashi #ifdef ACPI_PANASONIC_DEBUG
411ee6020c9SYoshihiro Takahashi 	device_printf(sc->dev, "%s button Fn+F%d\n",
412ee6020c9SYoshihiro Takahashi 		      (val & 0x80) ? "Pressed" : "Released",
413ee6020c9SYoshihiro Takahashi 		      (int)(val & 0x7f));
414ee6020c9SYoshihiro Takahashi #endif
415ee6020c9SYoshihiro Takahashi 	if ((val & 0x7f) > 0 && (val & 0x7f) < 11) {
416ee6020c9SYoshihiro Takahashi 		*arg = val;
417ee6020c9SYoshihiro Takahashi 		status = 0;
418ee6020c9SYoshihiro Takahashi 	}
419ee6020c9SYoshihiro Takahashi end:
420ee6020c9SYoshihiro Takahashi 	if (buf.Pointer)
421ee6020c9SYoshihiro Takahashi 		AcpiOsFree(buf.Pointer);
422ee6020c9SYoshihiro Takahashi 
423ee6020c9SYoshihiro Takahashi 	return (status);
424ee6020c9SYoshihiro Takahashi }
425ee6020c9SYoshihiro Takahashi 
426ee6020c9SYoshihiro Takahashi static void
acpi_panasonic_hkey_action(struct acpi_panasonic_softc * sc,ACPI_HANDLE h,UINT32 key)427ee6020c9SYoshihiro Takahashi acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, ACPI_HANDLE h,
428ee6020c9SYoshihiro Takahashi     UINT32 key)
429ee6020c9SYoshihiro Takahashi {
43014ecccdaSYoshihiro Takahashi 	struct acpi_softc *acpi_sc;
431f3118682SNate Lawson 	int arg, max, min;
432ee6020c9SYoshihiro Takahashi 
43314ecccdaSYoshihiro Takahashi 	acpi_sc = acpi_device_get_parent_softc(sc->dev);
43414ecccdaSYoshihiro Takahashi 
435e8a162f4SNate Lawson 	ACPI_SERIAL_ASSERT(panasonic);
436ee6020c9SYoshihiro Takahashi 	switch (key) {
437ee6020c9SYoshihiro Takahashi 	case 1:
438ee6020c9SYoshihiro Takahashi 		/* Decrease LCD brightness. */
439f3118682SNate Lawson 		hkey_lcd_brightness_max(h, HKEY_GET, &max);
440f3118682SNate Lawson 		hkey_lcd_brightness_min(h, HKEY_GET, &min);
441ee6020c9SYoshihiro Takahashi 		hkey_lcd_brightness(h, HKEY_GET, &arg);
442f3118682SNate Lawson 		arg -= max / HKEY_LCD_BRIGHTNESS_DIV;
443f3118682SNate Lawson 		if (arg < min)
444f3118682SNate Lawson 			arg = min;
445f3118682SNate Lawson 		else if (arg > max)
446f3118682SNate Lawson 			arg = max;
447ee6020c9SYoshihiro Takahashi 		hkey_lcd_brightness(h, HKEY_SET, &arg);
448ee6020c9SYoshihiro Takahashi 		break;
449ee6020c9SYoshihiro Takahashi 	case 2:
450ee6020c9SYoshihiro Takahashi 		/* Increase LCD brightness. */
451f3118682SNate Lawson 		hkey_lcd_brightness_max(h, HKEY_GET, &max);
452f3118682SNate Lawson 		hkey_lcd_brightness_min(h, HKEY_GET, &min);
453ee6020c9SYoshihiro Takahashi 		hkey_lcd_brightness(h, HKEY_GET, &arg);
454f3118682SNate Lawson 		arg += max / HKEY_LCD_BRIGHTNESS_DIV;
455f3118682SNate Lawson 		if (arg < min)
456f3118682SNate Lawson 			arg = min;
457f3118682SNate Lawson 		else if (arg > max)
458f3118682SNate Lawson 			arg = max;
459ee6020c9SYoshihiro Takahashi 		hkey_lcd_brightness(h, HKEY_SET, &arg);
460ee6020c9SYoshihiro Takahashi 		break;
461ee6020c9SYoshihiro Takahashi 	case 4:
462ee6020c9SYoshihiro Takahashi 		/* Toggle sound mute. */
463ee6020c9SYoshihiro Takahashi 		hkey_sound_mute(h, HKEY_GET, &arg);
464ee6020c9SYoshihiro Takahashi 		if (arg)
465ee6020c9SYoshihiro Takahashi 			arg = 0;
466ee6020c9SYoshihiro Takahashi 		else
467ee6020c9SYoshihiro Takahashi 			arg = 1;
468ee6020c9SYoshihiro Takahashi 		hkey_sound_mute(h, HKEY_SET, &arg);
469ee6020c9SYoshihiro Takahashi 		break;
47014ecccdaSYoshihiro Takahashi 	case 7:
47114ecccdaSYoshihiro Takahashi 		/* Suspend. */
472d6da5e88SYoshihiro Takahashi 		acpi_event_sleep_button_sleep(acpi_sc);
47314ecccdaSYoshihiro Takahashi 		break;
474ee6020c9SYoshihiro Takahashi 	}
475ee6020c9SYoshihiro Takahashi }
476ee6020c9SYoshihiro Takahashi 
477ee6020c9SYoshihiro Takahashi static void
acpi_panasonic_notify(ACPI_HANDLE h,UINT32 notify,void * context)478ee6020c9SYoshihiro Takahashi acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, void *context)
479ee6020c9SYoshihiro Takahashi {
480ee6020c9SYoshihiro Takahashi 	struct acpi_panasonic_softc *sc;
4816eb9bbf3SMatt Jacob 	UINT32 key = 0;
482ee6020c9SYoshihiro Takahashi 
483ee6020c9SYoshihiro Takahashi 	sc = (struct acpi_panasonic_softc *)context;
484ee6020c9SYoshihiro Takahashi 
485ee6020c9SYoshihiro Takahashi 	switch (notify) {
486ee6020c9SYoshihiro Takahashi 	case 0x80:
487e8a162f4SNate Lawson 		ACPI_SERIAL_BEGIN(panasonic);
488ee6020c9SYoshihiro Takahashi 		if (acpi_panasonic_hkey_event(sc, h, &key) == 0) {
489ee6020c9SYoshihiro Takahashi 			acpi_panasonic_hkey_action(sc, h, key);
490ee6020c9SYoshihiro Takahashi 			acpi_UserNotify("Panasonic", h, (uint8_t)key);
491ee6020c9SYoshihiro Takahashi 		}
492e8a162f4SNate Lawson 		ACPI_SERIAL_END(panasonic);
493ee6020c9SYoshihiro Takahashi 		break;
494a8fefd0eSShunsuke Akiyama 	case 0x81:
495a8fefd0eSShunsuke Akiyama 		if (!bootverbose)
496a8fefd0eSShunsuke Akiyama 			break;
497a8fefd0eSShunsuke Akiyama 		/* FALLTHROUGH */
498ee6020c9SYoshihiro Takahashi 	default:
499be223480SNate Lawson 		device_printf(sc->dev, "unknown notify: %#x\n", notify);
500ee6020c9SYoshihiro Takahashi 		break;
501ee6020c9SYoshihiro Takahashi 	}
502ee6020c9SYoshihiro Takahashi }
503f3118682SNate Lawson 
504f3118682SNate Lawson static void
acpi_panasonic_power_profile(void * arg)505f3118682SNate Lawson acpi_panasonic_power_profile(void *arg)
506f3118682SNate Lawson {
507f3118682SNate Lawson 	ACPI_HANDLE handle;
508f3118682SNate Lawson 	UINT32 brightness;
509f3118682SNate Lawson 
510f3118682SNate Lawson 	handle = (ACPI_HANDLE)arg;
511f3118682SNate Lawson 
512f3118682SNate Lawson 	/* Reset current brightness according to new power state. */
513f3118682SNate Lawson 	ACPI_SERIAL_BEGIN(panasonic);
514f3118682SNate Lawson 	hkey_lcd_brightness(handle, HKEY_GET, &brightness);
515f3118682SNate Lawson 	hkey_lcd_brightness(handle, HKEY_SET, &brightness);
516f3118682SNate Lawson 	ACPI_SERIAL_END(panasonic);
517f3118682SNate Lawson }
518