xref: /openbsd/sys/dev/acpi/acpitoshiba.c (revision 3b9d585e)
1 /* $OpenBSD: acpitoshiba.c,v 1.17 2024/04/13 23:44:11 jsg Exp $ */
2 /*-
3  * Copyright (c) 2003 Hiroyuki Aizu <aizu@navi.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 
31 #include <dev/acpi/acpivar.h>
32 #include <dev/acpi/acpidev.h>
33 #include <dev/acpi/amltypes.h>
34 #include <dev/acpi/dsdt.h>
35 
36 #include <machine/apmvar.h>
37 #include <dev/wscons/wsconsio.h>
38 #include <dev/wscons/wsdisplayvar.h>
39 
40 /*
41  * Toshiba HCI interface definitions
42  *
43  * HCI is Toshiba's "Hardware Control Interface" which is supposed to
44  * be uniform across all their models.	Ideally we would just call
45  * dedicated ACPI methods instead of using this primitive interface.
46  * However, the ACPI methods seem to be incomplete in some areas (for
47  * example they allow setting, but not reading, the LCD brightness
48  * value), so this is still useful.
49  */
50 #define	METHOD_HCI			"GHCI"
51 #define	METHOD_HCI_ENABLE		"ENAB"
52 
53 /* Operations */
54 #define	HCI_SET				0xFF00
55 #define	HCI_GET				0xFE00
56 
57 /* Functions */
58 #define	HCI_REG_SYSTEM_EVENT		0x0016
59 #define	HCI_REG_VIDEO_OUTPUT		0x001C
60 #define	HCI_REG_LCD_BRIGHTNESS		0x002A
61 
62 /* Field definitions */
63 #define	HCI_LCD_BRIGHTNESS_BITS		3
64 #define	HCI_LCD_BRIGHTNESS_SHIFT	(16 - HCI_LCD_BRIGHTNESS_BITS)
65 #define	HCI_LCD_BRIGHTNESS_MAX		((1 << HCI_LCD_BRIGHTNESS_BITS) - 1)
66 #define	HCI_LCD_BRIGHTNESS_MIN		0
67 #define	HCI_VIDEO_OUTPUT_FLAG		0x0100
68 #define	HCI_VIDEO_OUTPUT_CYCLE_MIN	0
69 #define	HCI_VIDEO_OUTPUT_CYCLE_MAX	7
70 
71 /* HCI register definitions */
72 #define	HCI_WORDS			6 /* Number of register */
73 #define	HCI_REG_AX			0 /* Operation, then return value */
74 #define	HCI_REG_BX			1 /* Function */
75 #define	HCI_REG_CX			2 /* Argument (in or out) */
76 
77 /* Return codes */
78 #define	HCI_FAILURE			-1
79 #define	HCI_SUCCESS			0
80 
81 /* Toshiba fn_keys events */
82 #define	FN_KEY_SUSPEND			0x01BD
83 #define	FN_KEY_HIBERNATE		0x01BE
84 #define	FN_KEY_VIDEO_OUTPUT		0x01BF
85 #define	FN_KEY_BRIGHTNESS_DOWN		0x01C0
86 #define	FN_KEY_BRIGHTNESS_UP		0x01C1
87 
88 struct acpitoshiba_softc {
89 	struct device		 sc_dev;
90 	struct acpi_softc	*sc_acpi;
91 	struct aml_node		*sc_devnode;
92 
93 	uint32_t		 sc_brightness;
94 };
95 
96 int	toshiba_enable_events(struct acpitoshiba_softc *);
97 int	toshiba_read_events(struct acpitoshiba_softc *);
98 int	toshiba_match(struct device *, void *, void *);
99 void	toshiba_attach(struct device *, struct device *, void *);
100 int	toshiba_hotkey(struct aml_node *, int, void *);
101 int	toshiba_get_brightness(struct acpitoshiba_softc *, uint32_t *);
102 int	toshiba_set_brightness(struct acpitoshiba_softc *, uint32_t *);
103 int	toshiba_get_video_output(struct acpitoshiba_softc *, uint32_t *);
104 int	toshiba_set_video_output(struct acpitoshiba_softc *, uint32_t *);
105 void	toshiba_update_brightness(void *, int);
106 int	toshiba_fn_key_brightness_up(struct acpitoshiba_softc *);
107 int	toshiba_fn_key_brightness_down(struct acpitoshiba_softc *);
108 int	toshiba_fn_key_video_output(struct acpitoshiba_softc *);
109 
110 /* wconsole hook functions */
111 int	acpitoshiba_get_param(struct wsdisplay_param *);
112 int	acpitoshiba_set_param(struct wsdisplay_param *);
113 int	get_param_brightness(struct wsdisplay_param *);
114 int	set_param_brightness(struct wsdisplay_param *);
115 
116 const struct cfattach acpitoshiba_ca = {
117 	sizeof(struct acpitoshiba_softc), toshiba_match, toshiba_attach
118 };
119 
120 struct cfdriver acpitoshiba_cd = {
121 	NULL, "acpitoshiba", DV_DULL
122 };
123 
124 const char *acpitoshiba_hids[] = {
125 	"TOS6200",	/* Libretto */
126 	"TOS6207",	/* Dynabook */
127 	"TOS6208",	/* SPA40 */
128 	NULL
129 };
130 
131 int
get_param_brightness(struct wsdisplay_param * dp)132 get_param_brightness(struct wsdisplay_param *dp)
133 {
134 	struct acpitoshiba_softc *sc = acpitoshiba_cd.cd_devs[0];
135 
136 	if (sc != NULL) {
137 		/* default settings */
138 		dp->min = HCI_LCD_BRIGHTNESS_MIN;
139 		dp->max = HCI_LCD_BRIGHTNESS_MAX;
140 		dp->curval = sc->sc_brightness;
141 		return (0);
142 	}
143 
144 	return (1);
145 }
146 
147 int
acpitoshiba_get_param(struct wsdisplay_param * dp)148 acpitoshiba_get_param(struct wsdisplay_param *dp)
149 {
150 	int ret;
151 
152 	switch (dp->param) {
153 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
154 		ret = get_param_brightness(dp);
155 		return (ret);
156 	default:
157 		return (1);
158 	}
159 }
160 
161 int
set_param_brightness(struct wsdisplay_param * dp)162 set_param_brightness(struct wsdisplay_param *dp)
163 {
164 	struct acpitoshiba_softc *sc = acpitoshiba_cd.cd_devs[0];
165 
166 	if (sc != NULL) {
167 		if (dp->curval < HCI_LCD_BRIGHTNESS_MIN)
168 			dp->curval = HCI_LCD_BRIGHTNESS_MIN;
169 		if (dp->curval > HCI_LCD_BRIGHTNESS_MAX)
170 			dp->curval = HCI_LCD_BRIGHTNESS_MAX;
171 		sc->sc_brightness = dp->curval;
172 		acpi_addtask(sc->sc_acpi, toshiba_update_brightness, sc, 0);
173 		acpi_wakeup(sc->sc_acpi);
174 		return (0);
175 	}
176 
177 	return (1);
178 }
179 
180 int
acpitoshiba_set_param(struct wsdisplay_param * dp)181 acpitoshiba_set_param(struct wsdisplay_param *dp)
182 {
183 	int ret;
184 
185 	switch (dp->param) {
186 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
187 		ret = set_param_brightness(dp);
188 		return (ret);
189 	default:
190 		return (1);
191 	}
192 }
193 
194 void
toshiba_update_brightness(void * arg0,int arg1)195 toshiba_update_brightness(void *arg0, int arg1)
196 {
197 	struct acpitoshiba_softc *sc = arg0;
198 
199 	toshiba_set_brightness(sc, &sc->sc_brightness);
200 }
201 
202 int
toshiba_match(struct device * parent,void * match,void * aux)203 toshiba_match(struct device *parent, void *match, void *aux)
204 {
205 	struct acpi_attach_args *aa = aux;
206 	struct cfdata	      *cf = match;
207 
208 	if (acpi_matchhids(aa, acpitoshiba_hids, cf->cf_driver->cd_name))
209 		return (1);
210 
211 	if (aa->aaa_name == NULL ||
212 	    strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
213 	    aa->aaa_table != NULL)
214 		return (0);
215 
216 	return (1);
217 }
218 
219 int
toshiba_enable_events(struct acpitoshiba_softc * sc)220 toshiba_enable_events(struct acpitoshiba_softc *sc)
221 {
222 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI_ENABLE,
223 		    0, NULL, NULL)) {
224 		printf("%s: couldn't toggle METHOD_HCI_ENABLE\n", DEVNAME(sc));
225 		return (HCI_FAILURE);
226 	}
227 
228 	return (HCI_SUCCESS);
229 }
230 
231 int
toshiba_read_events(struct acpitoshiba_softc * sc)232 toshiba_read_events(struct acpitoshiba_softc *sc)
233 {
234 	struct aml_value args[HCI_WORDS];
235 	struct aml_value res;
236 	int i, val;
237 
238 	bzero(args, sizeof(args));
239 	bzero(&res, sizeof(res));
240 
241 	for (i = 0; i < HCI_WORDS; ++i)
242 		args[i].type = AML_OBJTYPE_INTEGER;
243 
244 	args[HCI_REG_AX].v_integer = HCI_GET;
245 	args[HCI_REG_BX].v_integer = HCI_REG_SYSTEM_EVENT;
246 
247 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI,
248 		    i, args, &res)) {
249 		printf("%s: couldn't toggle METHOD_HCI\n", DEVNAME(sc));
250 		return (HCI_FAILURE);
251 	}
252 
253 	/*
254 	 * We receive a package type so we need to get the event
255 	 * value from the HCI_REG_CX.
256 	 */
257 	val = aml_val2int(res.v_package[HCI_REG_CX]);
258 	aml_freevalue(&res);
259 
260 	return (val);
261 }
262 
263 void
toshiba_attach(struct device * parent,struct device * self,void * aux)264 toshiba_attach(struct device *parent, struct device *self, void *aux)
265 {
266 	struct acpitoshiba_softc *sc = (struct acpitoshiba_softc *)self;
267 	struct acpi_attach_args *aa = aux;
268 	int ret;
269 
270 	sc->sc_acpi = (struct acpi_softc *)parent;
271 	sc->sc_devnode = aa->aaa_node;
272 
273 	printf("\n");
274 
275 	/* enable events and hotkeys */
276 	ret = toshiba_enable_events(sc);
277 	if (ret != HCI_FAILURE) {
278 		/* Run toshiba_hotkey on button presses */
279 		aml_register_notify(sc->sc_devnode, aa->aaa_dev,
280 				toshiba_hotkey, sc, ACPIDEV_NOPOLL);
281 	}
282 
283 	ret = toshiba_get_brightness(sc, &sc->sc_brightness);
284 	if (ret != HCI_FAILURE) {
285 		/* wsconsctl purpose */
286 		ws_get_param = acpitoshiba_get_param;
287 		ws_set_param = acpitoshiba_set_param;
288 	}
289 }
290 
291 int
toshiba_fn_key_brightness_up(struct acpitoshiba_softc * sc)292 toshiba_fn_key_brightness_up(struct acpitoshiba_softc *sc)
293 {
294 	uint32_t brightness_level;
295 	int ret;
296 
297 	ret = toshiba_get_brightness(sc, &brightness_level);
298 	if (ret != HCI_FAILURE) {
299 
300 		if (brightness_level++ == HCI_LCD_BRIGHTNESS_MAX)
301 			brightness_level = HCI_LCD_BRIGHTNESS_MAX;
302 		else
303 			ret = toshiba_set_brightness(sc, &brightness_level);
304 	}
305 
306 	return (ret);
307 }
308 
309 int
toshiba_fn_key_brightness_down(struct acpitoshiba_softc * sc)310 toshiba_fn_key_brightness_down(struct acpitoshiba_softc *sc)
311 {
312 	uint32_t brightness_level;
313 	int ret;
314 
315 	ret = toshiba_get_brightness(sc, &brightness_level);
316 	if (ret != HCI_FAILURE) {
317 		if (brightness_level-- == HCI_LCD_BRIGHTNESS_MIN)
318 			brightness_level = HCI_LCD_BRIGHTNESS_MIN;
319 		else
320 			ret = toshiba_set_brightness(sc, &brightness_level);
321 	}
322 
323 	return (ret);
324 }
325 
326 int
toshiba_fn_key_video_output(struct acpitoshiba_softc * sc)327 toshiba_fn_key_video_output(struct acpitoshiba_softc *sc)
328 {
329 	uint32_t video_output;
330 	int ret;
331 
332 	ret = toshiba_get_video_output(sc, &video_output);
333 	if (ret != HCI_FAILURE) {
334 		video_output = (video_output + 1) % HCI_VIDEO_OUTPUT_CYCLE_MAX;
335 
336 		ret = toshiba_set_video_output(sc, &video_output);
337 	}
338 
339 	return (ret);
340 }
341 
342 int
toshiba_hotkey(struct aml_node * node,int notify,void * arg)343 toshiba_hotkey(struct aml_node *node, int notify, void *arg)
344 {
345 	struct acpitoshiba_softc *sc = arg;
346 	int event, ret = HCI_FAILURE;
347 
348 	event = toshiba_read_events(sc);
349 	if (!event)
350 		return (0);
351 
352 	switch (event) {
353 	case FN_KEY_BRIGHTNESS_UP:
354 		/* Increase brightness */
355 		ret = toshiba_fn_key_brightness_up(sc);
356 		break;
357 	case FN_KEY_BRIGHTNESS_DOWN:
358 		/* Decrease brightness */
359 		ret = toshiba_fn_key_brightness_down(sc);
360 		break;
361 	case FN_KEY_SUSPEND:
362 #ifndef SMALL_KERNEL
363 		if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ)) {
364 			acpi_addtask(sc->sc_acpi, acpi_sleep_task,
365 			    sc->sc_acpi, SLEEP_SUSPEND);
366 			ret = HCI_SUCCESS;
367 		}
368 #endif
369 		break;
370 	case FN_KEY_HIBERNATE:
371 #if defined(HIBERNATE) && !defined(SMALL_KERNEL)
372 		if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ)) {
373 			acpi_addtask(sc->sc_acpi, acpi_sleep_task,
374 			    sc->sc_acpi, SLEEP_HIBERNATE);
375 			ret = HCI_SUCCESS;
376 		}
377 #endif
378 		break;
379 	case FN_KEY_VIDEO_OUTPUT:
380 		/* Cycle through video outputs. */
381 		ret = toshiba_fn_key_video_output(sc);
382 		break;
383 	default:
384 		break;
385 	}
386 
387 	if (ret != HCI_SUCCESS)
388 		return (1);
389 
390 	return (0);
391 }
392 
393 int
toshiba_set_brightness(struct acpitoshiba_softc * sc,uint32_t * brightness)394 toshiba_set_brightness(struct acpitoshiba_softc *sc, uint32_t *brightness)
395 {
396 	struct aml_value args[HCI_WORDS];
397 	int i;
398 
399 	bzero(args, sizeof(args));
400 
401 	for (i = 0; i < HCI_WORDS; ++i)
402 		args[i].type = AML_OBJTYPE_INTEGER;
403 
404 	if ((*brightness < HCI_LCD_BRIGHTNESS_MIN) ||
405 	    (*brightness > HCI_LCD_BRIGHTNESS_MAX))
406 		return (HCI_FAILURE);
407 
408 	*brightness <<= HCI_LCD_BRIGHTNESS_SHIFT;
409 
410 	args[HCI_REG_AX].v_integer = HCI_SET;
411 	args[HCI_REG_BX].v_integer = HCI_REG_LCD_BRIGHTNESS;
412 	args[HCI_REG_CX].v_integer = *brightness;
413 
414 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI,
415 	    i, args, NULL)) {
416 		printf("%s: set brightness failed\n", DEVNAME(sc));
417 		return (HCI_FAILURE);
418 	}
419 
420 	sc->sc_brightness = *brightness;
421 	return (HCI_SUCCESS);
422 }
423 
424 int
toshiba_get_brightness(struct acpitoshiba_softc * sc,uint32_t * brightness)425 toshiba_get_brightness(struct acpitoshiba_softc *sc, uint32_t *brightness)
426 {
427 	struct aml_value args[HCI_WORDS];
428 	struct aml_value res;
429 	int i;
430 
431 	bzero(args, sizeof(args));
432 	bzero(&res, sizeof(res));
433 
434 	for (i = 0; i < HCI_WORDS; ++i)
435 		args[i].type = AML_OBJTYPE_INTEGER;
436 
437 	args[HCI_REG_AX].v_integer = HCI_GET;
438 	args[HCI_REG_BX].v_integer = HCI_REG_LCD_BRIGHTNESS;
439 
440 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI,
441 	    i, args, &res)) {
442 		printf("%s: get brightness failed\n", DEVNAME(sc));
443 		return (HCI_FAILURE);
444 	}
445 
446 	/*
447 	 * We receive a package type so we need to get the event
448 	 * value from the HCI_REG_CX.
449 	 */
450 	*brightness = aml_val2int(res.v_package[HCI_REG_CX]);
451 
452 	*brightness >>= HCI_LCD_BRIGHTNESS_SHIFT;
453 
454 	aml_freevalue(&res);
455 
456 	return (HCI_SUCCESS);
457 }
458 
459 int
toshiba_get_video_output(struct acpitoshiba_softc * sc,uint32_t * video_output)460 toshiba_get_video_output(struct acpitoshiba_softc *sc, uint32_t *video_output)
461 {
462 	struct aml_value res, args[HCI_WORDS];
463 	int i;
464 
465 	bzero(args, sizeof(args));
466 	bzero(&res, sizeof(res));
467 
468 	for (i = 0; i < HCI_WORDS; ++i)
469 		args[i].type = AML_OBJTYPE_INTEGER;
470 
471 	args[HCI_REG_AX].v_integer = HCI_GET;
472 	args[HCI_REG_BX].v_integer = HCI_REG_VIDEO_OUTPUT;
473 
474 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI,
475 	    i, args, &res)) {
476 		printf("%s: get video output failed\n", DEVNAME(sc));
477 		return (HCI_FAILURE);
478 	}
479 
480 	/*
481 	 * We receive a package type so we need to get the event
482 	 * value from the HCI_REG_CX.
483 	 */
484 	*video_output = aml_val2int(res.v_package[HCI_REG_CX]);
485 
486 	*video_output &= 0xff;
487 
488 	aml_freevalue(&res);
489 
490 	return (HCI_SUCCESS);
491 }
492 
493 int
toshiba_set_video_output(struct acpitoshiba_softc * sc,uint32_t * video_output)494 toshiba_set_video_output(struct acpitoshiba_softc *sc, uint32_t *video_output)
495 {
496 	struct aml_value args[HCI_WORDS];
497 	int i;
498 
499 	bzero(args, sizeof(args));
500 
501 	if ((*video_output < HCI_VIDEO_OUTPUT_CYCLE_MIN) ||
502 	    (*video_output > HCI_VIDEO_OUTPUT_CYCLE_MAX))
503 		return (HCI_FAILURE);
504 
505 	*video_output |= HCI_VIDEO_OUTPUT_FLAG;
506 
507 	for (i = 0; i < HCI_WORDS; ++i)
508 		args[i].type = AML_OBJTYPE_INTEGER;
509 
510 	args[HCI_REG_AX].v_integer = HCI_SET;
511 	args[HCI_REG_BX].v_integer = HCI_REG_VIDEO_OUTPUT;
512 	args[HCI_REG_CX].v_integer = *video_output;
513 
514 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI,
515 	    i, args, NULL)) {
516 		printf("%s: set video output failed\n", DEVNAME(sc));
517 		return (HCI_FAILURE);
518 	}
519 
520 	return (HCI_SUCCESS);
521 }
522