xref: /freebsd/sys/dev/acpi_support/acpi_toshiba.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
165c0f491SNate Lawson /*-
265c0f491SNate Lawson  * Copyright (c) 2003 Hiroyuki Aizu <aizu@navi.org>
365c0f491SNate Lawson  * All rights reserved.
465c0f491SNate Lawson  *
565c0f491SNate Lawson  * Redistribution and use in source and binary forms, with or without
665c0f491SNate Lawson  * modification, are permitted provided that the following conditions
765c0f491SNate Lawson  * are met:
865c0f491SNate Lawson  * 1. Redistributions of source code must retain the above copyright
965c0f491SNate Lawson  *    notice, this list of conditions and the following disclaimer.
1065c0f491SNate Lawson  * 2. Redistributions in binary form must reproduce the above copyright
1165c0f491SNate Lawson  *    notice, this list of conditions and the following disclaimer in the
1265c0f491SNate Lawson  *    documentation and/or other materials provided with the distribution.
1365c0f491SNate Lawson  *
1465c0f491SNate Lawson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1565c0f491SNate Lawson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1665c0f491SNate Lawson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1765c0f491SNate Lawson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1865c0f491SNate Lawson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1965c0f491SNate Lawson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2065c0f491SNate Lawson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2165c0f491SNate Lawson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2265c0f491SNate Lawson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2365c0f491SNate Lawson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2465c0f491SNate Lawson  * SUCH DAMAGE.
2565c0f491SNate Lawson  *
2665c0f491SNate Lawson  */
2765c0f491SNate Lawson 
2865c0f491SNate Lawson #include <sys/cdefs.h>
2965c0f491SNate Lawson #include "opt_acpi.h"
3065c0f491SNate Lawson #include <sys/param.h>
3165c0f491SNate Lawson #include <sys/kernel.h>
3277409fe1SPoul-Henning Kamp #include <sys/module.h>
3365c0f491SNate Lawson #include <sys/bus.h>
3465c0f491SNate Lawson 
35129d3046SJung-uk Kim #include <contrib/dev/acpica/include/acpi.h>
36129d3046SJung-uk Kim 
3765c0f491SNate Lawson #include <dev/acpica/acpivar.h>
3865c0f491SNate Lawson 
39276cd921SNate Lawson #define _COMPONENT	ACPI_OEM
40276cd921SNate Lawson ACPI_MODULE_NAME("Toshiba")
41276cd921SNate Lawson 
4265c0f491SNate Lawson /*
4365c0f491SNate Lawson  * Toshiba HCI interface definitions
4465c0f491SNate Lawson  *
4565c0f491SNate Lawson  * HCI is Toshiba's "Hardware Control Interface" which is supposed to
4665c0f491SNate Lawson  * be uniform across all their models.  Ideally we would just call
4765c0f491SNate Lawson  * dedicated ACPI methods instead of using this primitive interface.
4865c0f491SNate Lawson  * However, the ACPI methods seem to be incomplete in some areas (for
4965c0f491SNate Lawson  * example they allow setting, but not reading, the LCD brightness
5065c0f491SNate Lawson  * value), so this is still useful.
5165c0f491SNate Lawson  */
5265c0f491SNate Lawson 
5365c0f491SNate Lawson #define METHOD_HCI		"GHCI"
5465c0f491SNate Lawson #define METHOD_HCI_ENABLE	"ENAB"
550287be96SNate Lawson #define METHOD_VIDEO		"DSSX"
5665c0f491SNate Lawson 
5765c0f491SNate Lawson /* Operations */
5865c0f491SNate Lawson #define HCI_SET				0xFF00
5965c0f491SNate Lawson #define HCI_GET				0xFE00
6065c0f491SNate Lawson 
6165c0f491SNate Lawson /* Return codes */
6265c0f491SNate Lawson #define HCI_SUCCESS			0x0000
6365c0f491SNate Lawson #define HCI_FAILURE			0x1000
6465c0f491SNate Lawson #define HCI_NOT_SUPPORTED		0x8000
6565c0f491SNate Lawson #define HCI_EMPTY			0x8C00
6665c0f491SNate Lawson 
6765c0f491SNate Lawson /* Functions */
6865c0f491SNate Lawson #define HCI_REG_LCD_BACKLIGHT		0x0002
6965c0f491SNate Lawson #define HCI_REG_FAN			0x0004
7065c0f491SNate Lawson #define HCI_REG_SYSTEM_EVENT		0x0016
7165c0f491SNate Lawson #define HCI_REG_VIDEO_OUTPUT		0x001C
7265c0f491SNate Lawson #define HCI_REG_HOTKEY_EVENT		0x001E
7365c0f491SNate Lawson #define HCI_REG_LCD_BRIGHTNESS		0x002A
7465c0f491SNate Lawson #define HCI_REG_CPU_SPEED		0x0032
7565c0f491SNate Lawson 
7665c0f491SNate Lawson /* Field definitions */
7765c0f491SNate Lawson #define HCI_FAN_SHIFT			7
7865c0f491SNate Lawson #define HCI_LCD_BRIGHTNESS_BITS		3
7965c0f491SNate Lawson #define HCI_LCD_BRIGHTNESS_SHIFT	(16 - HCI_LCD_BRIGHTNESS_BITS)
8065c0f491SNate Lawson #define HCI_LCD_BRIGHTNESS_MAX		((1 << HCI_LCD_BRIGHTNESS_BITS) - 1)
8165c0f491SNate Lawson #define HCI_VIDEO_OUTPUT_FLAG		0x0100
8265c0f491SNate Lawson #define HCI_VIDEO_OUTPUT_LCD		0x1
8365c0f491SNate Lawson #define HCI_VIDEO_OUTPUT_CRT		0x2
8465c0f491SNate Lawson #define HCI_VIDEO_OUTPUT_TV		0x4
8565c0f491SNate Lawson #define HCI_CPU_SPEED_BITS		3
8665c0f491SNate Lawson #define HCI_CPU_SPEED_SHIFT		(16 - HCI_CPU_SPEED_BITS)
8765c0f491SNate Lawson #define HCI_CPU_SPEED_MAX		((1 << HCI_CPU_SPEED_BITS) - 1)
8865c0f491SNate Lawson 
8965c0f491SNate Lawson /* Key press/release events. */
9065c0f491SNate Lawson #define FN_F1_PRESS	0x013B
9165c0f491SNate Lawson #define FN_F1_RELEASE	0x01BB
9265c0f491SNate Lawson #define FN_F2_PRESS	0x013C
9365c0f491SNate Lawson #define FN_F2_RELEASE	0x01BC
9465c0f491SNate Lawson #define FN_F3_PRESS	0x013D
9565c0f491SNate Lawson #define FN_F3_RELEASE	0x01BD
9665c0f491SNate Lawson #define FN_F4_PRESS	0x013E
9765c0f491SNate Lawson #define FN_F4_RELEASE	0x01BE
9865c0f491SNate Lawson #define FN_F5_PRESS	0x013F
9965c0f491SNate Lawson #define FN_F5_RELEASE	0x01BF
10065c0f491SNate Lawson #define FN_F6_PRESS	0x0140
10165c0f491SNate Lawson #define FN_F6_RELEASE	0x01C0
10265c0f491SNate Lawson #define FN_F7_PRESS	0x0141
10365c0f491SNate Lawson #define FN_F7_RELEASE	0x01C1
10465c0f491SNate Lawson #define FN_F8_PRESS	0x0142
10565c0f491SNate Lawson #define FN_F8_RELEASE	0x01C2
10665c0f491SNate Lawson #define FN_F9_PRESS	0x0143
10765c0f491SNate Lawson #define FN_F9_RELEASE	0x01C3
10865c0f491SNate Lawson #define FN_BS_PRESS	0x010E
10965c0f491SNate Lawson #define FN_BS_RELEASE	0x018E
11065c0f491SNate Lawson #define FN_ESC_PRESS	0x0101
11165c0f491SNate Lawson #define FN_ESC_RELEASE	0x0181
11265c0f491SNate Lawson #define FN_KNJ_PRESS	0x0129
11365c0f491SNate Lawson #define FN_KNJ_RELEASE	0x01A9
11465c0f491SNate Lawson 
11565c0f491SNate Lawson /* HCI register definitions. */
11665c0f491SNate Lawson #define HCI_WORDS	6		/* Number of registers */
11765c0f491SNate Lawson #define HCI_REG_AX	0		/* Operation, then return value */
11865c0f491SNate Lawson #define HCI_REG_BX	1		/* Function */
11965c0f491SNate Lawson #define HCI_REG_CX	2		/* Argument (in or out) */
12065c0f491SNate Lawson #define HCI_REG_DX	3		/* Unused? */
12165c0f491SNate Lawson #define HCI_REG_SI	4		/* Unused? */
12265c0f491SNate Lawson #define HCI_REG_DI	5		/* Unused? */
12365c0f491SNate Lawson 
12465c0f491SNate Lawson struct acpi_toshiba_softc {
12565c0f491SNate Lawson 	device_t	dev;
12665c0f491SNate Lawson 	ACPI_HANDLE	handle;
1270287be96SNate Lawson 	ACPI_HANDLE	video_handle;
12865c0f491SNate Lawson 	struct		sysctl_ctx_list sysctl_ctx;
12965c0f491SNate Lawson 	struct		sysctl_oid *sysctl_tree;
13065c0f491SNate Lawson };
13165c0f491SNate Lawson 
13265c0f491SNate Lawson /* Prototype for HCI functions for getting/setting a value. */
13365c0f491SNate Lawson typedef int	hci_fn_t(ACPI_HANDLE, int, UINT32 *);
13465c0f491SNate Lawson 
13565c0f491SNate Lawson static int	acpi_toshiba_probe(device_t dev);
13665c0f491SNate Lawson static int	acpi_toshiba_attach(device_t dev);
13765c0f491SNate Lawson static int	acpi_toshiba_detach(device_t dev);
13865c0f491SNate Lawson static int	acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS);
13965c0f491SNate Lawson static hci_fn_t	hci_force_fan;
14065c0f491SNate Lawson static hci_fn_t	hci_video_output;
14165c0f491SNate Lawson static hci_fn_t	hci_lcd_brightness;
14265c0f491SNate Lawson static hci_fn_t	hci_lcd_backlight;
14365c0f491SNate Lawson static hci_fn_t	hci_cpu_speed;
14465c0f491SNate Lawson static int	hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg);
1450287be96SNate Lawson static void	hci_key_action(struct acpi_toshiba_softc *sc, ACPI_HANDLE h,
1460287be96SNate Lawson 		    UINT32 key);
14765c0f491SNate Lawson static void	acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify,
14865c0f491SNate Lawson 		    void *context);
1490287be96SNate Lawson static int	acpi_toshiba_video_probe(device_t dev);
1500287be96SNate Lawson static int	acpi_toshiba_video_attach(device_t dev);
15165c0f491SNate Lawson 
1528781a785SNate Lawson ACPI_SERIAL_DECL(toshiba, "ACPI Toshiba Extras");
1538781a785SNate Lawson 
15465c0f491SNate Lawson /* Table of sysctl names and HCI functions to call. */
15565c0f491SNate Lawson static struct {
15665c0f491SNate Lawson 	char		*name;
15765c0f491SNate Lawson 	hci_fn_t	*handler;
15865c0f491SNate Lawson } sysctl_table[] = {
15965c0f491SNate Lawson 	/* name,		handler */
16065c0f491SNate Lawson 	{"force_fan",		hci_force_fan},
16165c0f491SNate Lawson 	{"video_output",	hci_video_output},
16265c0f491SNate Lawson 	{"lcd_brightness",	hci_lcd_brightness},
1631114c592SNate Lawson 	{"lcd_backlight",	hci_lcd_backlight},
1641114c592SNate Lawson 	{"cpu_speed",		hci_cpu_speed},
16565c0f491SNate Lawson 	{NULL, NULL}
16665c0f491SNate Lawson };
16765c0f491SNate Lawson 
16865c0f491SNate Lawson static device_method_t acpi_toshiba_methods[] = {
16965c0f491SNate Lawson 	DEVMETHOD(device_probe,		acpi_toshiba_probe),
17065c0f491SNate Lawson 	DEVMETHOD(device_attach,	acpi_toshiba_attach),
17165c0f491SNate Lawson 	DEVMETHOD(device_detach,	acpi_toshiba_detach),
17265c0f491SNate Lawson 
17361bfd867SSofian Brabez 	DEVMETHOD_END
17465c0f491SNate Lawson };
17565c0f491SNate Lawson 
17665c0f491SNate Lawson static driver_t acpi_toshiba_driver = {
17765c0f491SNate Lawson 	"acpi_toshiba",
17865c0f491SNate Lawson 	acpi_toshiba_methods,
17965c0f491SNate Lawson 	sizeof(struct acpi_toshiba_softc),
18065c0f491SNate Lawson };
18165c0f491SNate Lawson 
182*90161e72SJohn Baldwin DRIVER_MODULE(acpi_toshiba, acpi, acpi_toshiba_driver, 0, 0);
183a4ecd543SNate Lawson MODULE_DEPEND(acpi_toshiba, acpi, 1, 1, 1);
18465c0f491SNate Lawson 
1850287be96SNate Lawson static device_method_t acpi_toshiba_video_methods[] = {
1860287be96SNate Lawson 	DEVMETHOD(device_probe,		acpi_toshiba_video_probe),
1870287be96SNate Lawson 	DEVMETHOD(device_attach,	acpi_toshiba_video_attach),
1880287be96SNate Lawson 
18961bfd867SSofian Brabez 	DEVMETHOD_END
1900287be96SNate Lawson };
1910287be96SNate Lawson 
1920287be96SNate Lawson static driver_t acpi_toshiba_video_driver = {
1930287be96SNate Lawson 	"acpi_toshiba_video",
1940287be96SNate Lawson 	acpi_toshiba_video_methods,
1950287be96SNate Lawson 	0,
1960287be96SNate Lawson };
1970287be96SNate Lawson 
198*90161e72SJohn Baldwin DRIVER_MODULE(acpi_toshiba_video, acpi, acpi_toshiba_video_driver, 0, 0);
1990287be96SNate Lawson MODULE_DEPEND(acpi_toshiba_video, acpi, 1, 1, 1);
2000287be96SNate Lawson 
20165c0f491SNate Lawson static int	enable_fn_keys = 1;
20265c0f491SNate Lawson TUNABLE_INT("hw.acpi.toshiba.enable_fn_keys", &enable_fn_keys);
20365c0f491SNate Lawson 
20465c0f491SNate Lawson /*
20565c0f491SNate Lawson  * HID      Model
20665c0f491SNate Lawson  * -------------------------------------
20765c0f491SNate Lawson  * TOS6200  Libretto L Series
20865c0f491SNate Lawson  *          Dynabook Satellite 2455
20965c0f491SNate Lawson  *          Dynabook SS 3500
21065c0f491SNate Lawson  * TOS6207  Dynabook SS2110 Series
2113da063baSJohn Baldwin  * TOS6208  SPA40
21265c0f491SNate Lawson  */
21365c0f491SNate Lawson static int
acpi_toshiba_probe(device_t dev)21465c0f491SNate Lawson acpi_toshiba_probe(device_t dev)
21565c0f491SNate Lawson {
2163da063baSJohn Baldwin 	static char *tosh_ids[] = { "TOS6200", "TOS6207", "TOS6208", NULL };
2175efca36fSTakanori Watanabe 	int rv;
21865c0f491SNate Lawson 
2193ed1dfa1SNate Lawson 	if (acpi_disabled("toshiba") ||
2203ed1dfa1SNate Lawson 	    device_get_unit(dev) != 0)
2213ed1dfa1SNate Lawson 		return (ENXIO);
2225efca36fSTakanori Watanabe 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, tosh_ids, NULL);
2235efca36fSTakanori Watanabe 	if (rv <= 0)
2241114c592SNate Lawson 		device_set_desc(dev, "Toshiba HCI Extras");
2255efca36fSTakanori Watanabe 	return (rv);
22665c0f491SNate Lawson }
22765c0f491SNate Lawson 
22865c0f491SNate Lawson static int
acpi_toshiba_attach(device_t dev)22965c0f491SNate Lawson acpi_toshiba_attach(device_t dev)
23065c0f491SNate Lawson {
23165c0f491SNate Lawson 	struct		acpi_toshiba_softc *sc;
23265c0f491SNate Lawson 	struct		acpi_softc *acpi_sc;
23365c0f491SNate Lawson 	ACPI_STATUS	status;
23465c0f491SNate Lawson 	int		i;
23565c0f491SNate Lawson 
23665c0f491SNate Lawson 	sc = device_get_softc(dev);
23765c0f491SNate Lawson 	sc->dev = dev;
23865c0f491SNate Lawson 	sc->handle = acpi_get_handle(dev);
23965c0f491SNate Lawson 
24065c0f491SNate Lawson 	acpi_sc = acpi_device_get_parent_softc(dev);
24165c0f491SNate Lawson 	sysctl_ctx_init(&sc->sysctl_ctx);
24265c0f491SNate Lawson 	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
24365c0f491SNate Lawson 	    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
2447029da5cSPawel Biernacki 	    "toshiba", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
24565c0f491SNate Lawson 
24665c0f491SNate Lawson 	for (i = 0; sysctl_table[i].name != NULL; i++) {
24765c0f491SNate Lawson 		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
24865c0f491SNate Lawson 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
24965c0f491SNate Lawson 		    sysctl_table[i].name,
2507029da5cSPawel Biernacki 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY |
2516237a1ccSAlexander Motin 		    CTLFLAG_MPSAFE, sc, i, acpi_toshiba_sysctl, "I", "");
25265c0f491SNate Lawson 	}
25365c0f491SNate Lawson 
25465c0f491SNate Lawson 	if (enable_fn_keys != 0) {
25565c0f491SNate Lawson 		status = AcpiEvaluateObject(sc->handle, METHOD_HCI_ENABLE,
25665c0f491SNate Lawson 					    NULL, NULL);
25765c0f491SNate Lawson 		if (ACPI_FAILURE(status)) {
25865c0f491SNate Lawson 			device_printf(dev, "enable FN keys failed\n");
25965c0f491SNate Lawson 			sysctl_ctx_free(&sc->sysctl_ctx);
26065c0f491SNate Lawson 			return (ENXIO);
26165c0f491SNate Lawson 		}
26265c0f491SNate Lawson 		AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
26365c0f491SNate Lawson 					 acpi_toshiba_notify, sc);
26465c0f491SNate Lawson 	}
26565c0f491SNate Lawson 
26665c0f491SNate Lawson 	return (0);
26765c0f491SNate Lawson }
26865c0f491SNate Lawson 
26965c0f491SNate Lawson static int
acpi_toshiba_detach(device_t dev)27065c0f491SNate Lawson acpi_toshiba_detach(device_t dev)
27165c0f491SNate Lawson {
27265c0f491SNate Lawson 	struct		acpi_toshiba_softc *sc;
27365c0f491SNate Lawson 
27465c0f491SNate Lawson 	sc = device_get_softc(dev);
275b9585d5eSNate Lawson 	if (enable_fn_keys != 0) {
27665c0f491SNate Lawson 		AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
27765c0f491SNate Lawson 					acpi_toshiba_notify);
278b9585d5eSNate Lawson 	}
27965c0f491SNate Lawson 	sysctl_ctx_free(&sc->sysctl_ctx);
28065c0f491SNate Lawson 
28165c0f491SNate Lawson 	return (0);
28265c0f491SNate Lawson }
28365c0f491SNate Lawson 
28465c0f491SNate Lawson static int
acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS)28565c0f491SNate Lawson acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS)
28665c0f491SNate Lawson {
28765c0f491SNate Lawson 	struct		acpi_toshiba_softc *sc;
28865c0f491SNate Lawson 	UINT32		arg;
28965c0f491SNate Lawson 	int		function, error = 0;
29065c0f491SNate Lawson 	hci_fn_t	*handler;
29165c0f491SNate Lawson 
29265c0f491SNate Lawson 	sc = (struct acpi_toshiba_softc *)oidp->oid_arg1;
29365c0f491SNate Lawson 	function = oidp->oid_arg2;
29465c0f491SNate Lawson 	handler = sysctl_table[function].handler;
29565c0f491SNate Lawson 
29665c0f491SNate Lawson 	/* Get the current value from the appropriate function. */
2978781a785SNate Lawson 	ACPI_SERIAL_BEGIN(toshiba);
29865c0f491SNate Lawson 	error = handler(sc->handle, HCI_GET, &arg);
29965c0f491SNate Lawson 	if (error != 0)
3008781a785SNate Lawson 		goto out;
30165c0f491SNate Lawson 
30265c0f491SNate Lawson 	/* Send the current value to the user and return if no new value. */
30365c0f491SNate Lawson 	error = sysctl_handle_int(oidp, &arg, 0, req);
30465c0f491SNate Lawson 	if (error != 0 || req->newptr == NULL)
3058781a785SNate Lawson 		goto out;
30665c0f491SNate Lawson 
30765c0f491SNate Lawson 	/* Set the new value via the appropriate function. */
30865c0f491SNate Lawson 	error = handler(sc->handle, HCI_SET, &arg);
30965c0f491SNate Lawson 
3108781a785SNate Lawson out:
3118781a785SNate Lawson 	ACPI_SERIAL_END(toshiba);
31265c0f491SNate Lawson 	return (error);
31365c0f491SNate Lawson }
31465c0f491SNate Lawson 
31565c0f491SNate Lawson static int
hci_force_fan(ACPI_HANDLE h,int op,UINT32 * state)31665c0f491SNate Lawson hci_force_fan(ACPI_HANDLE h, int op, UINT32 *state)
31765c0f491SNate Lawson {
31865c0f491SNate Lawson 	int		ret;
31965c0f491SNate Lawson 
3208781a785SNate Lawson 	ACPI_SERIAL_ASSERT(toshiba);
32165c0f491SNate Lawson 	if (op == HCI_SET) {
32251251514SEitan Adler 		if (*state > 1)
32365c0f491SNate Lawson 			return (EINVAL);
32465c0f491SNate Lawson 		*state <<= HCI_FAN_SHIFT;
32565c0f491SNate Lawson 	}
32665c0f491SNate Lawson 	ret = hci_call(h, op, HCI_REG_FAN, state);
32765c0f491SNate Lawson 	if (ret == 0 && op == HCI_GET)
32865c0f491SNate Lawson 		*state >>= HCI_FAN_SHIFT;
32965c0f491SNate Lawson 	return (ret);
33065c0f491SNate Lawson }
33165c0f491SNate Lawson 
33265c0f491SNate Lawson static int
hci_video_output(ACPI_HANDLE h,int op,UINT32 * video_output)33365c0f491SNate Lawson hci_video_output(ACPI_HANDLE h, int op, UINT32 *video_output)
33465c0f491SNate Lawson {
33565c0f491SNate Lawson 	int		ret;
3360287be96SNate Lawson 	ACPI_STATUS	status;
33765c0f491SNate Lawson 
3388781a785SNate Lawson 	ACPI_SERIAL_ASSERT(toshiba);
33965c0f491SNate Lawson 	if (op == HCI_SET) {
34065c0f491SNate Lawson 		if (*video_output < 1 || *video_output > 7)
34165c0f491SNate Lawson 			return (EINVAL);
3420287be96SNate Lawson 		if (h == NULL)
3430287be96SNate Lawson 			return (ENXIO);
34465c0f491SNate Lawson 		*video_output |= HCI_VIDEO_OUTPUT_FLAG;
34596d340f2SNate Lawson 		status = acpi_SetInteger(h, METHOD_VIDEO, *video_output);
3460287be96SNate Lawson 		if (ACPI_SUCCESS(status))
3470287be96SNate Lawson 			ret = 0;
3480287be96SNate Lawson 		else
3490287be96SNate Lawson 			ret = ENXIO;
3500287be96SNate Lawson 	} else {
35165c0f491SNate Lawson 		ret = hci_call(h, op, HCI_REG_VIDEO_OUTPUT, video_output);
3520287be96SNate Lawson 		if (ret == 0)
35365c0f491SNate Lawson 			*video_output &= 0xff;
3540287be96SNate Lawson 	}
3550287be96SNate Lawson 
35665c0f491SNate Lawson 	return (ret);
35765c0f491SNate Lawson }
35865c0f491SNate Lawson 
35965c0f491SNate Lawson static int
hci_lcd_brightness(ACPI_HANDLE h,int op,UINT32 * brightness)36065c0f491SNate Lawson hci_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *brightness)
36165c0f491SNate Lawson {
36265c0f491SNate Lawson 	int		ret;
36365c0f491SNate Lawson 
3648781a785SNate Lawson 	ACPI_SERIAL_ASSERT(toshiba);
36565c0f491SNate Lawson 	if (op == HCI_SET) {
36651251514SEitan Adler 		if (*brightness > HCI_LCD_BRIGHTNESS_MAX)
36765c0f491SNate Lawson 			return (EINVAL);
36865c0f491SNate Lawson 		*brightness <<= HCI_LCD_BRIGHTNESS_SHIFT;
36965c0f491SNate Lawson 	}
37065c0f491SNate Lawson 	ret = hci_call(h, op, HCI_REG_LCD_BRIGHTNESS, brightness);
37165c0f491SNate Lawson 	if (ret == 0 && op == HCI_GET)
37265c0f491SNate Lawson 		*brightness >>= HCI_LCD_BRIGHTNESS_SHIFT;
37365c0f491SNate Lawson 	return (ret);
37465c0f491SNate Lawson }
37565c0f491SNate Lawson 
37665c0f491SNate Lawson static int
hci_lcd_backlight(ACPI_HANDLE h,int op,UINT32 * backlight)37765c0f491SNate Lawson hci_lcd_backlight(ACPI_HANDLE h, int op, UINT32 *backlight)
37865c0f491SNate Lawson {
3798781a785SNate Lawson 
3808781a785SNate Lawson 	ACPI_SERIAL_ASSERT(toshiba);
38165c0f491SNate Lawson 	if (op == HCI_SET) {
38251251514SEitan Adler 		if (*backlight > 1)
38365c0f491SNate Lawson 			return (EINVAL);
38465c0f491SNate Lawson 	}
38565c0f491SNate Lawson 	return (hci_call(h, op, HCI_REG_LCD_BACKLIGHT, backlight));
38665c0f491SNate Lawson }
38765c0f491SNate Lawson 
38865c0f491SNate Lawson static int
hci_cpu_speed(ACPI_HANDLE h,int op,UINT32 * speed)38965c0f491SNate Lawson hci_cpu_speed(ACPI_HANDLE h, int op, UINT32 *speed)
39065c0f491SNate Lawson {
39165c0f491SNate Lawson 	int		ret;
39265c0f491SNate Lawson 
3938781a785SNate Lawson 	ACPI_SERIAL_ASSERT(toshiba);
39465c0f491SNate Lawson 	if (op == HCI_SET) {
39551251514SEitan Adler 		if (*speed > HCI_CPU_SPEED_MAX)
39665c0f491SNate Lawson 			return (EINVAL);
39765c0f491SNate Lawson 		*speed <<= HCI_CPU_SPEED_SHIFT;
39865c0f491SNate Lawson 	}
39965c0f491SNate Lawson 	ret = hci_call(h, op, HCI_REG_CPU_SPEED, speed);
40065c0f491SNate Lawson 	if (ret == 0 && op == HCI_GET)
40165c0f491SNate Lawson 		*speed >>= HCI_CPU_SPEED_SHIFT;
40265c0f491SNate Lawson 	return (ret);
40365c0f491SNate Lawson }
40465c0f491SNate Lawson 
40565c0f491SNate Lawson static int
hci_call(ACPI_HANDLE h,int op,int function,UINT32 * arg)40665c0f491SNate Lawson hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg)
40765c0f491SNate Lawson {
40865c0f491SNate Lawson 	ACPI_OBJECT_LIST args;
40965c0f491SNate Lawson 	ACPI_BUFFER	results;
41065c0f491SNate Lawson 	ACPI_OBJECT	obj[HCI_WORDS];
41165c0f491SNate Lawson 	ACPI_OBJECT	*res;
41265c0f491SNate Lawson 	int		status, i, ret;
41365c0f491SNate Lawson 
4148781a785SNate Lawson 	ACPI_SERIAL_ASSERT(toshiba);
41565c0f491SNate Lawson 	status = ENXIO;
41665c0f491SNate Lawson 
41765c0f491SNate Lawson 	for (i = 0; i < HCI_WORDS; i++) {
41865c0f491SNate Lawson 		obj[i].Type = ACPI_TYPE_INTEGER;
41965c0f491SNate Lawson 		obj[i].Integer.Value = 0;
42065c0f491SNate Lawson 	}
42165c0f491SNate Lawson 	obj[HCI_REG_AX].Integer.Value = op;
42265c0f491SNate Lawson 	obj[HCI_REG_BX].Integer.Value = function;
42365c0f491SNate Lawson 	if (op == HCI_SET)
42465c0f491SNate Lawson 		obj[HCI_REG_CX].Integer.Value = *arg;
42565c0f491SNate Lawson 
42665c0f491SNate Lawson 	args.Count = HCI_WORDS;
42765c0f491SNate Lawson 	args.Pointer = obj;
42865c0f491SNate Lawson 	results.Pointer = NULL;
42965c0f491SNate Lawson 	results.Length = ACPI_ALLOCATE_BUFFER;
43065c0f491SNate Lawson 	if (ACPI_FAILURE(AcpiEvaluateObject(h, METHOD_HCI, &args, &results)))
43165c0f491SNate Lawson 		goto end;
43265c0f491SNate Lawson 	res = (ACPI_OBJECT *)results.Pointer;
43365c0f491SNate Lawson 	if (!ACPI_PKG_VALID(res, HCI_WORDS)) {
43465c0f491SNate Lawson 		printf("toshiba: invalid package!\n");
43565c0f491SNate Lawson 		return (ENXIO);
43665c0f491SNate Lawson 	}
43765c0f491SNate Lawson 
43865c0f491SNate Lawson 	acpi_PkgInt32(res, HCI_REG_AX, &ret);
43965c0f491SNate Lawson 	if (ret == HCI_SUCCESS) {
44065c0f491SNate Lawson 		if (op == HCI_GET)
44165c0f491SNate Lawson 			acpi_PkgInt32(res, HCI_REG_CX, arg);
44265c0f491SNate Lawson 		status = 0;
44365c0f491SNate Lawson 	} else if (function == HCI_REG_SYSTEM_EVENT && op == HCI_GET &&
44465c0f491SNate Lawson 	    ret == HCI_NOT_SUPPORTED) {
44565c0f491SNate Lawson 		/*
44665c0f491SNate Lawson 		 * Sometimes system events are disabled without us requesting
44765c0f491SNate Lawson 		 * it.  This workaround attempts to re-enable them.
4488781a785SNate Lawson 		 *
4498781a785SNate Lawson 		 * XXX This call probably shouldn't be recursive.  Queueing
4508781a785SNate Lawson 		 * a task via AcpiOsQueueForExecution() might be better.
45165c0f491SNate Lawson 		 */
45265c0f491SNate Lawson 		 i = 1;
45365c0f491SNate Lawson 		 hci_call(h, HCI_SET, HCI_REG_SYSTEM_EVENT, &i);
45465c0f491SNate Lawson 	}
45565c0f491SNate Lawson 
45665c0f491SNate Lawson end:
45765c0f491SNate Lawson 	if (results.Pointer != NULL)
45865c0f491SNate Lawson 		AcpiOsFree(results.Pointer);
45965c0f491SNate Lawson 
46065c0f491SNate Lawson 	return (status);
46165c0f491SNate Lawson }
46265c0f491SNate Lawson 
46365c0f491SNate Lawson /*
46465c0f491SNate Lawson  * Perform a few actions based on the keypress.  Users can extend this
46565c0f491SNate Lawson  * functionality by reading the keystrokes we send to devd(8).
46665c0f491SNate Lawson  */
46765c0f491SNate Lawson static void
hci_key_action(struct acpi_toshiba_softc * sc,ACPI_HANDLE h,UINT32 key)4680287be96SNate Lawson hci_key_action(struct acpi_toshiba_softc *sc, ACPI_HANDLE h, UINT32 key)
46965c0f491SNate Lawson {
47065c0f491SNate Lawson 	UINT32		arg;
47165c0f491SNate Lawson 
4728781a785SNate Lawson 	ACPI_SERIAL_ASSERT(toshiba);
47365c0f491SNate Lawson 	switch (key) {
47465c0f491SNate Lawson 	case FN_F6_RELEASE:
47565c0f491SNate Lawson 		/* Decrease LCD brightness. */
47665c0f491SNate Lawson 		hci_lcd_brightness(h, HCI_GET, &arg);
47765c0f491SNate Lawson 		if (arg-- == 0)
47865c0f491SNate Lawson 			arg = 0;
47965c0f491SNate Lawson 		else
48065c0f491SNate Lawson 			hci_lcd_brightness(h, HCI_SET, &arg);
48165c0f491SNate Lawson 		break;
48265c0f491SNate Lawson 	case FN_F7_RELEASE:
48365c0f491SNate Lawson 		/* Increase LCD brightness. */
48465c0f491SNate Lawson 		hci_lcd_brightness(h, HCI_GET, &arg);
48565c0f491SNate Lawson 		if (arg++ == 7)
48665c0f491SNate Lawson 			arg = 7;
48765c0f491SNate Lawson 		else
48865c0f491SNate Lawson 			hci_lcd_brightness(h, HCI_SET, &arg);
48965c0f491SNate Lawson 		break;
49065c0f491SNate Lawson 	case FN_F5_RELEASE:
49165c0f491SNate Lawson 		/* Cycle through video outputs. */
49265c0f491SNate Lawson 		hci_video_output(h, HCI_GET, &arg);
49365c0f491SNate Lawson 		arg = (arg + 1) % 7;
4940287be96SNate Lawson 		hci_video_output(sc->video_handle, HCI_SET, &arg);
49565c0f491SNate Lawson 		break;
49665c0f491SNate Lawson 	case FN_F8_RELEASE:
49765c0f491SNate Lawson 		/* Toggle LCD backlight. */
49865c0f491SNate Lawson 		hci_lcd_backlight(h, HCI_GET, &arg);
49965c0f491SNate Lawson 		arg = (arg != 0) ? 0 : 1;
50065c0f491SNate Lawson 		hci_lcd_backlight(h, HCI_SET, &arg);
50165c0f491SNate Lawson 		break;
50265c0f491SNate Lawson 	case FN_ESC_RELEASE:
50365c0f491SNate Lawson 		/* Toggle forcing fan on. */
50465c0f491SNate Lawson 		hci_force_fan(h, HCI_GET, &arg);
50565c0f491SNate Lawson 		arg = (arg != 0) ? 0 : 1;
50665c0f491SNate Lawson 		hci_force_fan(h, HCI_SET, &arg);
50765c0f491SNate Lawson 		break;
50865c0f491SNate Lawson 	}
50965c0f491SNate Lawson }
51065c0f491SNate Lawson 
51165c0f491SNate Lawson static void
acpi_toshiba_notify(ACPI_HANDLE h,UINT32 notify,void * context)51265c0f491SNate Lawson acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify, void *context)
51365c0f491SNate Lawson {
51465c0f491SNate Lawson 	struct		acpi_toshiba_softc *sc;
51565c0f491SNate Lawson 	UINT32		key;
51665c0f491SNate Lawson 
51765c0f491SNate Lawson 	sc = (struct acpi_toshiba_softc *)context;
51865c0f491SNate Lawson 
51965c0f491SNate Lawson 	if (notify == 0x80) {
5208781a785SNate Lawson 		ACPI_SERIAL_BEGIN(toshiba);
52165c0f491SNate Lawson 		while (hci_call(h, HCI_GET, HCI_REG_SYSTEM_EVENT, &key) == 0) {
5220287be96SNate Lawson 			hci_key_action(sc, h, key);
52365c0f491SNate Lawson 			acpi_UserNotify("TOSHIBA", h, (uint8_t)key);
52465c0f491SNate Lawson 		}
5258781a785SNate Lawson 		ACPI_SERIAL_END(toshiba);
5260287be96SNate Lawson 	} else
52765c0f491SNate Lawson 		device_printf(sc->dev, "unknown notify: 0x%x\n", notify);
52865c0f491SNate Lawson }
5290287be96SNate Lawson 
5300287be96SNate Lawson /*
5310287be96SNate Lawson  * Toshiba video pseudo-device to provide the DSSX method.
5320287be96SNate Lawson  *
5330287be96SNate Lawson  * HID      Model
5340287be96SNate Lawson  * -------------------------------------
5350287be96SNate Lawson  * TOS6201  Libretto L Series
5360287be96SNate Lawson  */
5370287be96SNate Lawson static int
acpi_toshiba_video_probe(device_t dev)5380287be96SNate Lawson acpi_toshiba_video_probe(device_t dev)
5390287be96SNate Lawson {
5403ed1dfa1SNate Lawson 	static char *vid_ids[] = { "TOS6201", NULL };
5415efca36fSTakanori Watanabe 	int rv;
5420287be96SNate Lawson 
5433ed1dfa1SNate Lawson 	if (acpi_disabled("toshiba") ||
5443ed1dfa1SNate Lawson 	    device_get_unit(dev) != 0)
5453ed1dfa1SNate Lawson 		return (ENXIO);
5463ed1dfa1SNate Lawson 
5470287be96SNate Lawson 	device_quiet(dev);
5485efca36fSTakanori Watanabe 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, vid_ids, NULL);
5495efca36fSTakanori Watanabe 	if (rv <= 0)
5500287be96SNate Lawson 		device_set_desc(dev, "Toshiba Video");
5515efca36fSTakanori Watanabe 	return (rv);
5520287be96SNate Lawson }
5530287be96SNate Lawson 
5540287be96SNate Lawson static int
acpi_toshiba_video_attach(device_t dev)5550287be96SNate Lawson acpi_toshiba_video_attach(device_t dev)
5560287be96SNate Lawson {
5570287be96SNate Lawson 	struct		acpi_toshiba_softc *sc;
5580287be96SNate Lawson 
5591ac10fa4SJohn Baldwin 	sc = device_get_softc(dev);
5600287be96SNate Lawson 	sc->video_handle = acpi_get_handle(dev);
5610287be96SNate Lawson 	return (0);
56265c0f491SNate Lawson }
563