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