xref: /openbsd/sys/dev/acpi/acpitoshiba.c (revision 76d0caae)
1 /* $OpenBSD: acpitoshiba.c,v 1.13 2020/03/16 08:51:48 jasper 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 
94 int	toshiba_enable_events(struct acpitoshiba_softc *);
95 int	toshiba_read_events(struct acpitoshiba_softc *);
96 int	toshiba_match(struct device *, void *, void *);
97 void	toshiba_attach(struct device *, struct device *, void *);
98 int	toshiba_hotkey(struct aml_node *, int, void *);
99 int	toshiba_get_brightness(struct acpitoshiba_softc *, uint32_t *);
100 int	toshiba_set_brightness(struct acpitoshiba_softc *, uint32_t *);
101 int	toshiba_get_video_output(struct acpitoshiba_softc *, uint32_t *);
102 int	toshiba_set_video_output(struct acpitoshiba_softc *, uint32_t *);
103 int	toshiba_find_brightness(struct acpitoshiba_softc *, int *);
104 int	toshiba_fn_key_brightness_up(struct acpitoshiba_softc *);
105 int	toshiba_fn_key_brightness_down(struct acpitoshiba_softc *);
106 int	toshiba_fn_key_video_output(struct acpitoshiba_softc *);
107 
108 /* wconsole hook functions */
109 int	acpitoshiba_get_param(struct wsdisplay_param *);
110 int	acpitoshiba_set_param(struct wsdisplay_param *);
111 int	get_param_brightness(struct wsdisplay_param *);
112 int	set_param_brightness(struct wsdisplay_param *);
113 
114 struct cfattach acpitoshiba_ca = {
115 	sizeof(struct acpitoshiba_softc), toshiba_match, toshiba_attach
116 };
117 
118 struct cfdriver acpitoshiba_cd = {
119 	NULL, "acpitoshiba", DV_DULL
120 };
121 
122 const char *acpitoshiba_hids[] = {
123 	"TOS6200",	/* Libretto */
124 	"TOS6207",	/* Dynabook */
125 	"TOS6208",	/* SPA40 */
126 	NULL
127 };
128 
129 int
130 get_param_brightness(struct wsdisplay_param *dp)
131 {
132 	struct acpitoshiba_softc	*sc = NULL;
133 	int i, ret;
134 
135 	for (i = 0; i < acpitoshiba_cd.cd_ndevs; i++) {
136 		if (acpitoshiba_cd.cd_devs[i] == NULL)
137 			continue;
138 
139 		sc = (struct acpitoshiba_softc *)acpitoshiba_cd.cd_devs[i];
140 	}
141 
142 	if (sc != NULL) {
143 		rw_enter_write(&sc->sc_acpi->sc_lck);
144 
145 		/* default settings */
146 		dp->min = HCI_LCD_BRIGHTNESS_MIN;
147 		dp->max = HCI_LCD_BRIGHTNESS_MAX;
148 
149 		ret = toshiba_get_brightness(sc, &dp->curval);
150 
151 		rw_exit_write(&sc->sc_acpi->sc_lck);
152 
153 		if ((dp->curval != -1) && (ret != HCI_FAILURE) )
154 			return (0);
155 	}
156 
157 	return (1);
158 }
159 
160 int
161 acpitoshiba_get_param(struct wsdisplay_param *dp)
162 {
163 	int ret;
164 
165 	switch (dp->param) {
166 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
167 		ret = get_param_brightness(dp);
168 		return (ret);
169 	default:
170 		return (1);
171 	}
172 }
173 
174 int
175 set_param_brightness(struct wsdisplay_param *dp)
176 {
177 	struct acpitoshiba_softc	*sc = NULL;
178 	int i, ret;
179 
180 	for (i = 0; i < acpitoshiba_cd.cd_ndevs; i++) {
181 		if (acpitoshiba_cd.cd_devs[i] == NULL)
182 			continue;
183 
184 		sc = (struct acpitoshiba_softc *)acpitoshiba_cd.cd_devs[i];
185 	}
186 
187 	if (sc != NULL) {
188 		rw_enter_write(&sc->sc_acpi->sc_lck);
189 		ret = toshiba_find_brightness(sc, &dp->curval);
190 		rw_exit_write(&sc->sc_acpi->sc_lck);
191 
192 		if ((dp->curval != -1) && (ret != HCI_FAILURE))
193 			return (0);
194 	}
195 
196 	return (1);
197 }
198 
199 int
200 acpitoshiba_set_param(struct wsdisplay_param *dp)
201 {
202 	int ret;
203 
204 	switch (dp->param) {
205 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
206 		ret = set_param_brightness(dp);
207 		return (ret);
208 	default:
209 		return (1);
210 	}
211 }
212 
213 int
214 toshiba_find_brightness(struct acpitoshiba_softc *sc, int *new_blevel)
215 {
216 	int ret, current_blevel;
217 
218 	ret = toshiba_get_brightness(sc, &current_blevel);
219 	if (ret != HCI_SUCCESS)
220 		return (1);
221 
222 	if (current_blevel != *new_blevel) {
223 		if (*new_blevel >= HCI_LCD_BRIGHTNESS_MAX)
224 			*new_blevel = current_blevel = HCI_LCD_BRIGHTNESS_MAX;
225 		else if (*new_blevel <= HCI_LCD_BRIGHTNESS_MIN)
226 			*new_blevel = current_blevel = HCI_LCD_BRIGHTNESS_MIN;
227 		else
228 			current_blevel = *new_blevel;
229 
230 		ret = toshiba_set_brightness(sc, &current_blevel);
231 		if (ret != HCI_SUCCESS)
232 			return (1);
233 	}
234 
235 	return (0);
236 }
237 
238 int
239 toshiba_match(struct device *parent, void *match, void *aux)
240 {
241       struct acpi_attach_args *aa = aux;
242       struct cfdata	      *cf = match;
243 
244         if (acpi_matchhids(aa, acpitoshiba_hids, cf->cf_driver->cd_name))
245                 return (1);
246 
247 	if (aa->aaa_name == NULL ||
248 	   strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
249 	   aa->aaa_table != NULL)
250 	      return (0);
251 
252       return (1);
253 
254 }
255 
256 int
257 toshiba_enable_events(struct acpitoshiba_softc *sc)
258 {
259 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI_ENABLE,
260 		    0, NULL, NULL)) {
261 		printf("%s: couldn't toggle METHOD_HCI_ENABLE\n", DEVNAME(sc));
262 		return (HCI_FAILURE);
263 	}
264 
265 	return (HCI_SUCCESS);
266 }
267 
268 int
269 toshiba_read_events(struct acpitoshiba_softc *sc)
270 {
271 	struct aml_value args[HCI_WORDS];
272 	struct aml_value res;
273 	int i, val;
274 
275 	bzero(args, sizeof(args));
276 	bzero(&res, sizeof(res));
277 
278 	for (i = 0; i < HCI_WORDS; ++i)
279 		args[i].type = AML_OBJTYPE_INTEGER;
280 
281 	args[HCI_REG_AX].v_integer = HCI_GET;
282 	args[HCI_REG_BX].v_integer = HCI_REG_SYSTEM_EVENT;
283 
284 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI,
285 		    i, args, &res)) {
286 		printf("%s: couldn't toggle METHOD_HCI\n", DEVNAME(sc));
287 		return (HCI_FAILURE);
288 	}
289 
290 	/*
291 	 * We receive a package type so we need to get the event
292 	 * value from the HCI_REG_CX.
293 	 */
294 	val = aml_val2int(res.v_package[HCI_REG_CX]);
295 	aml_freevalue(&res);
296 
297 	return (val);
298 }
299 
300 void
301 toshiba_attach(struct device *parent, struct device *self, void *aux)
302 {
303 	struct acpitoshiba_softc *sc = (struct acpitoshiba_softc *)self;
304 	struct acpi_attach_args *aa = aux;
305 	int ret;
306 
307 	sc->sc_acpi = (struct acpi_softc *)parent;
308 	sc->sc_devnode = aa->aaa_node;
309 
310 	printf("\n");
311 
312 	/* enable events and hotkeys */
313 	ret = toshiba_enable_events(sc);
314 	if (ret != HCI_FAILURE) {
315 		/* Run toshiba_hotkey on button presses */
316 		aml_register_notify(sc->sc_devnode, aa->aaa_dev,
317 				toshiba_hotkey, sc, ACPIDEV_NOPOLL);
318 
319 		/* wsconsctl purpose */
320 		ws_get_param = acpitoshiba_get_param;
321 		ws_set_param = acpitoshiba_set_param;
322 	}
323 
324 }
325 
326 int
327 toshiba_fn_key_brightness_up(struct acpitoshiba_softc *sc)
328 {
329 	uint32_t brightness_level;
330 	int ret;
331 
332 	ret = toshiba_get_brightness(sc, &brightness_level);
333 	if (ret != HCI_FAILURE) {
334 
335 		if (brightness_level++ == HCI_LCD_BRIGHTNESS_MAX)
336 			brightness_level = HCI_LCD_BRIGHTNESS_MAX;
337 		else
338 			ret = toshiba_set_brightness(sc, &brightness_level);
339 	}
340 
341 	return (ret);
342 }
343 
344 int
345 toshiba_fn_key_brightness_down(struct acpitoshiba_softc *sc)
346 {
347 	uint32_t brightness_level;
348 	int ret;
349 
350 	ret = toshiba_get_brightness(sc, &brightness_level);
351 	if (ret != HCI_FAILURE) {
352 		if (brightness_level-- == HCI_LCD_BRIGHTNESS_MIN)
353 			brightness_level = HCI_LCD_BRIGHTNESS_MIN;
354 		else
355 			ret = toshiba_set_brightness(sc, &brightness_level);
356 	}
357 
358 	return (ret);
359 }
360 
361 int
362 toshiba_fn_key_video_output(struct acpitoshiba_softc *sc)
363 {
364 	uint32_t video_output;
365 	int ret;
366 
367 	ret = toshiba_get_video_output(sc, &video_output);
368 	if (ret != HCI_FAILURE) {
369 		video_output = (video_output + 1) % HCI_VIDEO_OUTPUT_CYCLE_MAX;
370 
371 		ret = toshiba_set_video_output(sc, &video_output);
372 	}
373 
374 	return (ret);
375 }
376 
377 int
378 toshiba_hotkey(struct aml_node *node, int notify, void *arg)
379 {
380 	struct acpitoshiba_softc *sc = arg;
381 	int event, ret = HCI_FAILURE;
382 
383 	event = toshiba_read_events(sc);
384 	if (!event)
385 		return (0);
386 
387 	switch (event) {
388 	case FN_KEY_BRIGHTNESS_UP:
389 		/* Increase brightness */
390 		ret = toshiba_fn_key_brightness_up(sc);
391 		break;
392 	case FN_KEY_BRIGHTNESS_DOWN:
393 		/* Decrease brightness */
394 		ret = toshiba_fn_key_brightness_down(sc);
395 		break;
396 	case FN_KEY_SUSPEND:
397 #ifndef SMALL_KERNEL
398 		if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ)) {
399 			acpi_addtask(sc->sc_acpi, acpi_sleep_task,
400 			    sc->sc_acpi, ACPI_SLEEP_SUSPEND);
401 			ret = HCI_SUCCESS;
402 		}
403 #endif
404 		break;
405 	case FN_KEY_HIBERNATE:
406 #if defined(HIBERNATE) && !defined(SMALL_KERNEL)
407 		if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ)) {
408 			acpi_addtask(sc->sc_acpi, acpi_sleep_task,
409 			    sc->sc_acpi, ACPI_SLEEP_HIBERNATE);
410 			ret = HCI_SUCCESS;
411 		}
412 #endif
413 		break;
414 	case FN_KEY_VIDEO_OUTPUT:
415 		/* Cycle through video outputs. */
416 		ret = toshiba_fn_key_video_output(sc);
417 		break;
418 	default:
419 		break;
420 	}
421 
422 	if (ret != HCI_SUCCESS)
423 		return (1);
424 
425 	return (0);
426 }
427 
428 int
429 toshiba_set_brightness(struct acpitoshiba_softc *sc, uint32_t *brightness)
430 {
431 	struct aml_value args[HCI_WORDS];
432 	int i;
433 
434 	bzero(args, sizeof(args));
435 
436 	for (i = 0; i < HCI_WORDS; ++i)
437 		args[i].type = AML_OBJTYPE_INTEGER;
438 
439 	if ((*brightness < HCI_LCD_BRIGHTNESS_MIN) ||
440 	    (*brightness > HCI_LCD_BRIGHTNESS_MAX))
441 		return (HCI_FAILURE);
442 
443 	*brightness <<= HCI_LCD_BRIGHTNESS_SHIFT;
444 
445 	args[HCI_REG_AX].v_integer = HCI_SET;
446 	args[HCI_REG_BX].v_integer = HCI_REG_LCD_BRIGHTNESS;
447 	args[HCI_REG_CX].v_integer = *brightness;
448 
449 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI,
450 	    i, args, NULL)) {
451 		printf("%s: set brightness failed\n", DEVNAME(sc));
452 		return (HCI_FAILURE);
453 	}
454 
455 	return (HCI_SUCCESS);
456 }
457 
458 int
459 toshiba_get_brightness(struct acpitoshiba_softc *sc, uint32_t *brightness)
460 {
461 	struct aml_value args[HCI_WORDS];
462 	struct aml_value res;
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_LCD_BRIGHTNESS;
473 
474 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI,
475 	    i, args, &res)) {
476 		printf("%s: get brightness 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 	*brightness = aml_val2int(res.v_package[HCI_REG_CX]);
485 
486 	*brightness >>= HCI_LCD_BRIGHTNESS_SHIFT;
487 
488 	aml_freevalue(&res);
489 
490 	return (HCI_SUCCESS);
491 }
492 
493 int
494 toshiba_get_video_output(struct acpitoshiba_softc *sc, uint32_t *video_output)
495 {
496 	struct aml_value res, args[HCI_WORDS];
497 	int i;
498 
499 	bzero(args, sizeof(args));
500 	bzero(&res, sizeof(res));
501 
502 	for (i = 0; i < HCI_WORDS; ++i)
503 		args[i].type = AML_OBJTYPE_INTEGER;
504 
505 	args[HCI_REG_AX].v_integer = HCI_GET;
506 	args[HCI_REG_BX].v_integer = HCI_REG_VIDEO_OUTPUT;
507 
508 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI,
509 	    i, args, &res)) {
510 		printf("%s: get video output failed\n", DEVNAME(sc));
511 		return (HCI_FAILURE);
512 	}
513 
514 	/*
515 	 * We receive a package type so we need to get the event
516 	 * value from the HCI_REG_CX.
517 	 */
518 	*video_output = aml_val2int(res.v_package[HCI_REG_CX]);
519 
520 	*video_output &= 0xff;
521 
522 	aml_freevalue(&res);
523 
524 	return (HCI_SUCCESS);
525 }
526 
527 int
528 toshiba_set_video_output(struct acpitoshiba_softc *sc, uint32_t *video_output)
529 {
530 	struct aml_value args[HCI_WORDS];
531 	int i;
532 
533 	bzero(args, sizeof(args));
534 
535 	if ((*video_output < HCI_VIDEO_OUTPUT_CYCLE_MIN) ||
536 	    (*video_output > HCI_VIDEO_OUTPUT_CYCLE_MAX))
537 		return (HCI_FAILURE);
538 
539 	*video_output |= HCI_VIDEO_OUTPUT_FLAG;
540 
541 	for (i = 0; i < HCI_WORDS; ++i)
542 		args[i].type = AML_OBJTYPE_INTEGER;
543 
544 	args[HCI_REG_AX].v_integer = HCI_SET;
545 	args[HCI_REG_BX].v_integer = HCI_REG_VIDEO_OUTPUT;
546 	args[HCI_REG_CX].v_integer = *video_output;
547 
548 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI,
549 	    i, args, NULL)) {
550 		printf("%s: set video output failed\n", DEVNAME(sc));
551 		return (HCI_FAILURE);
552 	}
553 
554 	return (HCI_SUCCESS);
555 }
556