1132501Snyan/*-
2132501Snyan * Copyright (c) 2003 OGAWA Takaya <t-ogawa@triaez.kaisei.org>
3146216Snyan * Copyright (c) 2004 TAKAHASHI Yoshihiro <nyan@FreeBSD.org>
4132501Snyan * All rights Reserved.
5132501Snyan *
6132501Snyan * Redistribution and use in source and binary forms, with or without
7132501Snyan * modification, are permitted provided that the following conditions
8132501Snyan * are met:
9132501Snyan * 1. Redistributions of source code must retain the above copyright
10132501Snyan *    notice, this list of conditions and the following disclaimer.
11132501Snyan * 2. Redistributions in binary form must reproduce the above copyright
12132501Snyan *    notice, this list of conditions and the following disclaimer in the
13132501Snyan *    documentation and/or other materials provided with the distribution.
14132501Snyan *
15132501Snyan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16132501Snyan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17132501Snyan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18132501Snyan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19132501Snyan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20132501Snyan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21132501Snyan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22132501Snyan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23132501Snyan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24132501Snyan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25132501Snyan * SUCH DAMAGE.
26132501Snyan *
27132501Snyan */
28132501Snyan
29132501Snyan#include <sys/cdefs.h>
30132501Snyan__FBSDID("$FreeBSD$");
31132501Snyan
32132501Snyan#include "opt_acpi.h"
33132501Snyan#include <sys/param.h>
34132501Snyan#include <sys/kernel.h>
35132501Snyan#include <sys/malloc.h>
36132501Snyan#include <sys/module.h>
37132501Snyan#include <sys/bus.h>
38137365Snjl#include <sys/power.h>
39132501Snyan
40193530Sjkim#include <contrib/dev/acpica/include/acpi.h>
41193530Sjkim
42132501Snyan#include <dev/acpica/acpivar.h>
43132501Snyan
44138825Snjl#define _COMPONENT	ACPI_OEM
45138825SnjlACPI_MODULE_NAME("Panasonic")
46138825Snjl
47132501Snyan/* Debug */
48132501Snyan#undef	ACPI_PANASONIC_DEBUG
49132501Snyan
50132501Snyan/* Operations */
51132501Snyan#define	HKEY_SET	0
52132501Snyan#define	HKEY_GET	1
53132501Snyan
54132501Snyan/* Functions */
55137365Snjl#define	HKEY_REG_LCD_BRIGHTNESS_MAX_AC	0x02
56137365Snjl#define	HKEY_REG_LCD_BRIGHTNESS_MIN_AC	0x03
57137365Snjl#define	HKEY_REG_LCD_BRIGHTNESS_AC	0x04
58137365Snjl#define	HKEY_REG_LCD_BRIGHTNESS_MAX_DC	0x05
59137365Snjl#define	HKEY_REG_LCD_BRIGHTNESS_MIN_DC	0x06
60137365Snjl#define	HKEY_REG_LCD_BRIGHTNESS_DC	0x07
61132611Snjl#define	HKEY_REG_SOUND_MUTE		0x08
62132501Snyan
63132501Snyan/* Field definitions */
64132501Snyan#define	HKEY_LCD_BRIGHTNESS_BITS	4
65132501Snyan#define	HKEY_LCD_BRIGHTNESS_DIV		((1 << HKEY_LCD_BRIGHTNESS_BITS) - 1)
66132501Snyan
67132501Snyanstruct acpi_panasonic_softc {
68132501Snyan	device_t	dev;
69132501Snyan	ACPI_HANDLE	handle;
70132501Snyan
71132501Snyan	struct sysctl_ctx_list	sysctl_ctx;
72132501Snyan	struct sysctl_oid	*sysctl_tree;
73137365Snjl
74137365Snjl	eventhandler_tag	power_evh;
75132501Snyan};
76132501Snyan
77132501Snyan/* Prototype for HKEY functions for getting/setting a value. */
78132501Snyantypedef int hkey_fn_t(ACPI_HANDLE, int, UINT32 *);
79132501Snyan
80132501Snyanstatic int	acpi_panasonic_probe(device_t dev);
81132501Snyanstatic int	acpi_panasonic_attach(device_t dev);
82132501Snyanstatic int	acpi_panasonic_detach(device_t dev);
83188162Simpstatic int	acpi_panasonic_shutdown(device_t dev);
84132501Snyanstatic int	acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS);
85202771Sjkimstatic UINT64	acpi_panasonic_sinf(ACPI_HANDLE h, UINT64 index);
86202771Sjkimstatic void	acpi_panasonic_sset(ACPI_HANDLE h, UINT64 index,
87202771Sjkim		    UINT64 val);
88132501Snyanstatic int	acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc,
89132611Snjl		    ACPI_HANDLE h, UINT32 *arg);
90132501Snyanstatic void	acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc,
91132611Snjl		    ACPI_HANDLE h, UINT32 key);
92132501Snyanstatic void	acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify,
93132611Snjl		    void *context);
94137365Snjlstatic void	acpi_panasonic_power_profile(void *arg);
95132501Snyan
96132611Snjlstatic hkey_fn_t	hkey_lcd_brightness_max;
97137365Snjlstatic hkey_fn_t	hkey_lcd_brightness_min;
98132611Snjlstatic hkey_fn_t	hkey_lcd_brightness;
99132611Snjlstatic hkey_fn_t	hkey_sound_mute;
100133629SnjlACPI_SERIAL_DECL(panasonic, "ACPI Panasonic extras");
101132611Snjl
102132501Snyan/* Table of sysctl names and HKEY functions to call. */
103132501Snyanstatic struct {
104132501Snyan	char		*name;
105132501Snyan	hkey_fn_t	*handler;
106132501Snyan} sysctl_table[] = {
107132501Snyan	/* name,		handler */
108132501Snyan	{"lcd_brightness_max",	hkey_lcd_brightness_max},
109137365Snjl	{"lcd_brightness_min",	hkey_lcd_brightness_min},
110132501Snyan	{"lcd_brightness",	hkey_lcd_brightness},
111132501Snyan	{"sound_mute",		hkey_sound_mute},
112132501Snyan	{NULL, NULL}
113132501Snyan};
114132501Snyan
115132501Snyanstatic device_method_t acpi_panasonic_methods[] = {
116132501Snyan	DEVMETHOD(device_probe,		acpi_panasonic_probe),
117132501Snyan	DEVMETHOD(device_attach,	acpi_panasonic_attach),
118132501Snyan	DEVMETHOD(device_detach,	acpi_panasonic_detach),
119170213Snjl	DEVMETHOD(device_shutdown,	acpi_panasonic_shutdown),
120132501Snyan
121246128Ssbz	DEVMETHOD_END
122132501Snyan};
123132501Snyan
124132501Snyanstatic driver_t acpi_panasonic_driver = {
125132501Snyan	"acpi_panasonic",
126132501Snyan	acpi_panasonic_methods,
127132501Snyan	sizeof(struct acpi_panasonic_softc),
128132501Snyan};
129132501Snyan
130132501Snyanstatic devclass_t acpi_panasonic_devclass;
131132501Snyan
132132501SnyanDRIVER_MODULE(acpi_panasonic, acpi, acpi_panasonic_driver,
133132611Snjl    acpi_panasonic_devclass, 0, 0);
134132501SnyanMODULE_DEPEND(acpi_panasonic, acpi, 1, 1, 1);
135132501Snyan
136132501Snyanstatic int
137132501Snyanacpi_panasonic_probe(device_t dev)
138132501Snyan{
139132501Snyan	static char *mat_ids[] = { "MAT0019", NULL };
140132501Snyan
141132501Snyan	if (acpi_disabled("panasonic") ||
142132501Snyan	    ACPI_ID_PROBE(device_get_parent(dev), dev, mat_ids) == NULL ||
143132501Snyan	    device_get_unit(dev) != 0)
144132501Snyan		return (ENXIO);
145132501Snyan
146132501Snyan	device_set_desc(dev, "Panasonic Notebook Hotkeys");
147132501Snyan	return (0);
148132501Snyan}
149132501Snyan
150132501Snyanstatic int
151132501Snyanacpi_panasonic_attach(device_t dev)
152132501Snyan{
153132501Snyan	struct acpi_panasonic_softc *sc;
154132501Snyan	struct acpi_softc *acpi_sc;
155132501Snyan	ACPI_STATUS status;
156132501Snyan	int i;
157132501Snyan
158132501Snyan	sc = device_get_softc(dev);
159132501Snyan	sc->dev = dev;
160132501Snyan	sc->handle = acpi_get_handle(dev);
161132501Snyan
162132501Snyan	acpi_sc = acpi_device_get_parent_softc(dev);
163132501Snyan
164132501Snyan	/* Build sysctl tree */
165132501Snyan	sysctl_ctx_init(&sc->sysctl_ctx);
166132501Snyan	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
167132501Snyan	    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
168132501Snyan	    "panasonic", CTLFLAG_RD, 0, "");
169132501Snyan	for (i = 0; sysctl_table[i].name != NULL; i++) {
170132501Snyan		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
171132611Snjl		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
172132611Snjl		    sysctl_table[i].name,
173132611Snjl		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
174132611Snjl		    sc, i, acpi_panasonic_sysctl, "I", "");
175132501Snyan	}
176132501Snyan
177132501Snyan#if 0
178132501Snyan	/* Activate hotkeys */
179132501Snyan	status = AcpiEvaluateObject(sc->handle, "", NULL, NULL);
180132501Snyan	if (ACPI_FAILURE(status)) {
181132501Snyan		device_printf(dev, "enable FN keys failed\n");
182132501Snyan		sysctl_ctx_free(&sc->sysctl_ctx);
183132501Snyan		return (ENXIO);
184132501Snyan	}
185132501Snyan#endif
186132501Snyan
187170213Snjl	/* Handle notifies */
188132501Snyan	status = AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
189132611Snjl	    acpi_panasonic_notify, sc);
190132501Snyan	if (ACPI_FAILURE(status)) {
191132501Snyan		device_printf(dev, "couldn't install notify handler - %s\n",
192132611Snjl		    AcpiFormatException(status));
193132501Snyan		sysctl_ctx_free(&sc->sysctl_ctx);
194132501Snyan		return (ENXIO);
195132501Snyan	}
196132501Snyan
197137365Snjl	/* Install power profile event handler */
198137365Snjl	sc->power_evh = EVENTHANDLER_REGISTER(power_profile_change,
199137365Snjl	    acpi_panasonic_power_profile, sc->handle, 0);
200137365Snjl
201132501Snyan	return (0);
202132501Snyan}
203132501Snyan
204132501Snyanstatic int
205132501Snyanacpi_panasonic_detach(device_t dev)
206132501Snyan{
207132501Snyan	struct acpi_panasonic_softc *sc;
208132501Snyan
209132501Snyan	sc = device_get_softc(dev);
210132501Snyan
211137365Snjl	/* Remove power profile event handler */
212137365Snjl	EVENTHANDLER_DEREGISTER(power_profile_change, sc->power_evh);
213137365Snjl
214132501Snyan	/* Remove notify handler */
215132501Snyan	AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
216132611Snjl	    acpi_panasonic_notify);
217132501Snyan
218132501Snyan	/* Free sysctl tree */
219132501Snyan	sysctl_ctx_free(&sc->sysctl_ctx);
220132501Snyan
221132501Snyan	return (0);
222132501Snyan}
223132501Snyan
224188162Simpstatic int
225170213Snjlacpi_panasonic_shutdown(device_t dev)
226170213Snjl{
227170213Snjl	struct acpi_panasonic_softc *sc;
228170213Snjl	int mute;
229170213Snjl
230170213Snjl	/* Mute the main audio during reboot to prevent static burst to speaker. */
231170213Snjl	sc = device_get_softc(dev);
232170213Snjl	mute = 1;
233170213Snjl	hkey_sound_mute(sc->handle, HKEY_SET, &mute);
234188162Simp	return (0);
235170213Snjl}
236170213Snjl
237132501Snyanstatic int
238132501Snyanacpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS)
239132501Snyan{
240132501Snyan	struct acpi_panasonic_softc *sc;
241132501Snyan	UINT32 arg;
242132501Snyan	int function, error;
243132501Snyan	hkey_fn_t *handler;
244132501Snyan
245132501Snyan	sc = (struct acpi_panasonic_softc *)oidp->oid_arg1;
246132501Snyan	function = oidp->oid_arg2;
247132501Snyan	handler = sysctl_table[function].handler;
248132501Snyan
249170213Snjl	/* Get the current value from the appropriate function. */
250133629Snjl	ACPI_SERIAL_BEGIN(panasonic);
251132501Snyan	error = handler(sc->handle, HKEY_GET, &arg);
252132501Snyan	if (error != 0)
253133629Snjl		goto out;
254132501Snyan
255132501Snyan	/* Send the current value to the user and return if no new value. */
256132501Snyan	error = sysctl_handle_int(oidp, &arg, 0, req);
257132501Snyan	if (error != 0 || req->newptr == NULL)
258133629Snjl		goto out;
259132501Snyan
260132501Snyan	/* Set the new value via the appropriate function. */
261132501Snyan	error = handler(sc->handle, HKEY_SET, &arg);
262132501Snyan
263133629Snjlout:
264134258Snjl	ACPI_SERIAL_END(panasonic);
265132501Snyan	return (error);
266132501Snyan}
267132501Snyan
268202771Sjkimstatic UINT64
269202771Sjkimacpi_panasonic_sinf(ACPI_HANDLE h, UINT64 index)
270132501Snyan{
271132501Snyan	ACPI_BUFFER buf;
272132501Snyan	ACPI_OBJECT *res;
273202771Sjkim	UINT64 ret;
274132501Snyan
275133629Snjl	ACPI_SERIAL_ASSERT(panasonic);
276132501Snyan	ret = -1;
277132501Snyan	buf.Length = ACPI_ALLOCATE_BUFFER;
278132501Snyan	buf.Pointer = NULL;
279132501Snyan	AcpiEvaluateObject(h, "SINF", NULL, &buf);
280132501Snyan	res = (ACPI_OBJECT *)buf.Pointer;
281132501Snyan	if (res->Type == ACPI_TYPE_PACKAGE)
282132501Snyan		ret = res->Package.Elements[index].Integer.Value;
283132501Snyan	AcpiOsFree(buf.Pointer);
284132501Snyan
285132501Snyan	return (ret);
286132501Snyan}
287132501Snyan
288132501Snyanstatic void
289202771Sjkimacpi_panasonic_sset(ACPI_HANDLE h, UINT64 index, UINT64 val)
290132501Snyan{
291132501Snyan	ACPI_OBJECT_LIST args;
292132501Snyan	ACPI_OBJECT obj[2];
293132501Snyan
294133629Snjl	ACPI_SERIAL_ASSERT(panasonic);
295132501Snyan	obj[0].Type = ACPI_TYPE_INTEGER;
296132501Snyan	obj[0].Integer.Value = index;
297132501Snyan	obj[1].Type = ACPI_TYPE_INTEGER;
298132501Snyan	obj[1].Integer.Value = val;
299132501Snyan	args.Count = 2;
300132501Snyan	args.Pointer = obj;
301132501Snyan	AcpiEvaluateObject(h, "SSET", &args, NULL);
302132501Snyan}
303132501Snyan
304132501Snyanstatic int
305132501Snyanhkey_lcd_brightness_max(ACPI_HANDLE h, int op, UINT32 *val)
306132501Snyan{
307137365Snjl	int reg;
308132501Snyan
309133629Snjl	ACPI_SERIAL_ASSERT(panasonic);
310137365Snjl	reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?
311137365Snjl	    HKEY_REG_LCD_BRIGHTNESS_MAX_AC : HKEY_REG_LCD_BRIGHTNESS_MAX_DC;
312137365Snjl
313132501Snyan	switch (op) {
314132501Snyan	case HKEY_SET:
315137365Snjl		return (EPERM);
316132501Snyan		break;
317132501Snyan	case HKEY_GET:
318137365Snjl		*val = acpi_panasonic_sinf(h, reg);
319132501Snyan		break;
320132501Snyan	}
321132501Snyan
322132501Snyan	return (0);
323132501Snyan}
324132501Snyan
325132501Snyanstatic int
326137365Snjlhkey_lcd_brightness_min(ACPI_HANDLE h, int op, UINT32 *val)
327137365Snjl{
328137365Snjl	int reg;
329137365Snjl
330137365Snjl	ACPI_SERIAL_ASSERT(panasonic);
331137365Snjl	reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?
332137365Snjl	    HKEY_REG_LCD_BRIGHTNESS_MIN_AC : HKEY_REG_LCD_BRIGHTNESS_MIN_DC;
333137365Snjl
334137365Snjl	switch (op) {
335137365Snjl	case HKEY_SET:
336137365Snjl		return (EPERM);
337137365Snjl		break;
338137365Snjl	case HKEY_GET:
339137365Snjl		*val = acpi_panasonic_sinf(h, reg);
340137365Snjl		break;
341137365Snjl	}
342137365Snjl
343137365Snjl	return (0);
344137365Snjl}
345137365Snjl
346137365Snjlstatic int
347132501Snyanhkey_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *val)
348132501Snyan{
349137365Snjl	int reg;
350137365Snjl	UINT32 max, min;
351132501Snyan
352137365Snjl	reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?
353137365Snjl	    HKEY_REG_LCD_BRIGHTNESS_AC : HKEY_REG_LCD_BRIGHTNESS_DC;
354137365Snjl
355133629Snjl	ACPI_SERIAL_ASSERT(panasonic);
356132501Snyan	switch (op) {
357132501Snyan	case HKEY_SET:
358137365Snjl		hkey_lcd_brightness_max(h, HKEY_GET, &max);
359137365Snjl		hkey_lcd_brightness_min(h, HKEY_GET, &min);
360137365Snjl		if (*val < min || *val > max)
361132501Snyan			return (EINVAL);
362137365Snjl		acpi_panasonic_sset(h, reg, *val);
363132501Snyan		break;
364132501Snyan	case HKEY_GET:
365137365Snjl		*val = acpi_panasonic_sinf(h, reg);
366132501Snyan		break;
367132501Snyan	}
368132501Snyan
369132501Snyan	return (0);
370132501Snyan}
371132501Snyan
372132501Snyanstatic int
373132501Snyanhkey_sound_mute(ACPI_HANDLE h, int op, UINT32 *val)
374132501Snyan{
375132501Snyan
376133629Snjl	ACPI_SERIAL_ASSERT(panasonic);
377132501Snyan	switch (op) {
378132501Snyan	case HKEY_SET:
379132501Snyan		if (*val != 0 && *val != 1)
380132501Snyan			return (EINVAL);
381132501Snyan		acpi_panasonic_sset(h, HKEY_REG_SOUND_MUTE, *val);
382132501Snyan		break;
383132501Snyan	case HKEY_GET:
384132501Snyan		*val = acpi_panasonic_sinf(h, HKEY_REG_SOUND_MUTE);
385132501Snyan		break;
386132501Snyan	}
387132501Snyan
388132501Snyan	return (0);
389132501Snyan}
390132501Snyan
391132501Snyanstatic int
392132501Snyanacpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, ACPI_HANDLE h,
393132611Snjl    UINT32 *arg)
394132501Snyan{
395132501Snyan	ACPI_BUFFER buf;
396132501Snyan	ACPI_OBJECT *res;
397202771Sjkim	UINT64 val;
398132501Snyan	int status;
399132501Snyan
400133629Snjl	ACPI_SERIAL_ASSERT(panasonic);
401132501Snyan	status = ENXIO;
402132501Snyan
403132501Snyan	buf.Length = ACPI_ALLOCATE_BUFFER;
404132501Snyan	buf.Pointer = NULL;
405132501Snyan	AcpiEvaluateObject(h, "HINF", NULL, &buf);
406132501Snyan	res = (ACPI_OBJECT *)buf.Pointer;
407132501Snyan	if (res->Type != ACPI_TYPE_INTEGER) {
408132501Snyan		device_printf(sc->dev, "HINF returned non-integer\n");
409132501Snyan		goto end;
410132501Snyan	}
411132501Snyan	val = res->Integer.Value;
412132501Snyan#ifdef ACPI_PANASONIC_DEBUG
413132501Snyan	device_printf(sc->dev, "%s button Fn+F%d\n",
414132501Snyan		      (val & 0x80) ? "Pressed" : "Released",
415132501Snyan		      (int)(val & 0x7f));
416132501Snyan#endif
417132501Snyan	if ((val & 0x7f) > 0 && (val & 0x7f) < 11) {
418132501Snyan		*arg = val;
419132501Snyan		status = 0;
420132501Snyan	}
421132501Snyanend:
422132501Snyan	if (buf.Pointer)
423132501Snyan		AcpiOsFree(buf.Pointer);
424132501Snyan
425132501Snyan	return (status);
426132501Snyan}
427132501Snyan
428132501Snyanstatic void
429132501Snyanacpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, ACPI_HANDLE h,
430132611Snjl    UINT32 key)
431132501Snyan{
432159253Snyan	struct acpi_softc *acpi_sc;
433137365Snjl	int arg, max, min;
434132501Snyan
435159253Snyan	acpi_sc = acpi_device_get_parent_softc(sc->dev);
436159253Snyan
437133629Snjl	ACPI_SERIAL_ASSERT(panasonic);
438132501Snyan	switch (key) {
439132501Snyan	case 1:
440132501Snyan		/* Decrease LCD brightness. */
441137365Snjl		hkey_lcd_brightness_max(h, HKEY_GET, &max);
442137365Snjl		hkey_lcd_brightness_min(h, HKEY_GET, &min);
443132501Snyan		hkey_lcd_brightness(h, HKEY_GET, &arg);
444137365Snjl		arg -= max / HKEY_LCD_BRIGHTNESS_DIV;
445137365Snjl		if (arg < min)
446137365Snjl			arg = min;
447137365Snjl		else if (arg > max)
448137365Snjl			arg = max;
449132501Snyan		hkey_lcd_brightness(h, HKEY_SET, &arg);
450132501Snyan		break;
451132501Snyan	case 2:
452132501Snyan		/* Increase LCD brightness. */
453137365Snjl		hkey_lcd_brightness_max(h, HKEY_GET, &max);
454137365Snjl		hkey_lcd_brightness_min(h, HKEY_GET, &min);
455132501Snyan		hkey_lcd_brightness(h, HKEY_GET, &arg);
456137365Snjl		arg += max / HKEY_LCD_BRIGHTNESS_DIV;
457137365Snjl		if (arg < min)
458137365Snjl			arg = min;
459137365Snjl		else if (arg > max)
460137365Snjl			arg = max;
461132501Snyan		hkey_lcd_brightness(h, HKEY_SET, &arg);
462132501Snyan		break;
463132501Snyan	case 4:
464132501Snyan		/* Toggle sound mute. */
465132501Snyan		hkey_sound_mute(h, HKEY_GET, &arg);
466132501Snyan		if (arg)
467132501Snyan			arg = 0;
468132501Snyan		else
469132501Snyan			arg = 1;
470132501Snyan		hkey_sound_mute(h, HKEY_SET, &arg);
471132501Snyan		break;
472159253Snyan	case 7:
473159253Snyan		/* Suspend. */
474159347Snyan		acpi_event_sleep_button_sleep(acpi_sc);
475159253Snyan		break;
476132501Snyan	}
477132501Snyan}
478132501Snyan
479132501Snyanstatic void
480132501Snyanacpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, void *context)
481132501Snyan{
482132501Snyan	struct acpi_panasonic_softc *sc;
483170871Smjacob	UINT32 key = 0;
484132501Snyan
485132501Snyan	sc = (struct acpi_panasonic_softc *)context;
486132501Snyan
487132501Snyan	switch (notify) {
488132501Snyan	case 0x80:
489133629Snjl		ACPI_SERIAL_BEGIN(panasonic);
490132501Snyan		if (acpi_panasonic_hkey_event(sc, h, &key) == 0) {
491132501Snyan			acpi_panasonic_hkey_action(sc, h, key);
492132501Snyan			acpi_UserNotify("Panasonic", h, (uint8_t)key);
493132501Snyan		}
494133629Snjl		ACPI_SERIAL_END(panasonic);
495132501Snyan		break;
496132501Snyan	default:
497132611Snjl		device_printf(sc->dev, "unknown notify: %#x\n", notify);
498132501Snyan		break;
499132501Snyan	}
500132501Snyan}
501137365Snjl
502137365Snjlstatic void
503137365Snjlacpi_panasonic_power_profile(void *arg)
504137365Snjl{
505137365Snjl	ACPI_HANDLE handle;
506137365Snjl	UINT32 brightness;
507137365Snjl
508137365Snjl	handle = (ACPI_HANDLE)arg;
509137365Snjl
510137365Snjl	/* Reset current brightness according to new power state. */
511137365Snjl	ACPI_SERIAL_BEGIN(panasonic);
512137365Snjl	hkey_lcd_brightness(handle, HKEY_GET, &brightness);
513137365Snjl	hkey_lcd_brightness(handle, HKEY_SET, &brightness);
514137365Snjl	ACPI_SERIAL_END(panasonic);
515137365Snjl}
516