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