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 #include "opt_evdev.h" 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/eventhandler.h> 35 #include <sys/kernel.h> 36 #include <sys/malloc.h> 37 #include <sys/module.h> 38 #include <sys/power.h> 39 #include <sys/queue.h> 40 #include <sys/sysctl.h> 41 42 #include <contrib/dev/acpica/include/acpi.h> 43 44 #include <dev/acpica/acpivar.h> 45 46 #ifdef EVDEV_SUPPORT 47 #include <dev/evdev/input.h> 48 #include <dev/evdev/evdev.h> 49 #endif 50 51 /* ACPI video extension driver. */ 52 struct acpi_video_output { 53 ACPI_HANDLE handle; 54 UINT32 adr; 55 STAILQ_ENTRY(acpi_video_output) vo_next; 56 struct { 57 int num; 58 STAILQ_ENTRY(acpi_video_output) next; 59 } vo_unit; 60 int vo_hasbqc; /* Query method is present. */ 61 int vo_level; /* Cached level when !vo_hasbqc. */ 62 int vo_brightness; 63 int vo_fullpower; 64 int vo_economy; 65 int vo_numlevels; 66 int *vo_levels; 67 struct sysctl_ctx_list vo_sysctl_ctx; 68 struct sysctl_oid *vo_sysctl_tree; 69 #ifdef EVDEV_SUPPORT 70 struct evdev_dev *evdev; 71 #endif 72 }; 73 74 STAILQ_HEAD(acpi_video_output_queue, acpi_video_output); 75 76 struct acpi_video_softc { 77 device_t device; 78 ACPI_HANDLE handle; 79 struct acpi_video_output_queue vid_outputs; 80 eventhandler_tag vid_pwr_evh; 81 #ifdef EVDEV_SUPPORT 82 struct evdev_dev *evdev; 83 #endif 84 }; 85 86 /* interfaces */ 87 static int acpi_video_modevent(struct module*, int, void *); 88 static void acpi_video_identify(driver_t *driver, device_t parent); 89 static int acpi_video_probe(device_t); 90 static int acpi_video_attach(device_t); 91 static int acpi_video_detach(device_t); 92 static int acpi_video_resume(device_t); 93 static int acpi_video_shutdown(device_t); 94 static void acpi_video_notify_handler(ACPI_HANDLE, UINT32, void *); 95 static void acpi_video_power_profile(void *); 96 static void acpi_video_bind_outputs(struct acpi_video_softc *); 97 static struct acpi_video_output *acpi_video_vo_init(UINT32); 98 static void acpi_video_vo_bind(struct acpi_video_output *, ACPI_HANDLE); 99 static void acpi_video_vo_destroy(struct acpi_video_output *); 100 static int acpi_video_vo_check_level(struct acpi_video_output *, int); 101 static void acpi_video_vo_notify_handler(ACPI_HANDLE, UINT32, void *); 102 static int acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS); 103 static int acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS); 104 static int acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS); 105 static int acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS); 106 107 /* operations */ 108 static void vid_set_switch_policy(ACPI_HANDLE, UINT32); 109 static int vid_enum_outputs(ACPI_HANDLE, 110 void(*)(ACPI_HANDLE, UINT32, void *), void *); 111 static int vo_get_brightness_levels(ACPI_HANDLE, int **); 112 static int vo_get_brightness(struct acpi_video_output *); 113 static void vo_set_brightness(struct acpi_video_output *, int); 114 static UINT32 vo_get_device_status(ACPI_HANDLE); 115 static UINT32 vo_get_graphics_state(ACPI_HANDLE); 116 static void vo_set_device_state(ACPI_HANDLE, UINT32); 117 118 /* events */ 119 #define VID_NOTIFY_SWITCHED 0x80 120 #define VID_NOTIFY_REPROBE 0x81 121 #define VID_NOTIFY_CYCLE_OUT 0x82 122 #define VID_NOTIFY_NEXT_OUT 0x83 123 #define VID_NOTIFY_PREV_OUT 0x84 124 #define VID_NOTIFY_CYCLE_BRN 0x85 125 #define VID_NOTIFY_INC_BRN 0x86 126 #define VID_NOTIFY_DEC_BRN 0x87 127 #define VID_NOTIFY_ZERO_BRN 0x88 128 #define VID_NOTIFY_DISP_OFF 0x89 129 130 /* _DOS (Enable/Disable Output Switching) argument bits */ 131 #define DOS_SWITCH_MASK 3 132 #define DOS_SWITCH_BY_OSPM 0 133 #define DOS_SWITCH_BY_BIOS 1 134 #define DOS_SWITCH_LOCKED 2 135 #define DOS_BRIGHTNESS_BY_OSPM (1 << 2) 136 137 /* _DOD and subdev's _ADR */ 138 #define DOD_DEVID_MASK 0x0f00 139 #define DOD_DEVID_MASK_FULL 0xffff 140 #define DOD_DEVID_MASK_DISPIDX 0x000f 141 #define DOD_DEVID_MASK_DISPPORT 0x00f0 142 #define DOD_DEVID_MONITOR 0x0100 143 #define DOD_DEVID_LCD 0x0110 144 #define DOD_DEVID_TV 0x0200 145 #define DOD_DEVID_EXT 0x0300 146 #define DOD_DEVID_INTDFP 0x0400 147 #define DOD_BIOS (1 << 16) 148 #define DOD_NONVGA (1 << 17) 149 #define DOD_HEAD_ID_SHIFT 18 150 #define DOD_HEAD_ID_BITS 3 151 #define DOD_HEAD_ID_MASK \ 152 (((1 << DOD_HEAD_ID_BITS) - 1) << DOD_HEAD_ID_SHIFT) 153 #define DOD_DEVID_SCHEME_STD (1U << 31) 154 155 /* _BCL related constants */ 156 #define BCL_FULLPOWER 0 157 #define BCL_ECONOMY 1 158 159 /* _DCS (Device Currrent Status) value bits and masks. */ 160 #define DCS_EXISTS (1 << 0) 161 #define DCS_ACTIVE (1 << 1) 162 #define DCS_READY (1 << 2) 163 #define DCS_FUNCTIONAL (1 << 3) 164 #define DCS_ATTACHED (1 << 4) 165 166 /* _DSS (Device Set Status) argument bits and masks. */ 167 #define DSS_INACTIVE 0 168 #define DSS_ACTIVE (1 << 0) 169 #define DSS_SETNEXT (1 << 30) 170 #define DSS_COMMIT (1U << 31) 171 172 static device_method_t acpi_video_methods[] = { 173 DEVMETHOD(device_identify, acpi_video_identify), 174 DEVMETHOD(device_probe, acpi_video_probe), 175 DEVMETHOD(device_attach, acpi_video_attach), 176 DEVMETHOD(device_detach, acpi_video_detach), 177 DEVMETHOD(device_resume, acpi_video_resume), 178 DEVMETHOD(device_shutdown, acpi_video_shutdown), 179 { 0, 0 } 180 }; 181 182 static driver_t acpi_video_driver = { 183 "acpi_video", 184 acpi_video_methods, 185 sizeof(struct acpi_video_softc), 186 }; 187 188 DRIVER_MODULE(acpi_video, vgapci, acpi_video_driver, acpi_video_modevent, NULL); 189 MODULE_DEPEND(acpi_video, acpi, 1, 1, 1); 190 #ifdef EVDEV_SUPPORT 191 MODULE_DEPEND(acpi_video, evdev, 1, 1, 1); 192 #endif 193 194 static struct sysctl_ctx_list acpi_video_sysctl_ctx; 195 static struct sysctl_oid *acpi_video_sysctl_tree; 196 static struct acpi_video_output_queue crt_units, tv_units, 197 ext_units, lcd_units, other_units; 198 199 /* 200 * The 'video' lock protects the hierarchy of video output devices 201 * (the video "bus"). The 'video_output' lock protects per-output 202 * data is equivalent to a softc lock for each video output. 203 */ 204 ACPI_SERIAL_DECL(video, "ACPI video"); 205 ACPI_SERIAL_DECL(video_output, "ACPI video output"); 206 static MALLOC_DEFINE(M_ACPIVIDEO, "acpivideo", "ACPI video extension"); 207 208 #ifdef EVDEV_SUPPORT 209 static const struct { 210 UINT32 notify; 211 uint16_t key; 212 } acpi_video_evdev_map[] = { 213 { VID_NOTIFY_SWITCHED, KEY_SWITCHVIDEOMODE }, 214 { VID_NOTIFY_REPROBE, KEY_SWITCHVIDEOMODE }, 215 { VID_NOTIFY_CYCLE_OUT, KEY_SWITCHVIDEOMODE }, 216 { VID_NOTIFY_NEXT_OUT, KEY_VIDEO_NEXT }, 217 { VID_NOTIFY_PREV_OUT, KEY_VIDEO_PREV }, 218 { VID_NOTIFY_CYCLE_BRN, KEY_BRIGHTNESS_CYCLE }, 219 { VID_NOTIFY_INC_BRN, KEY_BRIGHTNESSUP }, 220 { VID_NOTIFY_DEC_BRN, KEY_BRIGHTNESSDOWN }, 221 { VID_NOTIFY_ZERO_BRN, KEY_BRIGHTNESS_ZERO }, 222 { VID_NOTIFY_DISP_OFF, KEY_DISPLAY_OFF }, 223 }; 224 225 static void 226 acpi_video_push_evdev_event(struct evdev_dev *evdev, UINT32 notify) 227 { 228 int i; 229 uint16_t key; 230 231 /* Do not allow to execute 2 instances this routine concurrently */ 232 ACPI_SERIAL_ASSERT(video_output); 233 234 for (i = 0; i < nitems(acpi_video_evdev_map); i++) { 235 if (acpi_video_evdev_map[i].notify == notify) { 236 key = acpi_video_evdev_map[i].key; 237 evdev_push_key(evdev, key, 1); 238 evdev_sync(evdev); 239 evdev_push_key(evdev, key, 0); 240 evdev_sync(evdev); 241 break; 242 } 243 } 244 } 245 #endif 246 247 static int 248 acpi_video_modevent(struct module *mod __unused, int evt, void *cookie __unused) 249 { 250 int err; 251 252 err = 0; 253 switch (evt) { 254 case MOD_LOAD: 255 sysctl_ctx_init(&acpi_video_sysctl_ctx); 256 STAILQ_INIT(&crt_units); 257 STAILQ_INIT(&tv_units); 258 STAILQ_INIT(&ext_units); 259 STAILQ_INIT(&lcd_units); 260 STAILQ_INIT(&other_units); 261 break; 262 case MOD_UNLOAD: 263 sysctl_ctx_free(&acpi_video_sysctl_ctx); 264 acpi_video_sysctl_tree = NULL; 265 break; 266 default: 267 err = EINVAL; 268 } 269 270 return (err); 271 } 272 273 static void 274 acpi_video_identify(driver_t *driver, device_t parent) 275 { 276 277 if (device_find_child(parent, "acpi_video", -1) == NULL) 278 device_add_child(parent, "acpi_video", DEVICE_UNIT_ANY); 279 } 280 281 static int 282 acpi_video_probe(device_t dev) 283 { 284 ACPI_HANDLE devh, h; 285 ACPI_OBJECT_TYPE t_dos; 286 287 devh = acpi_get_handle(dev); 288 if (acpi_disabled("video") || 289 ACPI_FAILURE(AcpiGetHandle(devh, "_DOD", &h)) || 290 ACPI_FAILURE(AcpiGetHandle(devh, "_DOS", &h)) || 291 ACPI_FAILURE(AcpiGetType(h, &t_dos)) || 292 t_dos != ACPI_TYPE_METHOD) 293 return (ENXIO); 294 295 device_set_desc(dev, "ACPI video extension"); 296 return (0); 297 } 298 299 static int 300 acpi_video_attach(device_t dev) 301 { 302 struct acpi_softc *acpi_sc; 303 struct acpi_video_softc *sc; 304 #ifdef EVDEV_SUPPORT 305 int i; 306 #endif 307 308 sc = device_get_softc(dev); 309 310 acpi_sc = devclass_get_softc(devclass_find("acpi"), 0); 311 if (acpi_sc == NULL) 312 return (ENXIO); 313 314 #ifdef EVDEV_SUPPORT 315 sc->evdev = evdev_alloc(); 316 evdev_set_name(sc->evdev, device_get_desc(dev)); 317 evdev_set_phys(sc->evdev, device_get_nameunit(dev)); 318 evdev_set_id(sc->evdev, BUS_HOST, 0, 0, 1); 319 evdev_support_event(sc->evdev, EV_SYN); 320 evdev_support_event(sc->evdev, EV_KEY); 321 for (i = 0; i < nitems(acpi_video_evdev_map); i++) 322 evdev_support_key(sc->evdev, acpi_video_evdev_map[i].key); 323 324 if (evdev_register(sc->evdev) != 0) 325 return (ENXIO); 326 #endif 327 328 ACPI_SERIAL_BEGIN(video); 329 if (acpi_video_sysctl_tree == NULL) { 330 acpi_video_sysctl_tree = SYSCTL_ADD_NODE(&acpi_video_sysctl_ctx, 331 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, 332 "video", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 333 "video extension control"); 334 } 335 ACPI_SERIAL_END(video); 336 337 sc->device = dev; 338 sc->handle = acpi_get_handle(dev); 339 STAILQ_INIT(&sc->vid_outputs); 340 341 AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 342 acpi_video_notify_handler, sc); 343 sc->vid_pwr_evh = EVENTHANDLER_REGISTER(power_profile_change, 344 acpi_video_power_profile, sc, 0); 345 346 ACPI_SERIAL_BEGIN(video); 347 acpi_video_bind_outputs(sc); 348 ACPI_SERIAL_END(video); 349 350 /* 351 * Notify the BIOS that we want to switch both active outputs and 352 * brightness levels. 353 */ 354 vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_OSPM | 355 DOS_BRIGHTNESS_BY_OSPM); 356 357 acpi_video_power_profile(sc); 358 359 return (0); 360 } 361 362 static int 363 acpi_video_detach(device_t dev) 364 { 365 struct acpi_video_softc *sc; 366 struct acpi_video_output *vo, *vn; 367 368 sc = device_get_softc(dev); 369 370 vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); 371 EVENTHANDLER_DEREGISTER(power_profile_change, sc->vid_pwr_evh); 372 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 373 acpi_video_notify_handler); 374 375 ACPI_SERIAL_BEGIN(video); 376 STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vn) { 377 acpi_video_vo_destroy(vo); 378 } 379 ACPI_SERIAL_END(video); 380 381 #ifdef EVDEV_SUPPORT 382 evdev_free(sc->evdev); 383 #endif 384 385 return (0); 386 } 387 388 static int 389 acpi_video_resume(device_t dev) 390 { 391 struct acpi_video_softc *sc; 392 struct acpi_video_output *vo, *vn; 393 int level; 394 395 sc = device_get_softc(dev); 396 397 /* Restore brightness level */ 398 ACPI_SERIAL_BEGIN(video); 399 ACPI_SERIAL_BEGIN(video_output); 400 STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vn) { 401 if ((vo->adr & DOD_DEVID_MASK_FULL) != DOD_DEVID_LCD && 402 (vo->adr & DOD_DEVID_MASK) != DOD_DEVID_INTDFP) 403 continue; 404 405 if ((vo_get_device_status(vo->handle) & DCS_ACTIVE) == 0) 406 continue; 407 408 level = vo_get_brightness(vo); 409 if (level != -1) 410 vo_set_brightness(vo, level); 411 } 412 ACPI_SERIAL_END(video_output); 413 ACPI_SERIAL_END(video); 414 415 return (0); 416 } 417 418 static int 419 acpi_video_shutdown(device_t dev) 420 { 421 struct acpi_video_softc *sc; 422 423 sc = device_get_softc(dev); 424 vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); 425 426 return (0); 427 } 428 429 static void 430 acpi_video_invoke_event_handler(void *context) 431 { 432 EVENTHANDLER_INVOKE(acpi_video_event, (int)(intptr_t)context); 433 } 434 435 static void 436 acpi_video_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 437 { 438 struct acpi_video_softc *sc; 439 struct acpi_video_output *vo, *vo_tmp; 440 ACPI_HANDLE lasthand; 441 UINT32 dcs, dss, dss_p; 442 443 sc = (struct acpi_video_softc *)context; 444 445 switch (notify) { 446 case VID_NOTIFY_SWITCHED: 447 dss_p = 0; 448 lasthand = NULL; 449 ACPI_SERIAL_BEGIN(video); 450 ACPI_SERIAL_BEGIN(video_output); 451 STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 452 dss = vo_get_graphics_state(vo->handle); 453 dcs = vo_get_device_status(vo->handle); 454 if (!(dcs & DCS_READY)) 455 dss = DSS_INACTIVE; 456 if (((dcs & DCS_ACTIVE) && dss == DSS_INACTIVE) || 457 (!(dcs & DCS_ACTIVE) && dss == DSS_ACTIVE)) { 458 if (lasthand != NULL) 459 vo_set_device_state(lasthand, dss_p); 460 dss_p = dss; 461 lasthand = vo->handle; 462 } 463 } 464 if (lasthand != NULL) 465 vo_set_device_state(lasthand, dss_p|DSS_COMMIT); 466 ACPI_SERIAL_END(video_output); 467 ACPI_SERIAL_END(video); 468 break; 469 case VID_NOTIFY_REPROBE: 470 ACPI_SERIAL_BEGIN(video); 471 STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) 472 vo->handle = NULL; 473 acpi_video_bind_outputs(sc); 474 STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vo_tmp) { 475 if (vo->handle == NULL) { 476 STAILQ_REMOVE(&sc->vid_outputs, vo, 477 acpi_video_output, vo_next); 478 acpi_video_vo_destroy(vo); 479 } 480 } 481 ACPI_SERIAL_END(video); 482 break; 483 /* Next events should not appear if DOS_SWITCH_BY_OSPM policy is set */ 484 case VID_NOTIFY_CYCLE_OUT: 485 case VID_NOTIFY_NEXT_OUT: 486 case VID_NOTIFY_PREV_OUT: 487 default: 488 device_printf(sc->device, "unknown notify event 0x%x\n", 489 notify); 490 } 491 AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_video_invoke_event_handler, 492 (void *)(uintptr_t)notify); 493 #ifdef EVDEV_SUPPORT 494 ACPI_SERIAL_BEGIN(video_output); 495 acpi_video_push_evdev_event(sc->evdev, notify); 496 ACPI_SERIAL_END(video_output); 497 #endif 498 } 499 500 static void 501 acpi_video_power_profile(void *context) 502 { 503 int state; 504 struct acpi_video_softc *sc; 505 struct acpi_video_output *vo; 506 507 sc = context; 508 state = power_profile_get_state(); 509 if (state != POWER_PROFILE_PERFORMANCE && 510 state != POWER_PROFILE_ECONOMY) 511 return; 512 513 ACPI_SERIAL_BEGIN(video); 514 ACPI_SERIAL_BEGIN(video_output); 515 STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 516 if (vo->vo_levels != NULL && vo->vo_brightness == -1) 517 vo_set_brightness(vo, 518 state == POWER_PROFILE_ECONOMY ? 519 vo->vo_economy : vo->vo_fullpower); 520 } 521 ACPI_SERIAL_END(video_output); 522 ACPI_SERIAL_END(video); 523 } 524 525 static void 526 acpi_video_bind_outputs_subr(ACPI_HANDLE handle, UINT32 adr, void *context) 527 { 528 struct acpi_video_softc *sc; 529 struct acpi_video_output *vo; 530 531 ACPI_SERIAL_ASSERT(video); 532 sc = context; 533 534 STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 535 if (vo->adr == adr) { 536 acpi_video_vo_bind(vo, handle); 537 return; 538 } 539 } 540 vo = acpi_video_vo_init(adr); 541 if (vo != NULL) { 542 #ifdef EVDEV_SUPPORT 543 vo->evdev = sc->evdev; 544 #endif 545 acpi_video_vo_bind(vo, handle); 546 STAILQ_INSERT_TAIL(&sc->vid_outputs, vo, vo_next); 547 } 548 } 549 550 static void 551 acpi_video_bind_outputs(struct acpi_video_softc *sc) 552 { 553 554 ACPI_SERIAL_ASSERT(video); 555 vid_enum_outputs(sc->handle, acpi_video_bind_outputs_subr, sc); 556 } 557 558 static struct acpi_video_output * 559 acpi_video_vo_init(UINT32 adr) 560 { 561 struct acpi_video_output *vn, *vo, *vp; 562 int n, x; 563 char name[8], env[32]; 564 const char *type, *desc; 565 struct acpi_video_output_queue *voqh; 566 567 ACPI_SERIAL_ASSERT(video); 568 569 switch (adr & DOD_DEVID_MASK) { 570 case DOD_DEVID_MONITOR: 571 if ((adr & DOD_DEVID_MASK_FULL) == DOD_DEVID_LCD) { 572 /* DOD_DEVID_LCD is a common, backward compatible ID */ 573 desc = "Internal/Integrated Digital Flat Panel"; 574 type = "lcd"; 575 voqh = &lcd_units; 576 } else { 577 desc = "VGA CRT or VESA Compatible Analog Monitor"; 578 type = "crt"; 579 voqh = &crt_units; 580 } 581 break; 582 case DOD_DEVID_TV: 583 desc = "TV/HDTV or Analog-Video Monitor"; 584 type = "tv"; 585 voqh = &tv_units; 586 break; 587 case DOD_DEVID_EXT: 588 desc = "External Digital Monitor"; 589 type = "ext"; 590 voqh = &ext_units; 591 break; 592 case DOD_DEVID_INTDFP: 593 desc = "Internal/Integrated Digital Flat Panel"; 594 type = "lcd"; 595 voqh = &lcd_units; 596 break; 597 default: 598 desc = "unknown output"; 599 type = "out"; 600 voqh = &other_units; 601 } 602 603 n = 0; 604 vp = NULL; 605 STAILQ_FOREACH(vn, voqh, vo_unit.next) { 606 if (vn->vo_unit.num != n) 607 break; 608 vp = vn; 609 n++; 610 } 611 612 snprintf(name, sizeof(name), "%s%d", type, n); 613 614 vo = malloc(sizeof(*vo), M_ACPIVIDEO, M_NOWAIT); 615 if (vo != NULL) { 616 vo->handle = NULL; 617 vo->adr = adr; 618 vo->vo_unit.num = n; 619 vo->vo_hasbqc = -1; 620 vo->vo_level = -1; 621 vo->vo_brightness = -1; 622 vo->vo_fullpower = -1; /* TODO: override with tunables */ 623 vo->vo_economy = -1; 624 vo->vo_numlevels = 0; 625 vo->vo_levels = NULL; 626 snprintf(env, sizeof(env), "hw.acpi.video.%s.fullpower", name); 627 if (getenv_int(env, &x)) 628 vo->vo_fullpower = x; 629 snprintf(env, sizeof(env), "hw.acpi.video.%s.economy", name); 630 if (getenv_int(env, &x)) 631 vo->vo_economy = x; 632 633 sysctl_ctx_init(&vo->vo_sysctl_ctx); 634 if (vp != NULL) 635 STAILQ_INSERT_AFTER(voqh, vp, vo, vo_unit.next); 636 else 637 STAILQ_INSERT_TAIL(voqh, vo, vo_unit.next); 638 if (acpi_video_sysctl_tree != NULL) 639 vo->vo_sysctl_tree = 640 SYSCTL_ADD_NODE(&vo->vo_sysctl_ctx, 641 SYSCTL_CHILDREN(acpi_video_sysctl_tree), 642 OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 643 0, desc); 644 if (vo->vo_sysctl_tree != NULL) { 645 SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 646 SYSCTL_CHILDREN(vo->vo_sysctl_tree), 647 OID_AUTO, "active", 648 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, vo, 649 0, acpi_video_vo_active_sysctl, "I", 650 "current activity of this device"); 651 SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 652 SYSCTL_CHILDREN(vo->vo_sysctl_tree), 653 OID_AUTO, "brightness", 654 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, vo, 655 0, acpi_video_vo_bright_sysctl, "I", 656 "current brightness level"); 657 SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 658 SYSCTL_CHILDREN(vo->vo_sysctl_tree), 659 OID_AUTO, "fullpower", 660 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, vo, 661 POWER_PROFILE_PERFORMANCE, 662 acpi_video_vo_presets_sysctl, "I", 663 "preset level for full power mode"); 664 SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 665 SYSCTL_CHILDREN(vo->vo_sysctl_tree), 666 OID_AUTO, "economy", 667 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, vo, 668 POWER_PROFILE_ECONOMY, 669 acpi_video_vo_presets_sysctl, "I", 670 "preset level for economy mode"); 671 SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 672 SYSCTL_CHILDREN(vo->vo_sysctl_tree), 673 OID_AUTO, "levels", 674 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, vo, 675 0, acpi_video_vo_levels_sysctl, "I", 676 "supported brightness levels"); 677 } else 678 printf("%s: sysctl node creation failed\n", type); 679 } else 680 printf("%s: softc allocation failed\n", type); 681 682 if (bootverbose) { 683 printf("found %s(%x)", desc, adr & DOD_DEVID_MASK_FULL); 684 printf(", idx#%x", adr & DOD_DEVID_MASK_DISPIDX); 685 printf(", port#%x", (adr & DOD_DEVID_MASK_DISPPORT) >> 4); 686 if (adr & DOD_BIOS) 687 printf(", detectable by BIOS"); 688 if (adr & DOD_NONVGA) 689 printf(" (Non-VGA output device whose power " 690 "is related to the VGA device)"); 691 printf(", head #%d\n", 692 (adr & DOD_HEAD_ID_MASK) >> DOD_HEAD_ID_SHIFT); 693 } 694 return (vo); 695 } 696 697 static void 698 acpi_video_vo_bind(struct acpi_video_output *vo, ACPI_HANDLE handle) 699 { 700 701 ACPI_SERIAL_BEGIN(video_output); 702 if (vo->vo_levels != NULL) { 703 AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY, 704 acpi_video_vo_notify_handler); 705 AcpiOsFree(vo->vo_levels); 706 vo->vo_levels = NULL; 707 } 708 vo->handle = handle; 709 vo->vo_numlevels = vo_get_brightness_levels(handle, &vo->vo_levels); 710 if (vo->vo_numlevels >= 2) { 711 if (vo->vo_fullpower == -1 || 712 acpi_video_vo_check_level(vo, vo->vo_fullpower) != 0) { 713 /* XXX - can't deal with rebinding... */ 714 vo->vo_fullpower = vo->vo_levels[BCL_FULLPOWER]; 715 } 716 if (vo->vo_economy == -1 || 717 acpi_video_vo_check_level(vo, vo->vo_economy) != 0) { 718 /* XXX - see above. */ 719 vo->vo_economy = vo->vo_levels[BCL_ECONOMY]; 720 } 721 AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, 722 acpi_video_vo_notify_handler, vo); 723 } 724 ACPI_SERIAL_END(video_output); 725 } 726 727 static void 728 acpi_video_vo_destroy(struct acpi_video_output *vo) 729 { 730 struct acpi_video_output_queue *voqh; 731 732 ACPI_SERIAL_ASSERT(video); 733 if (vo->vo_sysctl_tree != NULL) { 734 vo->vo_sysctl_tree = NULL; 735 sysctl_ctx_free(&vo->vo_sysctl_ctx); 736 } 737 if (vo->vo_levels != NULL) { 738 AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY, 739 acpi_video_vo_notify_handler); 740 AcpiOsFree(vo->vo_levels); 741 } 742 743 switch (vo->adr & DOD_DEVID_MASK) { 744 case DOD_DEVID_MONITOR: 745 if ((vo->adr & DOD_DEVID_MASK_FULL) == DOD_DEVID_LCD) 746 voqh = &lcd_units; 747 else 748 voqh = &crt_units; 749 break; 750 case DOD_DEVID_TV: 751 voqh = &tv_units; 752 break; 753 case DOD_DEVID_EXT: 754 voqh = &ext_units; 755 break; 756 case DOD_DEVID_INTDFP: 757 voqh = &lcd_units; 758 break; 759 default: 760 voqh = &other_units; 761 } 762 STAILQ_REMOVE(voqh, vo, acpi_video_output, vo_unit.next); 763 free(vo, M_ACPIVIDEO); 764 } 765 766 static int 767 acpi_video_vo_check_level(struct acpi_video_output *vo, int level) 768 { 769 int i; 770 771 ACPI_SERIAL_ASSERT(video_output); 772 if (vo->vo_levels == NULL) 773 return (ENODEV); 774 for (i = 0; i < vo->vo_numlevels; i++) 775 if (vo->vo_levels[i] == level) 776 return (0); 777 return (EINVAL); 778 } 779 780 static void 781 acpi_video_vo_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 782 { 783 struct acpi_video_output *vo; 784 int i, j, level, new_level; 785 786 vo = context; 787 ACPI_SERIAL_BEGIN(video_output); 788 if (vo->handle != handle) 789 goto out; 790 791 switch (notify) { 792 case VID_NOTIFY_CYCLE_BRN: 793 if (vo->vo_numlevels <= 3) 794 goto out; 795 /* FALLTHROUGH */ 796 case VID_NOTIFY_INC_BRN: 797 case VID_NOTIFY_DEC_BRN: 798 case VID_NOTIFY_ZERO_BRN: 799 case VID_NOTIFY_DISP_OFF: 800 if (vo->vo_levels == NULL) 801 goto out; 802 level = vo_get_brightness(vo); 803 if (level < 0) 804 goto out; 805 break; 806 default: 807 printf("unknown notify event 0x%x from %s\n", 808 notify, acpi_name(handle)); 809 goto out; 810 } 811 812 new_level = level; 813 switch (notify) { 814 case VID_NOTIFY_CYCLE_BRN: 815 for (i = 2; i < vo->vo_numlevels; i++) 816 if (vo->vo_levels[i] == level) { 817 new_level = vo->vo_numlevels > i + 1 ? 818 vo->vo_levels[i + 1] : vo->vo_levels[2]; 819 break; 820 } 821 break; 822 case VID_NOTIFY_INC_BRN: 823 case VID_NOTIFY_DEC_BRN: 824 for (i = 0; i < vo->vo_numlevels; i++) { 825 j = vo->vo_levels[i]; 826 if (notify == VID_NOTIFY_INC_BRN) { 827 if (j > level && 828 (j < new_level || level == new_level)) 829 new_level = j; 830 } else { 831 if (j < level && 832 (j > new_level || level == new_level)) 833 new_level = j; 834 } 835 } 836 break; 837 case VID_NOTIFY_ZERO_BRN: 838 for (i = 0; i < vo->vo_numlevels; i++) 839 if (vo->vo_levels[i] == 0) { 840 new_level = 0; 841 break; 842 } 843 break; 844 case VID_NOTIFY_DISP_OFF: 845 acpi_pwr_switch_consumer(handle, ACPI_STATE_D3); 846 break; 847 } 848 if (new_level != level) { 849 vo_set_brightness(vo, new_level); 850 vo->vo_brightness = new_level; 851 } 852 #ifdef EVDEV_SUPPORT 853 acpi_video_push_evdev_event(vo->evdev, notify); 854 #endif 855 856 out: 857 ACPI_SERIAL_END(video_output); 858 859 AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_video_invoke_event_handler, 860 (void *)(uintptr_t)notify); 861 } 862 863 /* ARGSUSED */ 864 static int 865 acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS) 866 { 867 struct acpi_video_output *vo; 868 int state, err; 869 870 vo = (struct acpi_video_output *)arg1; 871 if (vo->handle == NULL) 872 return (ENXIO); 873 ACPI_SERIAL_BEGIN(video_output); 874 state = (vo_get_device_status(vo->handle) & DCS_ACTIVE) ? 1 : 0; 875 err = sysctl_handle_int(oidp, &state, 0, req); 876 if (err != 0 || req->newptr == NULL) 877 goto out; 878 vo_set_device_state(vo->handle, 879 DSS_COMMIT | (state ? DSS_ACTIVE : DSS_INACTIVE)); 880 out: 881 ACPI_SERIAL_END(video_output); 882 return (err); 883 } 884 885 /* ARGSUSED */ 886 static int 887 acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS) 888 { 889 struct acpi_video_output *vo; 890 int level, preset, err; 891 892 vo = (struct acpi_video_output *)arg1; 893 ACPI_SERIAL_BEGIN(video_output); 894 if (vo->handle == NULL) { 895 err = ENXIO; 896 goto out; 897 } 898 if (vo->vo_levels == NULL) { 899 err = ENODEV; 900 goto out; 901 } 902 903 preset = (power_profile_get_state() == POWER_PROFILE_ECONOMY) ? 904 vo->vo_economy : vo->vo_fullpower; 905 level = vo->vo_brightness; 906 if (level == -1) 907 level = preset; 908 909 err = sysctl_handle_int(oidp, &level, 0, req); 910 if (err != 0 || req->newptr == NULL) 911 goto out; 912 if (level < -1 || level > 100) { 913 err = EINVAL; 914 goto out; 915 } 916 917 if (level != -1 && (err = acpi_video_vo_check_level(vo, level))) 918 goto out; 919 vo->vo_brightness = level; 920 vo_set_brightness(vo, (level == -1) ? preset : level); 921 922 out: 923 ACPI_SERIAL_END(video_output); 924 return (err); 925 } 926 927 static int 928 acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS) 929 { 930 struct acpi_video_output *vo; 931 int i, level, *preset, err; 932 933 vo = (struct acpi_video_output *)arg1; 934 ACPI_SERIAL_BEGIN(video_output); 935 if (vo->handle == NULL) { 936 err = ENXIO; 937 goto out; 938 } 939 if (vo->vo_levels == NULL) { 940 err = ENODEV; 941 goto out; 942 } 943 preset = (arg2 == POWER_PROFILE_ECONOMY) ? 944 &vo->vo_economy : &vo->vo_fullpower; 945 level = *preset; 946 err = sysctl_handle_int(oidp, &level, 0, req); 947 if (err != 0 || req->newptr == NULL) 948 goto out; 949 if (level < -1 || level > 100) { 950 err = EINVAL; 951 goto out; 952 } 953 if (level == -1) { 954 i = (arg2 == POWER_PROFILE_ECONOMY) ? 955 BCL_ECONOMY : BCL_FULLPOWER; 956 level = vo->vo_levels[i]; 957 } else if ((err = acpi_video_vo_check_level(vo, level)) != 0) 958 goto out; 959 960 if (vo->vo_brightness == -1 && (power_profile_get_state() == arg2)) 961 vo_set_brightness(vo, level); 962 *preset = level; 963 964 out: 965 ACPI_SERIAL_END(video_output); 966 return (err); 967 } 968 969 /* ARGSUSED */ 970 static int 971 acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS) 972 { 973 struct acpi_video_output *vo; 974 int err; 975 976 vo = (struct acpi_video_output *)arg1; 977 ACPI_SERIAL_BEGIN(video_output); 978 if (vo->vo_levels == NULL) { 979 err = ENODEV; 980 goto out; 981 } 982 if (req->newptr != NULL) { 983 err = EPERM; 984 goto out; 985 } 986 err = sysctl_handle_opaque(oidp, vo->vo_levels, 987 vo->vo_numlevels * sizeof(*vo->vo_levels), req); 988 989 out: 990 ACPI_SERIAL_END(video_output); 991 return (err); 992 } 993 994 static void 995 vid_set_switch_policy(ACPI_HANDLE handle, UINT32 policy) 996 { 997 ACPI_STATUS status; 998 999 status = acpi_SetInteger(handle, "_DOS", policy); 1000 if (ACPI_FAILURE(status)) 1001 printf("can't evaluate %s._DOS - %s\n", 1002 acpi_name(handle), AcpiFormatException(status)); 1003 } 1004 1005 struct enum_callback_arg { 1006 void (*callback)(ACPI_HANDLE, UINT32, void *); 1007 void *context; 1008 ACPI_OBJECT *dod_pkg; 1009 int count; 1010 }; 1011 1012 static ACPI_STATUS 1013 vid_enum_outputs_subr(ACPI_HANDLE handle, UINT32 level __unused, 1014 void *context, void **retp __unused) 1015 { 1016 ACPI_STATUS status; 1017 UINT32 adr, val; 1018 struct enum_callback_arg *argset; 1019 size_t i; 1020 1021 ACPI_SERIAL_ASSERT(video); 1022 argset = context; 1023 status = acpi_GetInteger(handle, "_ADR", &adr); 1024 if (ACPI_FAILURE(status)) 1025 return (AE_OK); 1026 1027 for (i = 0; i < argset->dod_pkg->Package.Count; i++) { 1028 if (acpi_PkgInt32(argset->dod_pkg, i, &val) == 0 && 1029 (val & DOD_DEVID_MASK_FULL) == 1030 (adr & DOD_DEVID_MASK_FULL)) { 1031 argset->callback(handle, val, argset->context); 1032 argset->count++; 1033 } 1034 } 1035 1036 return (AE_OK); 1037 } 1038 1039 static int 1040 vid_enum_outputs(ACPI_HANDLE handle, 1041 void (*callback)(ACPI_HANDLE, UINT32, void *), void *context) 1042 { 1043 ACPI_STATUS status; 1044 ACPI_BUFFER dod_buf; 1045 ACPI_OBJECT *res; 1046 struct enum_callback_arg argset; 1047 1048 ACPI_SERIAL_ASSERT(video); 1049 dod_buf.Length = ACPI_ALLOCATE_BUFFER; 1050 dod_buf.Pointer = NULL; 1051 status = AcpiEvaluateObject(handle, "_DOD", NULL, &dod_buf); 1052 if (ACPI_FAILURE(status)) { 1053 if (status != AE_NOT_FOUND) 1054 printf("can't evaluate %s._DOD - %s\n", 1055 acpi_name(handle), AcpiFormatException(status)); 1056 argset.count = -1; 1057 goto out; 1058 } 1059 res = (ACPI_OBJECT *)dod_buf.Pointer; 1060 if (!ACPI_PKG_VALID(res, 1)) { 1061 printf("evaluation of %s._DOD makes no sense\n", 1062 acpi_name(handle)); 1063 argset.count = -1; 1064 goto out; 1065 } 1066 if (callback == NULL) { 1067 argset.count = res->Package.Count; 1068 goto out; 1069 } 1070 argset.callback = callback; 1071 argset.context = context; 1072 argset.dod_pkg = res; 1073 argset.count = 0; 1074 status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1, 1075 vid_enum_outputs_subr, NULL, &argset, NULL); 1076 if (ACPI_FAILURE(status)) 1077 printf("failed walking down %s - %s\n", 1078 acpi_name(handle), AcpiFormatException(status)); 1079 out: 1080 if (dod_buf.Pointer != NULL) 1081 AcpiOsFree(dod_buf.Pointer); 1082 return (argset.count); 1083 } 1084 1085 static int 1086 vo_get_brightness_levels(ACPI_HANDLE handle, int **levelp) 1087 { 1088 ACPI_STATUS status; 1089 ACPI_BUFFER bcl_buf; 1090 ACPI_OBJECT *res; 1091 int num, i, n, *levels; 1092 1093 bcl_buf.Length = ACPI_ALLOCATE_BUFFER; 1094 bcl_buf.Pointer = NULL; 1095 status = AcpiEvaluateObject(handle, "_BCL", NULL, &bcl_buf); 1096 if (ACPI_FAILURE(status)) { 1097 if (status != AE_NOT_FOUND) 1098 printf("can't evaluate %s._BCL - %s\n", 1099 acpi_name(handle), AcpiFormatException(status)); 1100 goto out; 1101 } 1102 res = (ACPI_OBJECT *)bcl_buf.Pointer; 1103 if (!ACPI_PKG_VALID(res, 2)) { 1104 printf("evaluation of %s._BCL makes no sense\n", 1105 acpi_name(handle)); 1106 goto out; 1107 } 1108 num = res->Package.Count; 1109 if (num < 2 || levelp == NULL) 1110 goto out; 1111 levels = AcpiOsAllocate(num * sizeof(*levels)); 1112 if (levels == NULL) 1113 goto out; 1114 for (i = 0, n = 0; i < num; i++) 1115 if (acpi_PkgInt32(res, i, &levels[n]) == 0) 1116 n++; 1117 if (n < 2) { 1118 AcpiOsFree(levels); 1119 goto out; 1120 } 1121 *levelp = levels; 1122 return (n); 1123 1124 out: 1125 if (bcl_buf.Pointer != NULL) 1126 AcpiOsFree(bcl_buf.Pointer); 1127 return (0); 1128 } 1129 1130 static int 1131 vo_get_bqc(struct acpi_video_output *vo, UINT32 *level) 1132 { 1133 ACPI_STATUS status; 1134 1135 switch (vo->vo_hasbqc) { 1136 case 1: 1137 case -1: 1138 status = acpi_GetInteger(vo->handle, "_BQC", level); 1139 if (vo->vo_hasbqc == 1) 1140 break; 1141 vo->vo_hasbqc = status != AE_NOT_FOUND; 1142 if (vo->vo_hasbqc == 1) 1143 break; 1144 /* FALLTHROUGH */ 1145 default: 1146 KASSERT(vo->vo_hasbqc == 0, 1147 ("bad vo_hasbqc state %d", vo->vo_hasbqc)); 1148 *level = vo->vo_level; 1149 status = AE_OK; 1150 } 1151 return (status); 1152 } 1153 1154 static int 1155 vo_get_brightness(struct acpi_video_output *vo) 1156 { 1157 UINT32 level; 1158 ACPI_STATUS status; 1159 1160 ACPI_SERIAL_ASSERT(video_output); 1161 status = vo_get_bqc(vo, &level); 1162 if (ACPI_FAILURE(status)) { 1163 printf("can't evaluate %s._BQC - %s\n", acpi_name(vo->handle), 1164 AcpiFormatException(status)); 1165 return (-1); 1166 } 1167 if (level > 100) 1168 return (-1); 1169 1170 return (level); 1171 } 1172 1173 static void 1174 vo_set_brightness(struct acpi_video_output *vo, int level) 1175 { 1176 char notify_buf[16]; 1177 ACPI_STATUS status; 1178 1179 ACPI_SERIAL_ASSERT(video_output); 1180 status = acpi_SetInteger(vo->handle, "_BCM", level); 1181 if (ACPI_FAILURE(status)) { 1182 printf("can't evaluate %s._BCM - %s\n", 1183 acpi_name(vo->handle), AcpiFormatException(status)); 1184 } else { 1185 vo->vo_level = level; 1186 } 1187 snprintf(notify_buf, sizeof(notify_buf), "notify=%d", level); 1188 devctl_notify("ACPI", "Video", "brightness", notify_buf); 1189 } 1190 1191 static UINT32 1192 vo_get_device_status(ACPI_HANDLE handle) 1193 { 1194 UINT32 dcs; 1195 ACPI_STATUS status; 1196 1197 ACPI_SERIAL_ASSERT(video_output); 1198 dcs = 0; 1199 status = acpi_GetInteger(handle, "_DCS", &dcs); 1200 if (ACPI_FAILURE(status)) { 1201 /* 1202 * If the method is missing, assume that the device is always 1203 * operational. 1204 */ 1205 if (status != AE_NOT_FOUND) { 1206 printf("can't evaluate %s._DCS - %s\n", 1207 acpi_name(handle), AcpiFormatException(status)); 1208 } else { 1209 dcs = 0xff; 1210 } 1211 } 1212 1213 return (dcs); 1214 } 1215 1216 static UINT32 1217 vo_get_graphics_state(ACPI_HANDLE handle) 1218 { 1219 UINT32 dgs; 1220 ACPI_STATUS status; 1221 1222 dgs = 0; 1223 status = acpi_GetInteger(handle, "_DGS", &dgs); 1224 if (ACPI_FAILURE(status)) { 1225 /* 1226 * If the method is missing, assume that the device is always 1227 * operational. 1228 */ 1229 if (status != AE_NOT_FOUND) { 1230 printf("can't evaluate %s._DGS - %s\n", 1231 acpi_name(handle), AcpiFormatException(status)); 1232 } else { 1233 dgs = 0xff; 1234 } 1235 } 1236 1237 return (dgs); 1238 } 1239 1240 static void 1241 vo_set_device_state(ACPI_HANDLE handle, UINT32 state) 1242 { 1243 ACPI_STATUS status; 1244 1245 ACPI_SERIAL_ASSERT(video_output); 1246 status = acpi_SetInteger(handle, "_DSS", state); 1247 if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) 1248 printf("can't evaluate %s._DSS - %s\n", 1249 acpi_name(handle), AcpiFormatException(status)); 1250 } 1251