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, ¤t_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, ¤t_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