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