1 /*- 2 * Copyright (c) 2002-2003 Taku YAMAMOTO <taku@cent.saitama-u.ac.jp> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $Id: acpi_vid.c,v 1.4 2003/10/13 10:07:36 taku Exp $ 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/kernel.h> 34 #include <sys/malloc.h> 35 #include <sys/module.h> 36 #include <sys/bus.h> 37 #include <sys/power.h> 38 #include <sys/queue.h> 39 #include <sys/sysctl.h> 40 41 #include <contrib/dev/acpica/acpi.h> 42 #include <dev/acpica/acpivar.h> 43 44 /* ACPI video extension driver. */ 45 struct acpi_video_output { 46 ACPI_HANDLE handle; 47 UINT32 adr; 48 STAILQ_ENTRY(acpi_video_output) vo_next; 49 struct { 50 int num; 51 STAILQ_ENTRY(acpi_video_output) next; 52 } vo_unit; 53 int vo_brightness; 54 int vo_fullpower; 55 int vo_economy; 56 int vo_numlevels; 57 int *vo_levels; 58 struct sysctl_ctx_list vo_sysctl_ctx; 59 struct sysctl_oid *vo_sysctl_tree; 60 }; 61 62 STAILQ_HEAD(acpi_video_output_queue, acpi_video_output); 63 64 struct acpi_video_softc { 65 device_t device; 66 ACPI_HANDLE handle; 67 struct acpi_video_output_queue vid_outputs; 68 eventhandler_tag vid_pwr_evh; 69 }; 70 71 /* interfaces */ 72 static int acpi_video_modevent(struct module*, int, void *); 73 static int acpi_video_probe(device_t); 74 static int acpi_video_attach(device_t); 75 static int acpi_video_detach(device_t); 76 static int acpi_video_shutdown(device_t); 77 static void acpi_video_notify_handler(ACPI_HANDLE, UINT32, void *); 78 static void acpi_video_power_profile(void *); 79 static void acpi_video_bind_outputs(struct acpi_video_softc *); 80 static struct acpi_video_output *acpi_video_vo_init(UINT32); 81 static void acpi_video_vo_bind(struct acpi_video_output *, ACPI_HANDLE); 82 static void acpi_video_vo_destroy(struct acpi_video_output *); 83 static int acpi_video_vo_check_level(struct acpi_video_output *, int); 84 static int acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS); 85 static int acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS); 86 static int acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS); 87 static int acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS); 88 89 /* operations */ 90 static void vid_set_switch_policy(ACPI_HANDLE, UINT32); 91 static int vid_enum_outputs(ACPI_HANDLE, 92 void(*)(ACPI_HANDLE, UINT32, void *), void *); 93 static int vo_get_brightness_levels(ACPI_HANDLE, int **); 94 static void vo_set_brightness(ACPI_HANDLE, int); 95 static UINT32 vo_get_device_status(ACPI_HANDLE); 96 static UINT32 vo_get_graphics_state(ACPI_HANDLE); 97 static void vo_set_device_state(ACPI_HANDLE, UINT32); 98 99 /* events */ 100 #define VID_NOTIFY_SWITCHED 0x80 101 #define VID_NOTIFY_REPROBE 0x81 102 103 /* _DOS (Enable/Disable Output Switching) argument bits */ 104 #define DOS_SWITCH_MASK 3 105 #define DOS_SWITCH_BY_OSPM 0 106 #define DOS_SWITCH_BY_BIOS 1 107 #define DOS_SWITCH_LOCKED 2 108 #define DOS_BRIGHTNESS_BY_BIOS (1 << 2) 109 110 /* _DOD and subdev's _ADR */ 111 #define DOD_DEVID_MASK 0xffff 112 #define DOD_DEVID_MONITOR 0x0100 113 #define DOD_DEVID_PANEL 0x0110 114 #define DOD_DEVID_TV 0x0200 115 #define DOD_BIOS (1 << 16) 116 #define DOD_NONVGA (1 << 17) 117 #define DOD_HEAD_ID_SHIFT 18 118 #define DOD_HEAD_ID_BITS 3 119 #define DOD_HEAD_ID_MASK \ 120 (((1 << DOD_HEAD_ID_BITS) - 1) << DOD_HEAD_ID_SHIFT) 121 122 /* _BCL related constants */ 123 #define BCL_FULLPOWER 0 124 #define BCL_ECONOMY 1 125 126 /* _DCS (Device Currrent Status) value bits and masks. */ 127 #define DCS_EXISTS (1 << 0) 128 #define DCS_ACTIVE (1 << 1) 129 #define DCS_READY (1 << 2) 130 #define DCS_FUNCTIONAL (1 << 3) 131 #define DCS_ATTACHED (1 << 4) 132 133 /* _DSS (Device Set Status) argument bits and masks. */ 134 #define DSS_INACTIVE 0 135 #define DSS_ACTIVE (1 << 0) 136 #define DSS_SETNEXT (1 << 30) 137 #define DSS_COMMIT (1 << 31) 138 139 static device_method_t acpi_video_methods[] = { 140 DEVMETHOD(device_probe, acpi_video_probe), 141 DEVMETHOD(device_attach, acpi_video_attach), 142 DEVMETHOD(device_detach, acpi_video_detach), 143 DEVMETHOD(device_shutdown, acpi_video_shutdown), 144 { 0, 0 } 145 }; 146 147 static driver_t acpi_video_driver = { 148 "acpi_video", 149 acpi_video_methods, 150 sizeof(struct acpi_video_softc), 151 }; 152 153 static devclass_t acpi_video_devclass; 154 155 DRIVER_MODULE(acpi_video, pci, acpi_video_driver, acpi_video_devclass, 156 acpi_video_modevent, NULL); 157 MODULE_DEPEND(acpi_video, acpi, 1, 1, 1); 158 159 static struct sysctl_ctx_list acpi_video_sysctl_ctx; 160 static struct sysctl_oid *acpi_video_sysctl_tree; 161 static struct acpi_video_output_queue lcd_units, crt_units, tv_units, 162 other_units; 163 164 ACPI_SERIAL_DECL(video, "ACPI video"); 165 MALLOC_DEFINE(M_ACPIVIDEO, "acpivideo", "ACPI video extension"); 166 167 static int 168 acpi_video_modevent(struct module *mod __unused, int evt, void *cookie __unused) 169 { 170 int err; 171 172 err = 0; 173 switch (evt) { 174 case MOD_LOAD: 175 sysctl_ctx_init(&acpi_video_sysctl_ctx); 176 STAILQ_INIT(&lcd_units); 177 STAILQ_INIT(&crt_units); 178 STAILQ_INIT(&tv_units); 179 STAILQ_INIT(&other_units); 180 break; 181 case MOD_UNLOAD: 182 sysctl_ctx_free(&acpi_video_sysctl_ctx); 183 acpi_video_sysctl_tree = NULL; 184 break; 185 default: 186 err = EINVAL; 187 } 188 189 return (err); 190 } 191 192 static int 193 acpi_video_probe(device_t dev) 194 { 195 ACPI_HANDLE devh, h; 196 ACPI_OBJECT_TYPE t_dos; 197 198 devh = acpi_get_handle(dev); 199 if (acpi_disabled("video") || 200 ACPI_FAILURE(AcpiGetHandle(devh, "_DOD", &h)) || 201 ACPI_FAILURE(AcpiGetHandle(devh, "_DOS", &h)) || 202 ACPI_FAILURE(AcpiGetType(h, &t_dos)) || 203 t_dos != ACPI_TYPE_METHOD) 204 return (ENXIO); 205 206 device_set_desc(dev, "ACPI video extension"); 207 return (0); 208 } 209 210 static int 211 acpi_video_attach(device_t dev) 212 { 213 struct acpi_softc *acpi_sc; 214 struct acpi_video_softc *sc; 215 216 sc = device_get_softc(dev); 217 218 acpi_sc = devclass_get_softc(devclass_find("acpi"), 0); 219 if (acpi_sc == NULL) 220 return (ENXIO); 221 if (acpi_video_sysctl_tree == NULL) { 222 acpi_video_sysctl_tree = SYSCTL_ADD_NODE(&acpi_video_sysctl_ctx, 223 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 224 OID_AUTO, "video", CTLFLAG_RD, 0, 225 "video extension control"); 226 } 227 228 sc->device = dev; 229 sc->handle = acpi_get_handle(dev); 230 STAILQ_INIT(&sc->vid_outputs); 231 232 AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 233 acpi_video_notify_handler, sc); 234 sc->vid_pwr_evh = EVENTHANDLER_REGISTER(power_profile_change, 235 acpi_video_power_profile, sc, 0); 236 237 ACPI_SERIAL_BEGIN(video); 238 acpi_video_bind_outputs(sc); 239 ACPI_SERIAL_END(video); 240 241 /* 242 * Notify the BIOS that we want to switch both active outputs and 243 * brightness levels. 244 */ 245 vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_OSPM | 246 DOS_BRIGHTNESS_BY_BIOS); 247 248 acpi_video_power_profile(sc); 249 250 return (0); 251 } 252 253 static int 254 acpi_video_detach(device_t dev) 255 { 256 struct acpi_video_softc *sc; 257 struct acpi_video_output *vo, *vn; 258 259 sc = device_get_softc(dev); 260 261 vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); 262 EVENTHANDLER_DEREGISTER(power_profile_change, sc->vid_pwr_evh); 263 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 264 acpi_video_notify_handler); 265 266 ACPI_SERIAL_BEGIN(video); 267 for (vo = STAILQ_FIRST(&sc->vid_outputs); vo != NULL; vo = vn) { 268 vn = STAILQ_NEXT(vo, vo_next); 269 acpi_video_vo_destroy(vo); 270 } 271 ACPI_SERIAL_END(video); 272 273 return (0); 274 } 275 276 static int 277 acpi_video_shutdown(device_t dev) 278 { 279 struct acpi_video_softc *sc; 280 281 sc = device_get_softc(dev); 282 vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); 283 284 return (0); 285 } 286 287 static void 288 acpi_video_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 289 { 290 struct acpi_video_softc *sc; 291 struct acpi_video_output *vo, *vo_tmp; 292 ACPI_HANDLE lasthand; 293 UINT32 dcs, dss, dss_p; 294 295 sc = (struct acpi_video_softc *)context; 296 297 switch (notify) { 298 case VID_NOTIFY_SWITCHED: 299 dss_p = 0; 300 lasthand = NULL; 301 ACPI_SERIAL_BEGIN(video); 302 STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 303 dss = vo_get_graphics_state(vo->handle); 304 dcs = vo_get_device_status(vo->handle); 305 if (!(dcs & DCS_READY)) 306 dss = DSS_INACTIVE; 307 if (((dcs & DCS_ACTIVE) && dss == DSS_INACTIVE) || 308 (!(dcs & DCS_ACTIVE) && dss == DSS_ACTIVE)) { 309 if (lasthand != NULL) 310 vo_set_device_state(lasthand, dss_p); 311 dss_p = dss; 312 lasthand = vo->handle; 313 } 314 } 315 if (lasthand != NULL) 316 vo_set_device_state(lasthand, dss_p|DSS_COMMIT); 317 ACPI_SERIAL_END(video); 318 break; 319 case VID_NOTIFY_REPROBE: 320 ACPI_SERIAL_BEGIN(video); 321 STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) 322 vo->handle = NULL; 323 acpi_video_bind_outputs(sc); 324 STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vo_tmp) { 325 if (vo->handle == NULL) { 326 STAILQ_REMOVE(&sc->vid_outputs, vo, 327 acpi_video_output, vo_next); 328 acpi_video_vo_destroy(vo); 329 } 330 } 331 ACPI_SERIAL_END(video); 332 break; 333 default: 334 device_printf(sc->device, "unknown notify event 0x%x\n", 335 notify); 336 } 337 } 338 339 static void 340 acpi_video_power_profile(void *context) 341 { 342 int state; 343 struct acpi_video_softc *sc; 344 struct acpi_video_output *vo; 345 346 sc = context; 347 state = power_profile_get_state(); 348 if (state != POWER_PROFILE_PERFORMANCE && 349 state != POWER_PROFILE_ECONOMY) 350 return; 351 352 ACPI_SERIAL_BEGIN(video); 353 STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 354 if (vo->vo_levels != NULL && vo->vo_brightness == -1) 355 vo_set_brightness(vo->handle, 356 state == POWER_PROFILE_ECONOMY ? 357 vo->vo_economy : vo->vo_fullpower); 358 } 359 ACPI_SERIAL_END(video); 360 } 361 362 static void 363 acpi_video_bind_outputs_subr(ACPI_HANDLE handle, UINT32 adr, void *context) 364 { 365 struct acpi_video_softc *sc; 366 struct acpi_video_output *vo; 367 368 ACPI_SERIAL_ASSERT(video); 369 sc = context; 370 371 STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 372 if (vo->adr == adr) { 373 acpi_video_vo_bind(vo, handle); 374 return; 375 } 376 } 377 vo = acpi_video_vo_init(adr); 378 if (vo != NULL) { 379 acpi_video_vo_bind(vo, handle); 380 STAILQ_INSERT_TAIL(&sc->vid_outputs, vo, vo_next); 381 } 382 } 383 384 static void 385 acpi_video_bind_outputs(struct acpi_video_softc *sc) 386 { 387 388 ACPI_SERIAL_ASSERT(video); 389 vid_enum_outputs(sc->handle, acpi_video_bind_outputs_subr, sc); 390 } 391 392 static struct acpi_video_output * 393 acpi_video_vo_init(UINT32 adr) 394 { 395 struct acpi_video_output *vn, *vo, *vp; 396 int n, x; 397 char name[8], env[32]; 398 const char *type, *desc; 399 struct acpi_video_output_queue *voqh; 400 401 ACPI_SERIAL_ASSERT(video); 402 switch (adr & DOD_DEVID_MASK) { 403 case DOD_DEVID_MONITOR: 404 desc = "CRT monitor"; 405 type = "crt"; 406 voqh = &crt_units; 407 break; 408 case DOD_DEVID_PANEL: 409 desc = "LCD panel"; 410 type = "lcd"; 411 voqh = &lcd_units; 412 break; 413 case DOD_DEVID_TV: 414 desc = "TV"; 415 type = "tv"; 416 voqh = &tv_units; 417 break; 418 default: 419 desc = "unknown output"; 420 type = "out"; 421 voqh = &other_units; 422 } 423 424 n = 0; 425 vn = vp = NULL; 426 STAILQ_FOREACH(vn, voqh, vo_unit.next) { 427 if (vn->vo_unit.num != n) 428 break; 429 vp = vn; 430 n++; 431 } 432 433 snprintf(name, sizeof(name), "%s%d", type, n); 434 435 vo = malloc(sizeof(*vo), M_ACPIVIDEO, M_NOWAIT); 436 if (vo != NULL) { 437 vo->handle = NULL; 438 vo->adr = adr; 439 vo->vo_unit.num = n; 440 vo->vo_brightness = -1; 441 vo->vo_fullpower = -1; /* TODO: override with tunables */ 442 vo->vo_economy = -1; 443 vo->vo_numlevels = 0; 444 vo->vo_levels = NULL; 445 snprintf(env, sizeof(env), "hw.acpi.video.%s.fullpower", name); 446 if (getenv_int(env, &x)) 447 vo->vo_fullpower = x; 448 snprintf(env, sizeof(env), "hw.acpi.video.%s.economy", name); 449 if (getenv_int(env, &x)) 450 vo->vo_economy = x; 451 452 sysctl_ctx_init(&vo->vo_sysctl_ctx); 453 if (vp != NULL) 454 STAILQ_INSERT_AFTER(voqh, vp, vo, vo_unit.next); 455 else 456 STAILQ_INSERT_TAIL(voqh, vo, vo_unit.next); 457 if (acpi_video_sysctl_tree != NULL) 458 vo->vo_sysctl_tree = 459 SYSCTL_ADD_NODE(&vo->vo_sysctl_ctx, 460 SYSCTL_CHILDREN(acpi_video_sysctl_tree), 461 OID_AUTO, name, CTLFLAG_RD, 0, desc); 462 if (vo->vo_sysctl_tree != NULL) { 463 SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 464 SYSCTL_CHILDREN(vo->vo_sysctl_tree), 465 OID_AUTO, "active", 466 CTLTYPE_INT|CTLFLAG_RW, vo, 0, 467 acpi_video_vo_active_sysctl, "I", 468 "current activity of this device"); 469 SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 470 SYSCTL_CHILDREN(vo->vo_sysctl_tree), 471 OID_AUTO, "brightness", 472 CTLTYPE_INT|CTLFLAG_RW, vo, 0, 473 acpi_video_vo_bright_sysctl, "I", 474 "current brightness level"); 475 SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 476 SYSCTL_CHILDREN(vo->vo_sysctl_tree), 477 OID_AUTO, "fullpower", 478 CTLTYPE_INT|CTLFLAG_RW, vo, 479 POWER_PROFILE_PERFORMANCE, 480 acpi_video_vo_presets_sysctl, "I", 481 "preset level for full power mode"); 482 SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 483 SYSCTL_CHILDREN(vo->vo_sysctl_tree), 484 OID_AUTO, "economy", 485 CTLTYPE_INT|CTLFLAG_RW, vo, 486 POWER_PROFILE_ECONOMY, 487 acpi_video_vo_presets_sysctl, "I", 488 "preset level for economy mode"); 489 SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 490 SYSCTL_CHILDREN(vo->vo_sysctl_tree), 491 OID_AUTO, "levels", 492 CTLTYPE_OPAQUE|CTLFLAG_RD, vo, 0, 493 acpi_video_vo_levels_sysctl, "I", 494 "supported brightness levels"); 495 } else 496 printf("%s: sysctl node creation failed\n", type); 497 } else 498 printf("%s: softc allocation failed\n", type); 499 500 if (bootverbose) { 501 printf("found %s(%x)", desc, adr & DOD_DEVID_MASK); 502 if (adr & DOD_BIOS) 503 printf(", detectable by BIOS"); 504 if (adr & DOD_NONVGA) 505 printf(" (not a VGA output)"); 506 printf(", head #%d\n", 507 (adr & DOD_HEAD_ID_MASK) >> DOD_HEAD_ID_SHIFT); 508 } 509 return (vo); 510 } 511 512 static void 513 acpi_video_vo_bind(struct acpi_video_output *vo, ACPI_HANDLE handle) 514 { 515 516 ACPI_SERIAL_ASSERT(video); 517 if (vo->vo_levels != NULL) 518 AcpiOsFree(vo->vo_levels); 519 vo->handle = handle; 520 vo->vo_numlevels = vo_get_brightness_levels(handle, &vo->vo_levels); 521 if (vo->vo_numlevels >= 2) { 522 if (vo->vo_fullpower == -1 523 || acpi_video_vo_check_level(vo, vo->vo_fullpower) != 0) 524 /* XXX - can't deal with rebinding... */ 525 vo->vo_fullpower = vo->vo_levels[BCL_FULLPOWER]; 526 if (vo->vo_economy == -1 527 || acpi_video_vo_check_level(vo, vo->vo_economy) != 0) 528 /* XXX - see above. */ 529 vo->vo_economy = vo->vo_levels[BCL_ECONOMY]; 530 } 531 } 532 533 static void 534 acpi_video_vo_destroy(struct acpi_video_output *vo) 535 { 536 struct acpi_video_output_queue *voqh; 537 538 ACPI_SERIAL_ASSERT(video); 539 if (vo->vo_sysctl_tree != NULL) { 540 vo->vo_sysctl_tree = NULL; 541 sysctl_ctx_free(&vo->vo_sysctl_ctx); 542 } 543 if (vo->vo_levels != NULL) 544 AcpiOsFree(vo->vo_levels); 545 546 switch (vo->adr & DOD_DEVID_MASK) { 547 case DOD_DEVID_MONITOR: 548 voqh = &crt_units; 549 break; 550 case DOD_DEVID_PANEL: 551 voqh = &lcd_units; 552 break; 553 case DOD_DEVID_TV: 554 voqh = &tv_units; 555 break; 556 default: 557 voqh = &other_units; 558 } 559 STAILQ_REMOVE(voqh, vo, acpi_video_output, vo_unit.next); 560 free(vo, M_ACPIVIDEO); 561 } 562 563 static int 564 acpi_video_vo_check_level(struct acpi_video_output *vo, int level) 565 { 566 int i; 567 568 ACPI_SERIAL_ASSERT(video); 569 if (vo->vo_levels == NULL) 570 return (ENODEV); 571 for (i = 0; i < vo->vo_numlevels; i++) 572 if (vo->vo_levels[i] == level) 573 return (0); 574 return (EINVAL); 575 } 576 577 /* ARGSUSED */ 578 static int 579 acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS) 580 { 581 struct acpi_video_output *vo; 582 int state, err; 583 584 vo = (struct acpi_video_output *)arg1; 585 if (vo->handle == NULL) 586 return (ENXIO); 587 ACPI_SERIAL_BEGIN(video); 588 state = (vo_get_device_status(vo->handle) & DCS_ACTIVE) ? 1 : 0; 589 err = sysctl_handle_int(oidp, &state, 0, req); 590 if (err != 0 || req->newptr == NULL) 591 goto out; 592 vo_set_device_state(vo->handle, 593 DSS_COMMIT | (state ? DSS_ACTIVE : DSS_INACTIVE)); 594 out: 595 ACPI_SERIAL_END(video); 596 return (err); 597 } 598 599 /* ARGSUSED */ 600 static int 601 acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS) 602 { 603 struct acpi_video_output *vo; 604 int level, preset, err; 605 606 vo = (struct acpi_video_output *)arg1; 607 ACPI_SERIAL_BEGIN(video); 608 if (vo->handle == NULL) { 609 err = ENXIO; 610 goto out; 611 } 612 if (vo->vo_levels == NULL) { 613 err = ENODEV; 614 goto out; 615 } 616 617 preset = (power_profile_get_state() == POWER_PROFILE_ECONOMY) ? 618 vo->vo_economy : vo->vo_fullpower; 619 level = vo->vo_brightness; 620 if (level == -1) 621 level = preset; 622 623 err = sysctl_handle_int(oidp, &level, 0, req); 624 if (err != 0 || req->newptr == NULL) 625 goto out; 626 if (level < -1 || level > 100) { 627 err = EINVAL; 628 goto out; 629 } 630 631 if (level != -1 && (err = acpi_video_vo_check_level(vo, level))) 632 goto out; 633 vo->vo_brightness = level; 634 vo_set_brightness(vo->handle, (level == -1) ? preset : level); 635 636 out: 637 ACPI_SERIAL_END(video); 638 return (err); 639 } 640 641 static int 642 acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS) 643 { 644 struct acpi_video_output *vo; 645 int i, level, *preset, err; 646 647 err = 0; 648 vo = (struct acpi_video_output *)arg1; 649 ACPI_SERIAL_BEGIN(video); 650 if (vo->handle == NULL) { 651 err = ENXIO; 652 goto out; 653 } 654 if (vo->vo_levels == NULL) { 655 err = ENODEV; 656 goto out; 657 } 658 preset = (arg2 == POWER_PROFILE_ECONOMY) ? 659 &vo->vo_economy : &vo->vo_fullpower; 660 level = *preset; 661 err = sysctl_handle_int(oidp, &level, 0, req); 662 if (err != 0 || req->newptr == NULL) 663 goto out; 664 if (level < -1 || level > 100) { 665 err = EINVAL; 666 goto out; 667 } 668 if (level == -1) { 669 i = (arg2 == POWER_PROFILE_ECONOMY) ? 670 BCL_ECONOMY : BCL_FULLPOWER; 671 level = vo->vo_levels[i]; 672 } else if ((err = acpi_video_vo_check_level(vo, level)) != 0) 673 goto out; 674 675 if (vo->vo_brightness == -1 && (power_profile_get_state() == arg2)) 676 vo_set_brightness(vo->handle, level); 677 *preset = level; 678 679 out: 680 ACPI_SERIAL_END(video); 681 return (err); 682 } 683 684 /* ARGSUSED */ 685 static int 686 acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS) 687 { 688 struct acpi_video_output *vo; 689 int err; 690 691 vo = (struct acpi_video_output *)arg1; 692 ACPI_SERIAL_BEGIN(video); 693 if (vo->vo_levels == NULL) { 694 err = ENODEV; 695 goto out; 696 } 697 if (req->newptr != NULL) { 698 err = EPERM; 699 goto out; 700 } 701 err = sysctl_handle_opaque(oidp, vo->vo_levels, 702 vo->vo_numlevels * sizeof(*vo->vo_levels), req); 703 704 out: 705 ACPI_SERIAL_END(video); 706 return (err); 707 } 708 709 static void 710 vid_set_switch_policy(ACPI_HANDLE handle, UINT32 policy) 711 { 712 ACPI_STATUS status; 713 714 status = acpi_SetInteger(handle, "_DOS", policy); 715 if (ACPI_FAILURE(status)) 716 printf("can't evaluate %s._DOS - %s\n", 717 acpi_name(handle), AcpiFormatException(status)); 718 } 719 720 struct enum_callback_arg { 721 void (*callback)(ACPI_HANDLE, UINT32, void *); 722 void *context; 723 ACPI_OBJECT *dod_pkg; 724 int count; 725 }; 726 727 static ACPI_STATUS 728 vid_enum_outputs_subr(ACPI_HANDLE handle, UINT32 level __unused, 729 void *context, void **retp __unused) 730 { 731 ACPI_STATUS status; 732 UINT32 adr, val; 733 struct enum_callback_arg *argset; 734 size_t i; 735 736 ACPI_SERIAL_ASSERT(video); 737 argset = context; 738 status = acpi_GetInteger(handle, "_ADR", &adr); 739 if (ACPI_FAILURE(status)) 740 return (AE_OK); 741 742 for (i = 0; i < argset->dod_pkg->Package.Count; i++) { 743 if (acpi_PkgInt32(argset->dod_pkg, i, &val) == 0 && 744 (val & DOD_DEVID_MASK) == adr) { 745 argset->callback(handle, val, argset->context); 746 argset->count++; 747 } 748 } 749 750 return (AE_OK); 751 } 752 753 static int 754 vid_enum_outputs(ACPI_HANDLE handle, 755 void (*callback)(ACPI_HANDLE, UINT32, void *), void *context) 756 { 757 ACPI_STATUS status; 758 ACPI_BUFFER dod_buf; 759 ACPI_OBJECT *res; 760 struct enum_callback_arg argset; 761 762 ACPI_SERIAL_ASSERT(video); 763 dod_buf.Length = ACPI_ALLOCATE_BUFFER; 764 dod_buf.Pointer = NULL; 765 status = AcpiEvaluateObject(handle, "_DOD", NULL, &dod_buf); 766 if (ACPI_FAILURE(status)) { 767 if (status != AE_NOT_FOUND) 768 printf("can't evaluate %s._DOD - %s\n", 769 acpi_name(handle), AcpiFormatException(status)); 770 argset.count = -1; 771 goto out; 772 } 773 res = (ACPI_OBJECT *)dod_buf.Pointer; 774 if (!ACPI_PKG_VALID(res, 1)) { 775 printf("evaluation of %s._DOD makes no sense\n", 776 acpi_name(handle)); 777 argset.count = -1; 778 goto out; 779 } 780 if (callback == NULL) { 781 argset.count = res->Package.Count; 782 goto out; 783 } 784 argset.callback = callback; 785 argset.context = context; 786 argset.dod_pkg = res; 787 argset.count = 0; 788 status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1, 789 vid_enum_outputs_subr, &argset, NULL); 790 if (ACPI_FAILURE(status)) 791 printf("failed walking down %s - %s\n", 792 acpi_name(handle), AcpiFormatException(status)); 793 out: 794 if (dod_buf.Pointer != NULL) 795 AcpiOsFree(dod_buf.Pointer); 796 return (argset.count); 797 } 798 799 static int 800 vo_get_brightness_levels(ACPI_HANDLE handle, int **levelp) 801 { 802 ACPI_STATUS status; 803 ACPI_BUFFER bcl_buf; 804 ACPI_OBJECT *res; 805 int num, i, n, *levels; 806 807 num = 0; 808 bcl_buf.Length = ACPI_ALLOCATE_BUFFER; 809 bcl_buf.Pointer = NULL; 810 status = AcpiEvaluateObject(handle, "_BCL", NULL, &bcl_buf); 811 if (ACPI_FAILURE(status)) { 812 if (status != AE_NOT_FOUND) 813 printf("can't evaluate %s._BCL - %s\n", 814 acpi_name(handle), AcpiFormatException(status)); 815 num = -1; 816 goto out; 817 } 818 res = (ACPI_OBJECT *)bcl_buf.Pointer; 819 if (!ACPI_PKG_VALID(res, 2)) { 820 printf("evaluation of %s._BCL makes no sense\n", 821 acpi_name(handle)); 822 num = -1; 823 goto out; 824 } 825 num = res->Package.Count; 826 if (levelp == NULL) 827 goto out; 828 levels = AcpiOsAllocate(num * sizeof(*levels)); 829 if (levels == NULL) { 830 num = -1; 831 goto out; 832 } 833 for (i = 0, n = 0; i < num; i++) 834 if (acpi_PkgInt32(res, i, &levels[n]) == 0) 835 n++; 836 if (n < 2) { 837 num = -1; 838 AcpiOsFree(levels); 839 } else { 840 num = n; 841 *levelp = levels; 842 } 843 out: 844 if (bcl_buf.Pointer != NULL) 845 AcpiOsFree(bcl_buf.Pointer); 846 847 return (num); 848 } 849 850 static void 851 vo_set_brightness(ACPI_HANDLE handle, int level) 852 { 853 ACPI_STATUS status; 854 855 status = acpi_SetInteger(handle, "_BCM", level); 856 if (ACPI_FAILURE(status)) 857 printf("can't evaluate %s._BCM - %s\n", 858 acpi_name(handle), AcpiFormatException(status)); 859 } 860 861 static UINT32 862 vo_get_device_status(ACPI_HANDLE handle) 863 { 864 UINT32 dcs; 865 ACPI_STATUS status; 866 867 dcs = 0; 868 status = acpi_GetInteger(handle, "_DCS", &dcs); 869 if (ACPI_FAILURE(status)) 870 printf("can't evaluate %s._DCS - %s\n", 871 acpi_name(handle), AcpiFormatException(status)); 872 873 return (dcs); 874 } 875 876 static UINT32 877 vo_get_graphics_state(ACPI_HANDLE handle) 878 { 879 UINT32 dgs; 880 ACPI_STATUS status; 881 882 dgs = 0; 883 status = acpi_GetInteger(handle, "_DGS", &dgs); 884 if (ACPI_FAILURE(status)) 885 printf("can't evaluate %s._DGS - %s\n", 886 acpi_name(handle), AcpiFormatException(status)); 887 888 return (dgs); 889 } 890 891 static void 892 vo_set_device_state(ACPI_HANDLE handle, UINT32 state) 893 { 894 ACPI_STATUS status; 895 896 status = acpi_SetInteger(handle, "_DSS", state); 897 if (ACPI_FAILURE(status)) 898 printf("can't evaluate %s._DSS - %s\n", 899 acpi_name(handle), AcpiFormatException(status)); 900 } 901