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