1 /* $OpenBSD: acpitoshiba.c,v 1.16 2022/04/06 18:59:27 naddy 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 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 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 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 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 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 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 220 int 221 toshiba_enable_events(struct acpitoshiba_softc *sc) 222 { 223 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI_ENABLE, 224 0, NULL, NULL)) { 225 printf("%s: couldn't toggle METHOD_HCI_ENABLE\n", DEVNAME(sc)); 226 return (HCI_FAILURE); 227 } 228 229 return (HCI_SUCCESS); 230 } 231 232 int 233 toshiba_read_events(struct acpitoshiba_softc *sc) 234 { 235 struct aml_value args[HCI_WORDS]; 236 struct aml_value res; 237 int i, val; 238 239 bzero(args, sizeof(args)); 240 bzero(&res, sizeof(res)); 241 242 for (i = 0; i < HCI_WORDS; ++i) 243 args[i].type = AML_OBJTYPE_INTEGER; 244 245 args[HCI_REG_AX].v_integer = HCI_GET; 246 args[HCI_REG_BX].v_integer = HCI_REG_SYSTEM_EVENT; 247 248 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI, 249 i, args, &res)) { 250 printf("%s: couldn't toggle METHOD_HCI\n", DEVNAME(sc)); 251 return (HCI_FAILURE); 252 } 253 254 /* 255 * We receive a package type so we need to get the event 256 * value from the HCI_REG_CX. 257 */ 258 val = aml_val2int(res.v_package[HCI_REG_CX]); 259 aml_freevalue(&res); 260 261 return (val); 262 } 263 264 void 265 toshiba_attach(struct device *parent, struct device *self, void *aux) 266 { 267 struct acpitoshiba_softc *sc = (struct acpitoshiba_softc *)self; 268 struct acpi_attach_args *aa = aux; 269 int ret; 270 271 sc->sc_acpi = (struct acpi_softc *)parent; 272 sc->sc_devnode = aa->aaa_node; 273 274 printf("\n"); 275 276 /* enable events and hotkeys */ 277 ret = toshiba_enable_events(sc); 278 if (ret != HCI_FAILURE) { 279 /* Run toshiba_hotkey on button presses */ 280 aml_register_notify(sc->sc_devnode, aa->aaa_dev, 281 toshiba_hotkey, sc, ACPIDEV_NOPOLL); 282 } 283 284 ret = toshiba_get_brightness(sc, &sc->sc_brightness); 285 if (ret != HCI_FAILURE) { 286 /* wsconsctl purpose */ 287 ws_get_param = acpitoshiba_get_param; 288 ws_set_param = acpitoshiba_set_param; 289 } 290 } 291 292 int 293 toshiba_fn_key_brightness_up(struct acpitoshiba_softc *sc) 294 { 295 uint32_t brightness_level; 296 int ret; 297 298 ret = toshiba_get_brightness(sc, &brightness_level); 299 if (ret != HCI_FAILURE) { 300 301 if (brightness_level++ == HCI_LCD_BRIGHTNESS_MAX) 302 brightness_level = HCI_LCD_BRIGHTNESS_MAX; 303 else 304 ret = toshiba_set_brightness(sc, &brightness_level); 305 } 306 307 return (ret); 308 } 309 310 int 311 toshiba_fn_key_brightness_down(struct acpitoshiba_softc *sc) 312 { 313 uint32_t brightness_level; 314 int ret; 315 316 ret = toshiba_get_brightness(sc, &brightness_level); 317 if (ret != HCI_FAILURE) { 318 if (brightness_level-- == HCI_LCD_BRIGHTNESS_MIN) 319 brightness_level = HCI_LCD_BRIGHTNESS_MIN; 320 else 321 ret = toshiba_set_brightness(sc, &brightness_level); 322 } 323 324 return (ret); 325 } 326 327 int 328 toshiba_fn_key_video_output(struct acpitoshiba_softc *sc) 329 { 330 uint32_t video_output; 331 int ret; 332 333 ret = toshiba_get_video_output(sc, &video_output); 334 if (ret != HCI_FAILURE) { 335 video_output = (video_output + 1) % HCI_VIDEO_OUTPUT_CYCLE_MAX; 336 337 ret = toshiba_set_video_output(sc, &video_output); 338 } 339 340 return (ret); 341 } 342 343 int 344 toshiba_hotkey(struct aml_node *node, int notify, void *arg) 345 { 346 struct acpitoshiba_softc *sc = arg; 347 int event, ret = HCI_FAILURE; 348 349 event = toshiba_read_events(sc); 350 if (!event) 351 return (0); 352 353 switch (event) { 354 case FN_KEY_BRIGHTNESS_UP: 355 /* Increase brightness */ 356 ret = toshiba_fn_key_brightness_up(sc); 357 break; 358 case FN_KEY_BRIGHTNESS_DOWN: 359 /* Decrease brightness */ 360 ret = toshiba_fn_key_brightness_down(sc); 361 break; 362 case FN_KEY_SUSPEND: 363 #ifndef SMALL_KERNEL 364 if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ)) { 365 acpi_addtask(sc->sc_acpi, acpi_sleep_task, 366 sc->sc_acpi, SLEEP_SUSPEND); 367 ret = HCI_SUCCESS; 368 } 369 #endif 370 break; 371 case FN_KEY_HIBERNATE: 372 #if defined(HIBERNATE) && !defined(SMALL_KERNEL) 373 if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ)) { 374 acpi_addtask(sc->sc_acpi, acpi_sleep_task, 375 sc->sc_acpi, SLEEP_HIBERNATE); 376 ret = HCI_SUCCESS; 377 } 378 #endif 379 break; 380 case FN_KEY_VIDEO_OUTPUT: 381 /* Cycle through video outputs. */ 382 ret = toshiba_fn_key_video_output(sc); 383 break; 384 default: 385 break; 386 } 387 388 if (ret != HCI_SUCCESS) 389 return (1); 390 391 return (0); 392 } 393 394 int 395 toshiba_set_brightness(struct acpitoshiba_softc *sc, uint32_t *brightness) 396 { 397 struct aml_value args[HCI_WORDS]; 398 int i; 399 400 bzero(args, sizeof(args)); 401 402 for (i = 0; i < HCI_WORDS; ++i) 403 args[i].type = AML_OBJTYPE_INTEGER; 404 405 if ((*brightness < HCI_LCD_BRIGHTNESS_MIN) || 406 (*brightness > HCI_LCD_BRIGHTNESS_MAX)) 407 return (HCI_FAILURE); 408 409 *brightness <<= HCI_LCD_BRIGHTNESS_SHIFT; 410 411 args[HCI_REG_AX].v_integer = HCI_SET; 412 args[HCI_REG_BX].v_integer = HCI_REG_LCD_BRIGHTNESS; 413 args[HCI_REG_CX].v_integer = *brightness; 414 415 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI, 416 i, args, NULL)) { 417 printf("%s: set brightness failed\n", DEVNAME(sc)); 418 return (HCI_FAILURE); 419 } 420 421 sc->sc_brightness = *brightness; 422 return (HCI_SUCCESS); 423 } 424 425 int 426 toshiba_get_brightness(struct acpitoshiba_softc *sc, uint32_t *brightness) 427 { 428 struct aml_value args[HCI_WORDS]; 429 struct aml_value res; 430 int i; 431 432 bzero(args, sizeof(args)); 433 bzero(&res, sizeof(res)); 434 435 for (i = 0; i < HCI_WORDS; ++i) 436 args[i].type = AML_OBJTYPE_INTEGER; 437 438 args[HCI_REG_AX].v_integer = HCI_GET; 439 args[HCI_REG_BX].v_integer = HCI_REG_LCD_BRIGHTNESS; 440 441 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI, 442 i, args, &res)) { 443 printf("%s: get brightness failed\n", DEVNAME(sc)); 444 return (HCI_FAILURE); 445 } 446 447 /* 448 * We receive a package type so we need to get the event 449 * value from the HCI_REG_CX. 450 */ 451 *brightness = aml_val2int(res.v_package[HCI_REG_CX]); 452 453 *brightness >>= HCI_LCD_BRIGHTNESS_SHIFT; 454 455 aml_freevalue(&res); 456 457 return (HCI_SUCCESS); 458 } 459 460 int 461 toshiba_get_video_output(struct acpitoshiba_softc *sc, uint32_t *video_output) 462 { 463 struct aml_value res, args[HCI_WORDS]; 464 int i; 465 466 bzero(args, sizeof(args)); 467 bzero(&res, sizeof(res)); 468 469 for (i = 0; i < HCI_WORDS; ++i) 470 args[i].type = AML_OBJTYPE_INTEGER; 471 472 args[HCI_REG_AX].v_integer = HCI_GET; 473 args[HCI_REG_BX].v_integer = HCI_REG_VIDEO_OUTPUT; 474 475 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI, 476 i, args, &res)) { 477 printf("%s: get video output failed\n", DEVNAME(sc)); 478 return (HCI_FAILURE); 479 } 480 481 /* 482 * We receive a package type so we need to get the event 483 * value from the HCI_REG_CX. 484 */ 485 *video_output = aml_val2int(res.v_package[HCI_REG_CX]); 486 487 *video_output &= 0xff; 488 489 aml_freevalue(&res); 490 491 return (HCI_SUCCESS); 492 } 493 494 int 495 toshiba_set_video_output(struct acpitoshiba_softc *sc, uint32_t *video_output) 496 { 497 struct aml_value args[HCI_WORDS]; 498 int i; 499 500 bzero(args, sizeof(args)); 501 502 if ((*video_output < HCI_VIDEO_OUTPUT_CYCLE_MIN) || 503 (*video_output > HCI_VIDEO_OUTPUT_CYCLE_MAX)) 504 return (HCI_FAILURE); 505 506 *video_output |= HCI_VIDEO_OUTPUT_FLAG; 507 508 for (i = 0; i < HCI_WORDS; ++i) 509 args[i].type = AML_OBJTYPE_INTEGER; 510 511 args[HCI_REG_AX].v_integer = HCI_SET; 512 args[HCI_REG_BX].v_integer = HCI_REG_VIDEO_OUTPUT; 513 args[HCI_REG_CX].v_integer = *video_output; 514 515 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, METHOD_HCI, 516 i, args, NULL)) { 517 printf("%s: set video output failed\n", DEVNAME(sc)); 518 return (HCI_FAILURE); 519 } 520 521 return (HCI_SUCCESS); 522 } 523