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