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