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/include/acpi.h> 42 43 #include <dev/acpica/acpivar.h> 44 45 /* ACPI video extension driver. */ 46 struct 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 63 STAILQ_HEAD(acpi_video_output_queue, acpi_video_output); 64 65 struct 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 */ 73 static int acpi_video_modevent(struct module*, int, void *); 74 static void acpi_video_identify(driver_t *driver, device_t parent); 75 static int acpi_video_probe(device_t); 76 static int acpi_video_attach(device_t); 77 static int acpi_video_detach(device_t); 78 static int acpi_video_shutdown(device_t); 79 static void acpi_video_notify_handler(ACPI_HANDLE, UINT32, void *); 80 static void acpi_video_power_profile(void *); 81 static void acpi_video_bind_outputs(struct acpi_video_softc *); 82 static struct acpi_video_output *acpi_video_vo_init(UINT32); 83 static void acpi_video_vo_bind(struct acpi_video_output *, ACPI_HANDLE); 84 static void acpi_video_vo_destroy(struct acpi_video_output *); 85 static int acpi_video_vo_check_level(struct acpi_video_output *, int); 86 static int acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS); 87 static int acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS); 88 static int acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS); 89 static int acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS); 90 91 /* operations */ 92 static void vid_set_switch_policy(ACPI_HANDLE, UINT32); 93 static int vid_enum_outputs(ACPI_HANDLE, 94 void(*)(ACPI_HANDLE, UINT32, void *), void *); 95 static int vo_get_brightness_levels(ACPI_HANDLE, int **); 96 static void vo_set_brightness(ACPI_HANDLE, int); 97 static UINT32 vo_get_device_status(ACPI_HANDLE); 98 static UINT32 vo_get_graphics_state(ACPI_HANDLE); 99 static 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 147 static 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 156 static driver_t acpi_video_driver = { 157 "acpi_video", 158 acpi_video_methods, 159 sizeof(struct acpi_video_softc), 160 }; 161 162 static devclass_t acpi_video_devclass; 163 164 DRIVER_MODULE(acpi_video, vgapci, acpi_video_driver, acpi_video_devclass, 165 acpi_video_modevent, NULL); 166 MODULE_DEPEND(acpi_video, acpi, 1, 1, 1); 167 168 static struct sysctl_ctx_list acpi_video_sysctl_ctx; 169 static struct sysctl_oid *acpi_video_sysctl_tree; 170 static 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 */ 178 ACPI_SERIAL_DECL(video, "ACPI video"); 179 ACPI_SERIAL_DECL(video_output, "ACPI video output"); 180 MALLOC_DEFINE(M_ACPIVIDEO, "acpivideo", "ACPI video extension"); 181 182 static int 183 acpi_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 208 static void 209 acpi_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 216 static int 217 acpi_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 234 static int 235 acpi_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 279 static int 280 acpi_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 301 static int 302 acpi_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 312 static void 313 acpi_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 366 static void 367 acpi_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 391 static void 392 acpi_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 413 static void 414 acpi_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 421 static struct acpi_video_output * 422 acpi_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 561 static void 562 acpi_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 583 static void 584 acpi_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 616 static int 617 acpi_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 */ 631 static int 632 acpi_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)); 647 out: 648 ACPI_SERIAL_END(video_output); 649 return (err); 650 } 651 652 /* ARGSUSED */ 653 static int 654 acpi_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 689 out: 690 ACPI_SERIAL_END(video_output); 691 return (err); 692 } 693 694 static int 695 acpi_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 732 out: 733 ACPI_SERIAL_END(video_output); 734 return (err); 735 } 736 737 /* ARGSUSED */ 738 static int 739 acpi_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 757 out: 758 ACPI_SERIAL_END(video_output); 759 return (err); 760 } 761 762 static void 763 vid_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 773 struct enum_callback_arg { 774 void (*callback)(ACPI_HANDLE, UINT32, void *); 775 void *context; 776 ACPI_OBJECT *dod_pkg; 777 int count; 778 }; 779 780 static ACPI_STATUS 781 vid_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 806 static int 807 vid_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, 842 vid_enum_outputs_subr, NULL, &argset, NULL); 843 if (ACPI_FAILURE(status)) 844 printf("failed walking down %s - %s\n", 845 acpi_name(handle), AcpiFormatException(status)); 846 out: 847 if (dod_buf.Pointer != NULL) 848 AcpiOsFree(dod_buf.Pointer); 849 return (argset.count); 850 } 851 852 static int 853 vo_get_brightness_levels(ACPI_HANDLE handle, int **levelp) 854 { 855 ACPI_STATUS status; 856 ACPI_BUFFER bcl_buf; 857 ACPI_OBJECT *res; 858 int num, i, n, *levels; 859 860 num = 0; 861 bcl_buf.Length = ACPI_ALLOCATE_BUFFER; 862 bcl_buf.Pointer = NULL; 863 status = AcpiEvaluateObject(handle, "_BCL", NULL, &bcl_buf); 864 if (ACPI_FAILURE(status)) { 865 if (status != AE_NOT_FOUND) 866 printf("can't evaluate %s._BCL - %s\n", 867 acpi_name(handle), AcpiFormatException(status)); 868 num = -1; 869 goto out; 870 } 871 res = (ACPI_OBJECT *)bcl_buf.Pointer; 872 if (!ACPI_PKG_VALID(res, 2)) { 873 printf("evaluation of %s._BCL makes no sense\n", 874 acpi_name(handle)); 875 num = -1; 876 goto out; 877 } 878 num = res->Package.Count; 879 if (levelp == NULL) 880 goto out; 881 levels = AcpiOsAllocate(num * sizeof(*levels)); 882 if (levels == NULL) { 883 num = -1; 884 goto out; 885 } 886 for (i = 0, n = 0; i < num; i++) 887 if (acpi_PkgInt32(res, i, &levels[n]) == 0) 888 n++; 889 if (n < 2) { 890 num = -1; 891 AcpiOsFree(levels); 892 } else { 893 num = n; 894 *levelp = levels; 895 } 896 out: 897 if (bcl_buf.Pointer != NULL) 898 AcpiOsFree(bcl_buf.Pointer); 899 900 return (num); 901 } 902 903 static void 904 vo_set_brightness(ACPI_HANDLE handle, int level) 905 { 906 ACPI_STATUS status; 907 908 ACPI_SERIAL_ASSERT(video_output); 909 status = acpi_SetInteger(handle, "_BCM", level); 910 if (ACPI_FAILURE(status)) 911 printf("can't evaluate %s._BCM - %s\n", 912 acpi_name(handle), AcpiFormatException(status)); 913 } 914 915 static UINT32 916 vo_get_device_status(ACPI_HANDLE handle) 917 { 918 UINT32 dcs; 919 ACPI_STATUS status; 920 921 ACPI_SERIAL_ASSERT(video_output); 922 dcs = 0; 923 status = acpi_GetInteger(handle, "_DCS", &dcs); 924 if (ACPI_FAILURE(status)) 925 printf("can't evaluate %s._DCS - %s\n", 926 acpi_name(handle), AcpiFormatException(status)); 927 928 return (dcs); 929 } 930 931 static UINT32 932 vo_get_graphics_state(ACPI_HANDLE handle) 933 { 934 UINT32 dgs; 935 ACPI_STATUS status; 936 937 dgs = 0; 938 status = acpi_GetInteger(handle, "_DGS", &dgs); 939 if (ACPI_FAILURE(status)) 940 printf("can't evaluate %s._DGS - %s\n", 941 acpi_name(handle), AcpiFormatException(status)); 942 943 return (dgs); 944 } 945 946 static void 947 vo_set_device_state(ACPI_HANDLE handle, UINT32 state) 948 { 949 ACPI_STATUS status; 950 951 ACPI_SERIAL_ASSERT(video_output); 952 status = acpi_SetInteger(handle, "_DSS", state); 953 if (ACPI_FAILURE(status)) 954 printf("can't evaluate %s._DSS - %s\n", 955 acpi_name(handle), AcpiFormatException(status)); 956 } 957