1124386Snjl/*-
2124386Snjl * Copyright (c) 2003 Hiroyuki Aizu <aizu@navi.org>
3124386Snjl * All rights reserved.
4124386Snjl *
5124386Snjl * Redistribution and use in source and binary forms, with or without
6124386Snjl * modification, are permitted provided that the following conditions
7124386Snjl * are met:
8124386Snjl * 1. Redistributions of source code must retain the above copyright
9124386Snjl *    notice, this list of conditions and the following disclaimer.
10124386Snjl * 2. Redistributions in binary form must reproduce the above copyright
11124386Snjl *    notice, this list of conditions and the following disclaimer in the
12124386Snjl *    documentation and/or other materials provided with the distribution.
13124386Snjl *
14124386Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15124386Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16124386Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17124386Snjl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18124386Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19124386Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20124386Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21124386Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22124386Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23124386Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24124386Snjl * SUCH DAMAGE.
25124386Snjl *
26124386Snjl */
27124386Snjl
28124386Snjl#include <sys/cdefs.h>
29124386Snjl__FBSDID("$FreeBSD: releng/10.3/sys/dev/acpi_support/acpi_toshiba.c 249816 2013-04-23 18:30:33Z jkim $");
30124386Snjl
31124386Snjl#include "opt_acpi.h"
32124386Snjl#include <sys/param.h>
33124386Snjl#include <sys/kernel.h>
34129882Sphk#include <sys/module.h>
35124386Snjl#include <sys/bus.h>
36124386Snjl
37193530Sjkim#include <contrib/dev/acpica/include/acpi.h>
38193530Sjkim
39124386Snjl#include <dev/acpica/acpivar.h>
40124386Snjl
41138825Snjl#define _COMPONENT	ACPI_OEM
42138825SnjlACPI_MODULE_NAME("Toshiba")
43138825Snjl
44124386Snjl/*
45124386Snjl * Toshiba HCI interface definitions
46124386Snjl *
47124386Snjl * HCI is Toshiba's "Hardware Control Interface" which is supposed to
48124386Snjl * be uniform across all their models.  Ideally we would just call
49124386Snjl * dedicated ACPI methods instead of using this primitive interface.
50124386Snjl * However, the ACPI methods seem to be incomplete in some areas (for
51124386Snjl * example they allow setting, but not reading, the LCD brightness
52124386Snjl * value), so this is still useful.
53124386Snjl */
54124386Snjl
55124386Snjl#define METHOD_HCI		"GHCI"
56124386Snjl#define METHOD_HCI_ENABLE	"ENAB"
57128207Snjl#define METHOD_VIDEO		"DSSX"
58124386Snjl
59124386Snjl/* Operations */
60124386Snjl#define HCI_SET				0xFF00
61124386Snjl#define HCI_GET				0xFE00
62124386Snjl
63124386Snjl/* Return codes */
64124386Snjl#define HCI_SUCCESS			0x0000
65124386Snjl#define HCI_FAILURE			0x1000
66124386Snjl#define HCI_NOT_SUPPORTED		0x8000
67124386Snjl#define HCI_EMPTY			0x8C00
68124386Snjl
69124386Snjl/* Functions */
70124386Snjl#define HCI_REG_LCD_BACKLIGHT		0x0002
71124386Snjl#define HCI_REG_FAN			0x0004
72124386Snjl#define HCI_REG_SYSTEM_EVENT		0x0016
73124386Snjl#define HCI_REG_VIDEO_OUTPUT		0x001C
74124386Snjl#define HCI_REG_HOTKEY_EVENT		0x001E
75124386Snjl#define HCI_REG_LCD_BRIGHTNESS		0x002A
76124386Snjl#define HCI_REG_CPU_SPEED		0x0032
77124386Snjl
78124386Snjl/* Field definitions */
79124386Snjl#define HCI_FAN_SHIFT			7
80124386Snjl#define HCI_LCD_BRIGHTNESS_BITS		3
81124386Snjl#define HCI_LCD_BRIGHTNESS_SHIFT	(16 - HCI_LCD_BRIGHTNESS_BITS)
82124386Snjl#define HCI_LCD_BRIGHTNESS_MAX		((1 << HCI_LCD_BRIGHTNESS_BITS) - 1)
83124386Snjl#define HCI_VIDEO_OUTPUT_FLAG		0x0100
84124386Snjl#define HCI_VIDEO_OUTPUT_LCD		0x1
85124386Snjl#define HCI_VIDEO_OUTPUT_CRT		0x2
86124386Snjl#define HCI_VIDEO_OUTPUT_TV		0x4
87124386Snjl#define HCI_CPU_SPEED_BITS		3
88124386Snjl#define HCI_CPU_SPEED_SHIFT		(16 - HCI_CPU_SPEED_BITS)
89124386Snjl#define HCI_CPU_SPEED_MAX		((1 << HCI_CPU_SPEED_BITS) - 1)
90124386Snjl
91124386Snjl/* Key press/release events. */
92124386Snjl#define FN_F1_PRESS	0x013B
93124386Snjl#define FN_F1_RELEASE	0x01BB
94124386Snjl#define FN_F2_PRESS	0x013C
95124386Snjl#define FN_F2_RELEASE	0x01BC
96124386Snjl#define FN_F3_PRESS	0x013D
97124386Snjl#define FN_F3_RELEASE	0x01BD
98124386Snjl#define FN_F4_PRESS	0x013E
99124386Snjl#define FN_F4_RELEASE	0x01BE
100124386Snjl#define FN_F5_PRESS	0x013F
101124386Snjl#define FN_F5_RELEASE	0x01BF
102124386Snjl#define FN_F6_PRESS	0x0140
103124386Snjl#define FN_F6_RELEASE	0x01C0
104124386Snjl#define FN_F7_PRESS	0x0141
105124386Snjl#define FN_F7_RELEASE	0x01C1
106124386Snjl#define FN_F8_PRESS	0x0142
107124386Snjl#define FN_F8_RELEASE	0x01C2
108124386Snjl#define FN_F9_PRESS	0x0143
109124386Snjl#define FN_F9_RELEASE	0x01C3
110124386Snjl#define FN_BS_PRESS	0x010E
111124386Snjl#define FN_BS_RELEASE	0x018E
112124386Snjl#define FN_ESC_PRESS	0x0101
113124386Snjl#define FN_ESC_RELEASE	0x0181
114124386Snjl#define FN_KNJ_PRESS	0x0129
115124386Snjl#define FN_KNJ_RELEASE	0x01A9
116124386Snjl
117124386Snjl/* HCI register definitions. */
118124386Snjl#define HCI_WORDS	6		/* Number of registers */
119124386Snjl#define HCI_REG_AX	0		/* Operation, then return value */
120124386Snjl#define HCI_REG_BX	1		/* Function */
121124386Snjl#define HCI_REG_CX	2		/* Argument (in or out) */
122124386Snjl#define HCI_REG_DX	3		/* Unused? */
123124386Snjl#define HCI_REG_SI	4		/* Unused? */
124124386Snjl#define HCI_REG_DI	5		/* Unused? */
125124386Snjl
126124386Snjlstruct acpi_toshiba_softc {
127124386Snjl	device_t	dev;
128124386Snjl	ACPI_HANDLE	handle;
129128207Snjl	ACPI_HANDLE	video_handle;
130124386Snjl	struct		sysctl_ctx_list sysctl_ctx;
131124386Snjl	struct		sysctl_oid *sysctl_tree;
132124386Snjl};
133124386Snjl
134124386Snjl/* Prototype for HCI functions for getting/setting a value. */
135124386Snjltypedef int	hci_fn_t(ACPI_HANDLE, int, UINT32 *);
136124386Snjl
137124386Snjlstatic int	acpi_toshiba_probe(device_t dev);
138124386Snjlstatic int	acpi_toshiba_attach(device_t dev);
139124386Snjlstatic int	acpi_toshiba_detach(device_t dev);
140124386Snjlstatic int	acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS);
141124386Snjlstatic hci_fn_t	hci_force_fan;
142124386Snjlstatic hci_fn_t	hci_video_output;
143124386Snjlstatic hci_fn_t	hci_lcd_brightness;
144124386Snjlstatic hci_fn_t	hci_lcd_backlight;
145124386Snjlstatic hci_fn_t	hci_cpu_speed;
146124386Snjlstatic int	hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg);
147128207Snjlstatic void	hci_key_action(struct acpi_toshiba_softc *sc, ACPI_HANDLE h,
148128207Snjl		    UINT32 key);
149124386Snjlstatic void	acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify,
150128207Snjl		    void *context);
151128207Snjlstatic int	acpi_toshiba_video_probe(device_t dev);
152128207Snjlstatic int	acpi_toshiba_video_attach(device_t dev);
153124386Snjl
154133630SnjlACPI_SERIAL_DECL(toshiba, "ACPI Toshiba Extras");
155133630Snjl
156124386Snjl/* Table of sysctl names and HCI functions to call. */
157124386Snjlstatic struct {
158124386Snjl	char		*name;
159124386Snjl	hci_fn_t	*handler;
160124386Snjl} sysctl_table[] = {
161124386Snjl	/* name,		handler */
162124386Snjl	{"force_fan",		hci_force_fan},
163124386Snjl	{"video_output",	hci_video_output},
164124415Snjl	{"lcd_brightness",	hci_lcd_brightness},
165124386Snjl	{"lcd_backlight",	hci_lcd_backlight},
166124415Snjl	{"cpu_speed",		hci_cpu_speed},
167124386Snjl	{NULL, NULL}
168124386Snjl};
169124386Snjl
170124386Snjlstatic device_method_t acpi_toshiba_methods[] = {
171124386Snjl	DEVMETHOD(device_probe,		acpi_toshiba_probe),
172124386Snjl	DEVMETHOD(device_attach,	acpi_toshiba_attach),
173124386Snjl	DEVMETHOD(device_detach,	acpi_toshiba_detach),
174124386Snjl
175246128Ssbz	DEVMETHOD_END
176124386Snjl};
177124386Snjl
178124386Snjlstatic driver_t acpi_toshiba_driver = {
179124386Snjl	"acpi_toshiba",
180124386Snjl	acpi_toshiba_methods,
181124386Snjl	sizeof(struct acpi_toshiba_softc),
182124386Snjl};
183124386Snjl
184124386Snjlstatic devclass_t acpi_toshiba_devclass;
185124386SnjlDRIVER_MODULE(acpi_toshiba, acpi, acpi_toshiba_driver, acpi_toshiba_devclass,
186128207Snjl    0, 0);
187128036SnjlMODULE_DEPEND(acpi_toshiba, acpi, 1, 1, 1);
188124386Snjl
189128207Snjlstatic device_method_t acpi_toshiba_video_methods[] = {
190128207Snjl	DEVMETHOD(device_probe,		acpi_toshiba_video_probe),
191128207Snjl	DEVMETHOD(device_attach,	acpi_toshiba_video_attach),
192128207Snjl
193246128Ssbz	DEVMETHOD_END
194128207Snjl};
195128207Snjl
196128207Snjlstatic driver_t acpi_toshiba_video_driver = {
197128207Snjl	"acpi_toshiba_video",
198128207Snjl	acpi_toshiba_video_methods,
199128207Snjl	0,
200128207Snjl};
201128207Snjl
202128207Snjlstatic devclass_t acpi_toshiba_video_devclass;
203128207SnjlDRIVER_MODULE(acpi_toshiba_video, acpi, acpi_toshiba_video_driver,
204128207Snjl    acpi_toshiba_video_devclass, 0, 0);
205128207SnjlMODULE_DEPEND(acpi_toshiba_video, acpi, 1, 1, 1);
206128207Snjl
207124386Snjlstatic int	enable_fn_keys = 1;
208124386SnjlTUNABLE_INT("hw.acpi.toshiba.enable_fn_keys", &enable_fn_keys);
209124386Snjl
210124386Snjl/*
211124386Snjl * HID      Model
212124386Snjl * -------------------------------------
213124386Snjl * TOS6200  Libretto L Series
214124386Snjl *          Dynabook Satellite 2455
215124386Snjl *          Dynabook SS 3500
216124386Snjl * TOS6207  Dynabook SS2110 Series
217144074Sjhb * TOS6208  SPA40
218124386Snjl */
219124386Snjlstatic int
220124386Snjlacpi_toshiba_probe(device_t dev)
221124386Snjl{
222144074Sjhb	static char *tosh_ids[] = { "TOS6200", "TOS6207", "TOS6208", NULL };
223124386Snjl
224131284Snjl	if (acpi_disabled("toshiba") ||
225131284Snjl	    ACPI_ID_PROBE(device_get_parent(dev), dev, tosh_ids) == NULL ||
226131284Snjl	    device_get_unit(dev) != 0)
227131284Snjl		return (ENXIO);
228124386Snjl
229131284Snjl	device_set_desc(dev, "Toshiba HCI Extras");
230131284Snjl	return (0);
231124386Snjl}
232124386Snjl
233124386Snjlstatic int
234124386Snjlacpi_toshiba_attach(device_t dev)
235124386Snjl{
236124386Snjl	struct		acpi_toshiba_softc *sc;
237124386Snjl	struct		acpi_softc *acpi_sc;
238124386Snjl	ACPI_STATUS	status;
239124386Snjl	int		i;
240124386Snjl
241124386Snjl	sc = device_get_softc(dev);
242124386Snjl	sc->dev = dev;
243124386Snjl	sc->handle = acpi_get_handle(dev);
244124386Snjl
245124386Snjl	acpi_sc = acpi_device_get_parent_softc(dev);
246124386Snjl	sysctl_ctx_init(&sc->sysctl_ctx);
247124386Snjl	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
248124386Snjl	    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
249124386Snjl	    "toshiba", CTLFLAG_RD, 0, "");
250124386Snjl
251124386Snjl	for (i = 0; sysctl_table[i].name != NULL; i++) {
252124386Snjl		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
253124386Snjl		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
254124386Snjl		    sysctl_table[i].name,
255124386Snjl		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
256124386Snjl		    sc, i, acpi_toshiba_sysctl, "I", "");
257124386Snjl	}
258124386Snjl
259124386Snjl	if (enable_fn_keys != 0) {
260124386Snjl		status = AcpiEvaluateObject(sc->handle, METHOD_HCI_ENABLE,
261124386Snjl					    NULL, NULL);
262124386Snjl		if (ACPI_FAILURE(status)) {
263124386Snjl			device_printf(dev, "enable FN keys failed\n");
264124386Snjl			sysctl_ctx_free(&sc->sysctl_ctx);
265124386Snjl			return (ENXIO);
266124386Snjl		}
267124386Snjl		AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
268124386Snjl					 acpi_toshiba_notify, sc);
269124386Snjl	}
270124386Snjl
271124386Snjl	return (0);
272124386Snjl}
273124386Snjl
274124386Snjlstatic int
275124386Snjlacpi_toshiba_detach(device_t dev)
276124386Snjl{
277124386Snjl	struct		acpi_toshiba_softc *sc;
278124386Snjl
279124386Snjl	sc = device_get_softc(dev);
280124442Snjl	if (enable_fn_keys != 0) {
281124442Snjl		AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
282124442Snjl					acpi_toshiba_notify);
283124442Snjl	}
284124386Snjl	sysctl_ctx_free(&sc->sysctl_ctx);
285124386Snjl
286124386Snjl	return (0);
287124386Snjl}
288124386Snjl
289124386Snjlstatic int
290124386Snjlacpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS)
291124386Snjl{
292124386Snjl	struct		acpi_toshiba_softc *sc;
293124386Snjl	UINT32		arg;
294124386Snjl	int		function, error = 0;
295124386Snjl	hci_fn_t	*handler;
296124386Snjl
297124386Snjl	sc = (struct acpi_toshiba_softc *)oidp->oid_arg1;
298124386Snjl	function = oidp->oid_arg2;
299124386Snjl	handler = sysctl_table[function].handler;
300124386Snjl
301124386Snjl	/* Get the current value from the appropriate function. */
302133630Snjl	ACPI_SERIAL_BEGIN(toshiba);
303124386Snjl	error = handler(sc->handle, HCI_GET, &arg);
304124386Snjl	if (error != 0)
305133630Snjl		goto out;
306124386Snjl
307124386Snjl	/* Send the current value to the user and return if no new value. */
308124386Snjl	error = sysctl_handle_int(oidp, &arg, 0, req);
309124386Snjl	if (error != 0 || req->newptr == NULL)
310133630Snjl		goto out;
311124386Snjl
312124386Snjl	/* Set the new value via the appropriate function. */
313124386Snjl	error = handler(sc->handle, HCI_SET, &arg);
314124386Snjl
315133630Snjlout:
316133630Snjl	ACPI_SERIAL_END(toshiba);
317124386Snjl	return (error);
318124386Snjl}
319124386Snjl
320124386Snjlstatic int
321124386Snjlhci_force_fan(ACPI_HANDLE h, int op, UINT32 *state)
322124386Snjl{
323124386Snjl	int		ret;
324124386Snjl
325133630Snjl	ACPI_SERIAL_ASSERT(toshiba);
326124386Snjl	if (op == HCI_SET) {
327249816Sjkim		if (*state > 1)
328124386Snjl			return (EINVAL);
329124386Snjl		*state <<= HCI_FAN_SHIFT;
330124386Snjl	}
331124386Snjl	ret = hci_call(h, op, HCI_REG_FAN, state);
332124386Snjl	if (ret == 0 && op == HCI_GET)
333124386Snjl		*state >>= HCI_FAN_SHIFT;
334124386Snjl	return (ret);
335124386Snjl}
336124386Snjl
337124386Snjlstatic int
338124386Snjlhci_video_output(ACPI_HANDLE h, int op, UINT32 *video_output)
339124386Snjl{
340124386Snjl	int		ret;
341128207Snjl	ACPI_STATUS	status;
342124386Snjl
343133630Snjl	ACPI_SERIAL_ASSERT(toshiba);
344124386Snjl	if (op == HCI_SET) {
345124386Snjl		if (*video_output < 1 || *video_output > 7)
346124386Snjl			return (EINVAL);
347128207Snjl		if (h == NULL)
348128207Snjl			return (ENXIO);
349124386Snjl		*video_output |= HCI_VIDEO_OUTPUT_FLAG;
350128223Snjl		status = acpi_SetInteger(h, METHOD_VIDEO, *video_output);
351128207Snjl		if (ACPI_SUCCESS(status))
352128207Snjl			ret = 0;
353128207Snjl		else
354128207Snjl			ret = ENXIO;
355128207Snjl	} else {
356128207Snjl		ret = hci_call(h, op, HCI_REG_VIDEO_OUTPUT, video_output);
357128207Snjl		if (ret == 0)
358128207Snjl			*video_output &= 0xff;
359124386Snjl	}
360128207Snjl
361124386Snjl	return (ret);
362124386Snjl}
363124386Snjl
364124386Snjlstatic int
365124386Snjlhci_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *brightness)
366124386Snjl{
367124386Snjl	int		ret;
368124386Snjl
369133630Snjl	ACPI_SERIAL_ASSERT(toshiba);
370124386Snjl	if (op == HCI_SET) {
371249816Sjkim		if (*brightness > HCI_LCD_BRIGHTNESS_MAX)
372124386Snjl			return (EINVAL);
373124386Snjl		*brightness <<= HCI_LCD_BRIGHTNESS_SHIFT;
374124386Snjl	}
375124386Snjl	ret = hci_call(h, op, HCI_REG_LCD_BRIGHTNESS, brightness);
376124386Snjl	if (ret == 0 && op == HCI_GET)
377124386Snjl		*brightness >>= HCI_LCD_BRIGHTNESS_SHIFT;
378124386Snjl	return (ret);
379124386Snjl}
380124386Snjl
381124386Snjlstatic int
382124386Snjlhci_lcd_backlight(ACPI_HANDLE h, int op, UINT32 *backlight)
383124386Snjl{
384133630Snjl
385133630Snjl	ACPI_SERIAL_ASSERT(toshiba);
386124386Snjl	if (op == HCI_SET) {
387249816Sjkim		if (*backlight > 1)
388124386Snjl			return (EINVAL);
389124386Snjl	}
390124386Snjl	return (hci_call(h, op, HCI_REG_LCD_BACKLIGHT, backlight));
391124386Snjl}
392124386Snjl
393124386Snjlstatic int
394124386Snjlhci_cpu_speed(ACPI_HANDLE h, int op, UINT32 *speed)
395124386Snjl{
396124386Snjl	int		ret;
397124386Snjl
398133630Snjl	ACPI_SERIAL_ASSERT(toshiba);
399124386Snjl	if (op == HCI_SET) {
400249816Sjkim		if (*speed > HCI_CPU_SPEED_MAX)
401124386Snjl			return (EINVAL);
402124386Snjl		*speed <<= HCI_CPU_SPEED_SHIFT;
403124386Snjl	}
404124386Snjl	ret = hci_call(h, op, HCI_REG_CPU_SPEED, speed);
405124386Snjl	if (ret == 0 && op == HCI_GET)
406124386Snjl		*speed >>= HCI_CPU_SPEED_SHIFT;
407124386Snjl	return (ret);
408124386Snjl}
409124386Snjl
410124386Snjlstatic int
411124386Snjlhci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg)
412124386Snjl{
413124386Snjl	ACPI_OBJECT_LIST args;
414124386Snjl	ACPI_BUFFER	results;
415124386Snjl	ACPI_OBJECT	obj[HCI_WORDS];
416124386Snjl	ACPI_OBJECT	*res;
417124386Snjl	int		status, i, ret;
418124386Snjl
419133630Snjl	ACPI_SERIAL_ASSERT(toshiba);
420124386Snjl	status = ENXIO;
421124386Snjl
422124386Snjl	for (i = 0; i < HCI_WORDS; i++) {
423124386Snjl		obj[i].Type = ACPI_TYPE_INTEGER;
424124386Snjl		obj[i].Integer.Value = 0;
425124386Snjl	}
426124386Snjl	obj[HCI_REG_AX].Integer.Value = op;
427124386Snjl	obj[HCI_REG_BX].Integer.Value = function;
428124386Snjl	if (op == HCI_SET)
429124386Snjl		obj[HCI_REG_CX].Integer.Value = *arg;
430124386Snjl
431124386Snjl	args.Count = HCI_WORDS;
432124386Snjl	args.Pointer = obj;
433124386Snjl	results.Pointer = NULL;
434124386Snjl	results.Length = ACPI_ALLOCATE_BUFFER;
435124386Snjl	if (ACPI_FAILURE(AcpiEvaluateObject(h, METHOD_HCI, &args, &results)))
436124386Snjl		goto end;
437124386Snjl	res = (ACPI_OBJECT *)results.Pointer;
438124386Snjl	if (!ACPI_PKG_VALID(res, HCI_WORDS)) {
439124386Snjl		printf("toshiba: invalid package!\n");
440124386Snjl		return (ENXIO);
441124386Snjl	}
442124386Snjl
443124386Snjl	acpi_PkgInt32(res, HCI_REG_AX, &ret);
444124386Snjl	if (ret == HCI_SUCCESS) {
445124386Snjl		if (op == HCI_GET)
446124386Snjl			acpi_PkgInt32(res, HCI_REG_CX, arg);
447124386Snjl		status = 0;
448124386Snjl	} else if (function == HCI_REG_SYSTEM_EVENT && op == HCI_GET &&
449124386Snjl	    ret == HCI_NOT_SUPPORTED) {
450124386Snjl		/*
451124386Snjl		 * Sometimes system events are disabled without us requesting
452124386Snjl		 * it.  This workaround attempts to re-enable them.
453133630Snjl		 *
454133630Snjl		 * XXX This call probably shouldn't be recursive.  Queueing
455133630Snjl		 * a task via AcpiOsQueueForExecution() might be better.
456124386Snjl		 */
457124386Snjl		 i = 1;
458124386Snjl		 hci_call(h, HCI_SET, HCI_REG_SYSTEM_EVENT, &i);
459124386Snjl	}
460124386Snjl
461124386Snjlend:
462124386Snjl	if (results.Pointer != NULL)
463124386Snjl		AcpiOsFree(results.Pointer);
464124386Snjl
465124386Snjl	return (status);
466124386Snjl}
467124386Snjl
468124386Snjl/*
469124386Snjl * Perform a few actions based on the keypress.  Users can extend this
470124386Snjl * functionality by reading the keystrokes we send to devd(8).
471124386Snjl */
472124386Snjlstatic void
473128207Snjlhci_key_action(struct acpi_toshiba_softc *sc, ACPI_HANDLE h, UINT32 key)
474124386Snjl{
475124386Snjl	UINT32		arg;
476124386Snjl
477133630Snjl	ACPI_SERIAL_ASSERT(toshiba);
478124386Snjl	switch (key) {
479124386Snjl	case FN_F6_RELEASE:
480124386Snjl		/* Decrease LCD brightness. */
481124386Snjl		hci_lcd_brightness(h, HCI_GET, &arg);
482124386Snjl		if (arg-- == 0)
483124386Snjl			arg = 0;
484124386Snjl		else
485124386Snjl			hci_lcd_brightness(h, HCI_SET, &arg);
486124386Snjl		break;
487124386Snjl	case FN_F7_RELEASE:
488124386Snjl		/* Increase LCD brightness. */
489124386Snjl		hci_lcd_brightness(h, HCI_GET, &arg);
490124386Snjl		if (arg++ == 7)
491124386Snjl			arg = 7;
492124386Snjl		else
493124386Snjl			hci_lcd_brightness(h, HCI_SET, &arg);
494124386Snjl		break;
495124386Snjl	case FN_F5_RELEASE:
496124386Snjl		/* Cycle through video outputs. */
497124386Snjl		hci_video_output(h, HCI_GET, &arg);
498124386Snjl		arg = (arg + 1) % 7;
499128207Snjl		hci_video_output(sc->video_handle, HCI_SET, &arg);
500124386Snjl		break;
501124386Snjl	case FN_F8_RELEASE:
502124386Snjl		/* Toggle LCD backlight. */
503124386Snjl		hci_lcd_backlight(h, HCI_GET, &arg);
504124386Snjl		arg = (arg != 0) ? 0 : 1;
505124386Snjl		hci_lcd_backlight(h, HCI_SET, &arg);
506124386Snjl		break;
507124386Snjl	case FN_ESC_RELEASE:
508124386Snjl		/* Toggle forcing fan on. */
509124386Snjl		hci_force_fan(h, HCI_GET, &arg);
510124386Snjl		arg = (arg != 0) ? 0 : 1;
511124386Snjl		hci_force_fan(h, HCI_SET, &arg);
512124386Snjl		break;
513124386Snjl	}
514124386Snjl}
515124386Snjl
516124386Snjlstatic void
517124386Snjlacpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify, void *context)
518124386Snjl{
519124386Snjl	struct		acpi_toshiba_softc *sc;
520124386Snjl	UINT32		key;
521124386Snjl
522124386Snjl	sc = (struct acpi_toshiba_softc *)context;
523124386Snjl
524124386Snjl	if (notify == 0x80) {
525133630Snjl		ACPI_SERIAL_BEGIN(toshiba);
526124386Snjl		while (hci_call(h, HCI_GET, HCI_REG_SYSTEM_EVENT, &key) == 0) {
527128207Snjl			hci_key_action(sc, h, key);
528124386Snjl			acpi_UserNotify("TOSHIBA", h, (uint8_t)key);
529124386Snjl		}
530133630Snjl		ACPI_SERIAL_END(toshiba);
531128207Snjl	} else
532124386Snjl		device_printf(sc->dev, "unknown notify: 0x%x\n", notify);
533128207Snjl}
534128207Snjl
535128207Snjl/*
536128207Snjl * Toshiba video pseudo-device to provide the DSSX method.
537128207Snjl *
538128207Snjl * HID      Model
539128207Snjl * -------------------------------------
540128207Snjl * TOS6201  Libretto L Series
541128207Snjl */
542128207Snjlstatic int
543128207Snjlacpi_toshiba_video_probe(device_t dev)
544128207Snjl{
545131284Snjl	static char *vid_ids[] = { "TOS6201", NULL };
546128207Snjl
547131284Snjl	if (acpi_disabled("toshiba") ||
548131284Snjl	    ACPI_ID_PROBE(device_get_parent(dev), dev, vid_ids) == NULL ||
549131284Snjl	    device_get_unit(dev) != 0)
550131284Snjl		return (ENXIO);
551128207Snjl
552131284Snjl	device_quiet(dev);
553131284Snjl	device_set_desc(dev, "Toshiba Video");
554131284Snjl	return (0);
555124386Snjl}
556128207Snjl
557128207Snjlstatic int
558128207Snjlacpi_toshiba_video_attach(device_t dev)
559128207Snjl{
560128207Snjl	struct		acpi_toshiba_softc *sc;
561128207Snjl
562128207Snjl	sc = devclass_get_softc(acpi_toshiba_devclass, 0);
563128207Snjl	if (sc == NULL)
564128207Snjl		return (ENXIO);
565128207Snjl	sc->video_handle = acpi_get_handle(dev);
566128207Snjl	return (0);
567128207Snjl}
568