1126430Snjl/*- 2126430Snjl * Copyright (c) 2002-2003 Taku YAMAMOTO <taku@cent.saitama-u.ac.jp> 3126430Snjl * All rights reserved. 4126430Snjl * 5126430Snjl * Redistribution and use in source and binary forms, with or without 6126430Snjl * modification, are permitted provided that the following conditions 7126430Snjl * are met: 8126430Snjl * 1. Redistributions of source code must retain the above copyright 9126430Snjl * notice, this list of conditions and the following disclaimer. 10126430Snjl * 2. Redistributions in binary form must reproduce the above copyright 11126430Snjl * notice, this list of conditions and the following disclaimer in the 12126430Snjl * documentation and/or other materials provided with the distribution. 13126430Snjl * 14126430Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15126430Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16126430Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17126430Snjl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18126430Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19126430Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20126430Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21126430Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22126430Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23126430Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24126430Snjl * SUCH DAMAGE. 25126430Snjl * 26126430Snjl * $Id: acpi_vid.c,v 1.4 2003/10/13 10:07:36 taku Exp $ 27126430Snjl */ 28126430Snjl 29143002Sobrien#include <sys/cdefs.h> 30143002Sobrien__FBSDID("$FreeBSD$"); 31143002Sobrien 32126430Snjl#include <sys/param.h> 33126430Snjl#include <sys/kernel.h> 34126430Snjl#include <sys/malloc.h> 35129879Sphk#include <sys/module.h> 36126430Snjl#include <sys/bus.h> 37126430Snjl#include <sys/power.h> 38126430Snjl#include <sys/queue.h> 39126430Snjl#include <sys/sysctl.h> 40126430Snjl 41193530Sjkim#include <contrib/dev/acpica/include/acpi.h> 42193530Sjkim 43126430Snjl#include <dev/acpica/acpivar.h> 44126430Snjl 45126430Snjl/* ACPI video extension driver. */ 46126430Snjlstruct acpi_video_output { 47126430Snjl ACPI_HANDLE handle; 48126430Snjl UINT32 adr; 49126430Snjl STAILQ_ENTRY(acpi_video_output) vo_next; 50126430Snjl struct { 51126430Snjl int num; 52126430Snjl STAILQ_ENTRY(acpi_video_output) next; 53126430Snjl } vo_unit; 54126430Snjl int vo_brightness; 55126430Snjl int vo_fullpower; 56126430Snjl int vo_economy; 57126430Snjl int vo_numlevels; 58126430Snjl int *vo_levels; 59126430Snjl struct sysctl_ctx_list vo_sysctl_ctx; 60126430Snjl struct sysctl_oid *vo_sysctl_tree; 61126430Snjl}; 62126430Snjl 63126430SnjlSTAILQ_HEAD(acpi_video_output_queue, acpi_video_output); 64126430Snjl 65126430Snjlstruct acpi_video_softc { 66126430Snjl device_t device; 67126430Snjl ACPI_HANDLE handle; 68132526Snjl struct acpi_video_output_queue vid_outputs; 69126430Snjl eventhandler_tag vid_pwr_evh; 70126430Snjl}; 71126430Snjl 72126430Snjl/* interfaces */ 73126430Snjlstatic int acpi_video_modevent(struct module*, int, void *); 74153578Sjhbstatic void acpi_video_identify(driver_t *driver, device_t parent); 75126430Snjlstatic int acpi_video_probe(device_t); 76126430Snjlstatic int acpi_video_attach(device_t); 77126430Snjlstatic int acpi_video_detach(device_t); 78237197Siwasakistatic int acpi_video_resume(device_t); 79126430Snjlstatic int acpi_video_shutdown(device_t); 80126430Snjlstatic void acpi_video_notify_handler(ACPI_HANDLE, UINT32, void *); 81126430Snjlstatic void acpi_video_power_profile(void *); 82126430Snjlstatic void acpi_video_bind_outputs(struct acpi_video_softc *); 83132526Snjlstatic struct acpi_video_output *acpi_video_vo_init(UINT32); 84126430Snjlstatic void acpi_video_vo_bind(struct acpi_video_output *, ACPI_HANDLE); 85126430Snjlstatic void acpi_video_vo_destroy(struct acpi_video_output *); 86126430Snjlstatic int acpi_video_vo_check_level(struct acpi_video_output *, int); 87203810Sjkimstatic void acpi_video_vo_notify_handler(ACPI_HANDLE, UINT32, void *); 88126430Snjlstatic int acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS); 89126430Snjlstatic int acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS); 90126430Snjlstatic int acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS); 91126430Snjlstatic int acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS); 92126430Snjl 93126430Snjl/* operations */ 94126430Snjlstatic void vid_set_switch_policy(ACPI_HANDLE, UINT32); 95126430Snjlstatic int vid_enum_outputs(ACPI_HANDLE, 96132526Snjl void(*)(ACPI_HANDLE, UINT32, void *), void *); 97133625Snjlstatic int vo_get_brightness_levels(ACPI_HANDLE, int **); 98203810Sjkimstatic int vo_get_brightness(ACPI_HANDLE); 99126430Snjlstatic void vo_set_brightness(ACPI_HANDLE, int); 100126430Snjlstatic UINT32 vo_get_device_status(ACPI_HANDLE); 101133625Snjlstatic UINT32 vo_get_graphics_state(ACPI_HANDLE); 102126430Snjlstatic void vo_set_device_state(ACPI_HANDLE, UINT32); 103126430Snjl 104126430Snjl/* events */ 105203936Sjkim#define VID_NOTIFY_SWITCHED 0x80 106203936Sjkim#define VID_NOTIFY_REPROBE 0x81 107203936Sjkim#define VID_NOTIFY_CYCLE_BRN 0x85 108203810Sjkim#define VID_NOTIFY_INC_BRN 0x86 109203810Sjkim#define VID_NOTIFY_DEC_BRN 0x87 110203936Sjkim#define VID_NOTIFY_ZERO_BRN 0x88 111126430Snjl 112126430Snjl/* _DOS (Enable/Disable Output Switching) argument bits */ 113203936Sjkim#define DOS_SWITCH_MASK 3 114203936Sjkim#define DOS_SWITCH_BY_OSPM 0 115203936Sjkim#define DOS_SWITCH_BY_BIOS 1 116203936Sjkim#define DOS_SWITCH_LOCKED 2 117203936Sjkim#define DOS_BRIGHTNESS_BY_OSPM (1 << 2) 118126430Snjl 119126430Snjl/* _DOD and subdev's _ADR */ 120203936Sjkim#define DOD_DEVID_MASK 0x0f00 121203936Sjkim#define DOD_DEVID_MASK_FULL 0xffff 122203936Sjkim#define DOD_DEVID_MASK_DISPIDX 0x000f 123203936Sjkim#define DOD_DEVID_MASK_DISPPORT 0x00f0 124203936Sjkim#define DOD_DEVID_MONITOR 0x0100 125203936Sjkim#define DOD_DEVID_LCD 0x0110 126203936Sjkim#define DOD_DEVID_TV 0x0200 127203936Sjkim#define DOD_DEVID_EXT 0x0300 128203936Sjkim#define DOD_DEVID_INTDFP 0x0400 129203936Sjkim#define DOD_BIOS (1 << 16) 130203936Sjkim#define DOD_NONVGA (1 << 17) 131203936Sjkim#define DOD_HEAD_ID_SHIFT 18 132203936Sjkim#define DOD_HEAD_ID_BITS 3 133203936Sjkim#define DOD_HEAD_ID_MASK \ 134132526Snjl (((1 << DOD_HEAD_ID_BITS) - 1) << DOD_HEAD_ID_SHIFT) 135258780Seadler#define DOD_DEVID_SCHEME_STD (1U << 31) 136126430Snjl 137126430Snjl/* _BCL related constants */ 138203936Sjkim#define BCL_FULLPOWER 0 139203936Sjkim#define BCL_ECONOMY 1 140126430Snjl 141126430Snjl/* _DCS (Device Currrent Status) value bits and masks. */ 142203936Sjkim#define DCS_EXISTS (1 << 0) 143203936Sjkim#define DCS_ACTIVE (1 << 1) 144203936Sjkim#define DCS_READY (1 << 2) 145203936Sjkim#define DCS_FUNCTIONAL (1 << 3) 146203936Sjkim#define DCS_ATTACHED (1 << 4) 147126430Snjl 148126430Snjl/* _DSS (Device Set Status) argument bits and masks. */ 149203936Sjkim#define DSS_INACTIVE 0 150203936Sjkim#define DSS_ACTIVE (1 << 0) 151203936Sjkim#define DSS_SETNEXT (1 << 30) 152258780Seadler#define DSS_COMMIT (1U << 31) 153126430Snjl 154126430Snjlstatic device_method_t acpi_video_methods[] = { 155153578Sjhb DEVMETHOD(device_identify, acpi_video_identify), 156126430Snjl DEVMETHOD(device_probe, acpi_video_probe), 157126430Snjl DEVMETHOD(device_attach, acpi_video_attach), 158126430Snjl DEVMETHOD(device_detach, acpi_video_detach), 159237197Siwasaki DEVMETHOD(device_resume, acpi_video_resume), 160126430Snjl DEVMETHOD(device_shutdown, acpi_video_shutdown), 161126430Snjl { 0, 0 } 162126430Snjl}; 163126430Snjl 164126430Snjlstatic driver_t acpi_video_driver = { 165126430Snjl "acpi_video", 166126430Snjl acpi_video_methods, 167126430Snjl sizeof(struct acpi_video_softc), 168126430Snjl}; 169126430Snjl 170126430Snjlstatic devclass_t acpi_video_devclass; 171126430Snjl 172153578SjhbDRIVER_MODULE(acpi_video, vgapci, acpi_video_driver, acpi_video_devclass, 173126430Snjl acpi_video_modevent, NULL); 174128036SnjlMODULE_DEPEND(acpi_video, acpi, 1, 1, 1); 175126430Snjl 176132526Snjlstatic struct sysctl_ctx_list acpi_video_sysctl_ctx; 177132526Snjlstatic struct sysctl_oid *acpi_video_sysctl_tree; 178161184Sbrunostatic struct acpi_video_output_queue crt_units, tv_units, 179161184Sbruno ext_units, lcd_units, other_units; 180126430Snjl 181197648Sjhb/* 182197648Sjhb * The 'video' lock protects the hierarchy of video output devices 183197648Sjhb * (the video "bus"). The 'video_output' lock protects per-output 184197648Sjhb * data is equivalent to a softc lock for each video output. 185197648Sjhb */ 186133625SnjlACPI_SERIAL_DECL(video, "ACPI video"); 187197648SjhbACPI_SERIAL_DECL(video_output, "ACPI video output"); 188227293Sedstatic MALLOC_DEFINE(M_ACPIVIDEO, "acpivideo", "ACPI video extension"); 189126430Snjl 190126430Snjlstatic int 191126430Snjlacpi_video_modevent(struct module *mod __unused, int evt, void *cookie __unused) 192126430Snjl{ 193137438Snjl int err; 194126430Snjl 195137438Snjl err = 0; 196126430Snjl switch (evt) { 197126430Snjl case MOD_LOAD: 198126430Snjl sysctl_ctx_init(&acpi_video_sysctl_ctx); 199126430Snjl STAILQ_INIT(&crt_units); 200126430Snjl STAILQ_INIT(&tv_units); 201161184Sbruno STAILQ_INIT(&ext_units); 202161184Sbruno STAILQ_INIT(&lcd_units); 203126430Snjl STAILQ_INIT(&other_units); 204126430Snjl break; 205126430Snjl case MOD_UNLOAD: 206132256Snjl sysctl_ctx_free(&acpi_video_sysctl_ctx); 207126430Snjl acpi_video_sysctl_tree = NULL; 208126430Snjl break; 209126430Snjl default: 210126430Snjl err = EINVAL; 211126430Snjl } 212126430Snjl 213126430Snjl return (err); 214126430Snjl} 215126430Snjl 216153578Sjhbstatic void 217153578Sjhbacpi_video_identify(driver_t *driver, device_t parent) 218153578Sjhb{ 219153578Sjhb 220153578Sjhb if (device_find_child(parent, "acpi_video", -1) == NULL) 221153578Sjhb device_add_child(parent, "acpi_video", -1); 222153578Sjhb} 223153578Sjhb 224126430Snjlstatic int 225126430Snjlacpi_video_probe(device_t dev) 226126430Snjl{ 227132526Snjl ACPI_HANDLE devh, h; 228132526Snjl ACPI_OBJECT_TYPE t_dos; 229126430Snjl 230132526Snjl devh = acpi_get_handle(dev); 231132526Snjl if (acpi_disabled("video") || 232132526Snjl ACPI_FAILURE(AcpiGetHandle(devh, "_DOD", &h)) || 233132526Snjl ACPI_FAILURE(AcpiGetHandle(devh, "_DOS", &h)) || 234132526Snjl ACPI_FAILURE(AcpiGetType(h, &t_dos)) || 235132526Snjl t_dos != ACPI_TYPE_METHOD) 236132526Snjl return (ENXIO); 237126430Snjl 238132526Snjl device_set_desc(dev, "ACPI video extension"); 239132526Snjl return (0); 240126430Snjl} 241126430Snjl 242126430Snjlstatic int 243126430Snjlacpi_video_attach(device_t dev) 244126430Snjl{ 245126430Snjl struct acpi_softc *acpi_sc; 246126430Snjl struct acpi_video_softc *sc; 247126430Snjl 248126430Snjl sc = device_get_softc(dev); 249126430Snjl 250132256Snjl acpi_sc = devclass_get_softc(devclass_find("acpi"), 0); 251132605Snjl if (acpi_sc == NULL) 252132605Snjl return (ENXIO); 253197648Sjhb ACPI_SERIAL_BEGIN(video); 254132605Snjl if (acpi_video_sysctl_tree == NULL) { 255126430Snjl acpi_video_sysctl_tree = SYSCTL_ADD_NODE(&acpi_video_sysctl_ctx, 256126430Snjl SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 257126430Snjl OID_AUTO, "video", CTLFLAG_RD, 0, 258126430Snjl "video extension control"); 259126430Snjl } 260197648Sjhb ACPI_SERIAL_END(video); 261126430Snjl 262126430Snjl sc->device = dev; 263126430Snjl sc->handle = acpi_get_handle(dev); 264126430Snjl STAILQ_INIT(&sc->vid_outputs); 265126430Snjl 266126430Snjl AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 267126430Snjl acpi_video_notify_handler, sc); 268126430Snjl sc->vid_pwr_evh = EVENTHANDLER_REGISTER(power_profile_change, 269126430Snjl acpi_video_power_profile, sc, 0); 270126430Snjl 271133625Snjl ACPI_SERIAL_BEGIN(video); 272126430Snjl acpi_video_bind_outputs(sc); 273133625Snjl ACPI_SERIAL_END(video); 274126430Snjl 275137438Snjl /* 276137438Snjl * Notify the BIOS that we want to switch both active outputs and 277137438Snjl * brightness levels. 278137438Snjl */ 279137438Snjl vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_OSPM | 280203936Sjkim DOS_BRIGHTNESS_BY_OSPM); 281137438Snjl 282126430Snjl acpi_video_power_profile(sc); 283126430Snjl 284126430Snjl return (0); 285126430Snjl} 286126430Snjl 287126430Snjlstatic int 288126430Snjlacpi_video_detach(device_t dev) 289126430Snjl{ 290126430Snjl struct acpi_video_softc *sc; 291126430Snjl struct acpi_video_output *vo, *vn; 292126430Snjl 293126430Snjl sc = device_get_softc(dev); 294126430Snjl 295126430Snjl vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); 296126430Snjl EVENTHANDLER_DEREGISTER(power_profile_change, sc->vid_pwr_evh); 297126430Snjl AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 298126430Snjl acpi_video_notify_handler); 299126430Snjl 300133625Snjl ACPI_SERIAL_BEGIN(video); 301197438Sjhb STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vn) { 302126430Snjl acpi_video_vo_destroy(vo); 303126430Snjl } 304133625Snjl ACPI_SERIAL_END(video); 305126430Snjl 306126430Snjl return (0); 307126430Snjl} 308126430Snjl 309126430Snjlstatic int 310237197Siwasakiacpi_video_resume(device_t dev) 311237197Siwasaki{ 312237197Siwasaki struct acpi_video_softc *sc; 313237197Siwasaki struct acpi_video_output *vo, *vn; 314237197Siwasaki int level; 315237197Siwasaki 316237197Siwasaki sc = device_get_softc(dev); 317237197Siwasaki 318237197Siwasaki /* Restore brightness level */ 319237197Siwasaki ACPI_SERIAL_BEGIN(video); 320237197Siwasaki ACPI_SERIAL_BEGIN(video_output); 321237197Siwasaki STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vn) { 322237197Siwasaki if ((vo->adr & DOD_DEVID_MASK_FULL) != DOD_DEVID_LCD && 323237197Siwasaki (vo->adr & DOD_DEVID_MASK) != DOD_DEVID_INTDFP) 324237197Siwasaki continue; 325237197Siwasaki 326237197Siwasaki if ((vo_get_device_status(vo->handle) & DCS_ACTIVE) == 0) 327237197Siwasaki continue; 328237197Siwasaki 329237197Siwasaki level = vo_get_brightness(vo->handle); 330237197Siwasaki if (level != -1) 331237197Siwasaki vo_set_brightness(vo->handle, level); 332237197Siwasaki } 333237197Siwasaki ACPI_SERIAL_END(video_output); 334237197Siwasaki ACPI_SERIAL_END(video); 335237197Siwasaki 336237197Siwasaki return (0); 337237197Siwasaki} 338237197Siwasaki 339237197Siwasakistatic int 340126430Snjlacpi_video_shutdown(device_t dev) 341126430Snjl{ 342126430Snjl struct acpi_video_softc *sc; 343126430Snjl 344126430Snjl sc = device_get_softc(dev); 345126430Snjl vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); 346126430Snjl 347126430Snjl return (0); 348126430Snjl} 349126430Snjl 350126430Snjlstatic void 351137438Snjlacpi_video_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 352126430Snjl{ 353126430Snjl struct acpi_video_softc *sc; 354132605Snjl struct acpi_video_output *vo, *vo_tmp; 355137438Snjl ACPI_HANDLE lasthand; 356137438Snjl UINT32 dcs, dss, dss_p; 357126430Snjl 358137438Snjl sc = (struct acpi_video_softc *)context; 359126430Snjl 360126430Snjl switch (notify) { 361126430Snjl case VID_NOTIFY_SWITCHED: 362137438Snjl dss_p = 0; 363137438Snjl lasthand = NULL; 364133625Snjl ACPI_SERIAL_BEGIN(video); 365197648Sjhb ACPI_SERIAL_BEGIN(video_output); 366126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 367133625Snjl dss = vo_get_graphics_state(vo->handle); 368126430Snjl dcs = vo_get_device_status(vo->handle); 369126430Snjl if (!(dcs & DCS_READY)) 370126430Snjl dss = DSS_INACTIVE; 371126430Snjl if (((dcs & DCS_ACTIVE) && dss == DSS_INACTIVE) || 372126430Snjl (!(dcs & DCS_ACTIVE) && dss == DSS_ACTIVE)) { 373126430Snjl if (lasthand != NULL) 374126430Snjl vo_set_device_state(lasthand, dss_p); 375126430Snjl dss_p = dss; 376126430Snjl lasthand = vo->handle; 377126430Snjl } 378126430Snjl } 379126430Snjl if (lasthand != NULL) 380126430Snjl vo_set_device_state(lasthand, dss_p|DSS_COMMIT); 381197648Sjhb ACPI_SERIAL_END(video_output); 382133625Snjl ACPI_SERIAL_END(video); 383126430Snjl break; 384126430Snjl case VID_NOTIFY_REPROBE: 385133625Snjl ACPI_SERIAL_BEGIN(video); 386126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) 387126430Snjl vo->handle = NULL; 388126430Snjl acpi_video_bind_outputs(sc); 389132605Snjl STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vo_tmp) { 390126430Snjl if (vo->handle == NULL) { 391126430Snjl STAILQ_REMOVE(&sc->vid_outputs, vo, 392137438Snjl acpi_video_output, vo_next); 393126430Snjl acpi_video_vo_destroy(vo); 394126430Snjl } 395126430Snjl } 396133625Snjl ACPI_SERIAL_END(video); 397126430Snjl break; 398126430Snjl default: 399137438Snjl device_printf(sc->device, "unknown notify event 0x%x\n", 400137438Snjl notify); 401126430Snjl } 402126430Snjl} 403126430Snjl 404126430Snjlstatic void 405126430Snjlacpi_video_power_profile(void *context) 406126430Snjl{ 407126430Snjl int state; 408126430Snjl struct acpi_video_softc *sc; 409126430Snjl struct acpi_video_output *vo; 410126430Snjl 411126430Snjl sc = context; 412126430Snjl state = power_profile_get_state(); 413126430Snjl if (state != POWER_PROFILE_PERFORMANCE && 414126430Snjl state != POWER_PROFILE_ECONOMY) 415126430Snjl return; 416126430Snjl 417133625Snjl ACPI_SERIAL_BEGIN(video); 418197648Sjhb ACPI_SERIAL_BEGIN(video_output); 419126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 420126430Snjl if (vo->vo_levels != NULL && vo->vo_brightness == -1) 421126430Snjl vo_set_brightness(vo->handle, 422137438Snjl state == POWER_PROFILE_ECONOMY ? 423137438Snjl vo->vo_economy : vo->vo_fullpower); 424126430Snjl } 425197648Sjhb ACPI_SERIAL_END(video_output); 426133625Snjl ACPI_SERIAL_END(video); 427126430Snjl} 428126430Snjl 429126430Snjlstatic void 430126430Snjlacpi_video_bind_outputs_subr(ACPI_HANDLE handle, UINT32 adr, void *context) 431126430Snjl{ 432126430Snjl struct acpi_video_softc *sc; 433126430Snjl struct acpi_video_output *vo; 434126430Snjl 435133625Snjl ACPI_SERIAL_ASSERT(video); 436126430Snjl sc = context; 437126430Snjl 438126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 439126430Snjl if (vo->adr == adr) { 440126430Snjl acpi_video_vo_bind(vo, handle); 441126430Snjl return; 442126430Snjl } 443126430Snjl } 444126430Snjl vo = acpi_video_vo_init(adr); 445126430Snjl if (vo != NULL) { 446126430Snjl acpi_video_vo_bind(vo, handle); 447126430Snjl STAILQ_INSERT_TAIL(&sc->vid_outputs, vo, vo_next); 448126430Snjl } 449126430Snjl} 450126430Snjl 451126430Snjlstatic void 452126430Snjlacpi_video_bind_outputs(struct acpi_video_softc *sc) 453126430Snjl{ 454126430Snjl 455133625Snjl ACPI_SERIAL_ASSERT(video); 456126430Snjl vid_enum_outputs(sc->handle, acpi_video_bind_outputs_subr, sc); 457126430Snjl} 458126430Snjl 459126430Snjlstatic struct acpi_video_output * 460126430Snjlacpi_video_vo_init(UINT32 adr) 461126430Snjl{ 462126430Snjl struct acpi_video_output *vn, *vo, *vp; 463126430Snjl int n, x; 464137438Snjl char name[8], env[32]; 465126430Snjl const char *type, *desc; 466126430Snjl struct acpi_video_output_queue *voqh; 467126430Snjl 468133625Snjl ACPI_SERIAL_ASSERT(video); 469161184Sbruno 470126430Snjl switch (adr & DOD_DEVID_MASK) { 471126430Snjl case DOD_DEVID_MONITOR: 472161184Sbruno if ((adr & DOD_DEVID_MASK_FULL) == DOD_DEVID_LCD) { 473161184Sbruno /* DOD_DEVID_LCD is a common, backward compatible ID */ 474161184Sbruno desc = "Internal/Integrated Digital Flat Panel"; 475161184Sbruno type = "lcd"; 476161184Sbruno voqh = &lcd_units; 477161184Sbruno } else { 478161184Sbruno desc = "VGA CRT or VESA Compatible Analog Monitor"; 479161184Sbruno type = "crt"; 480161184Sbruno voqh = &crt_units; 481161184Sbruno } 482126430Snjl break; 483126430Snjl case DOD_DEVID_TV: 484161184Sbruno desc = "TV/HDTV or Analog-Video Monitor"; 485126430Snjl type = "tv"; 486126430Snjl voqh = &tv_units; 487126430Snjl break; 488161184Sbruno case DOD_DEVID_EXT: 489161184Sbruno desc = "External Digital Monitor"; 490161184Sbruno type = "ext"; 491161184Sbruno voqh = &ext_units; 492161184Sbruno break; 493161184Sbruno case DOD_DEVID_INTDFP: 494161184Sbruno desc = "Internal/Integrated Digital Flat Panel"; 495161184Sbruno type = "lcd"; 496161184Sbruno voqh = &lcd_units; 497161184Sbruno break; 498126430Snjl default: 499126430Snjl desc = "unknown output"; 500126430Snjl type = "out"; 501126430Snjl voqh = &other_units; 502126430Snjl } 503126430Snjl 504126430Snjl n = 0; 505209064Sjkim vp = NULL; 506126430Snjl STAILQ_FOREACH(vn, voqh, vo_unit.next) { 507126430Snjl if (vn->vo_unit.num != n) 508126430Snjl break; 509126430Snjl vp = vn; 510126430Snjl n++; 511126430Snjl } 512126430Snjl 513132526Snjl snprintf(name, sizeof(name), "%s%d", type, n); 514126430Snjl 515126430Snjl vo = malloc(sizeof(*vo), M_ACPIVIDEO, M_NOWAIT); 516126430Snjl if (vo != NULL) { 517126430Snjl vo->handle = NULL; 518126430Snjl vo->adr = adr; 519126430Snjl vo->vo_unit.num = n; 520126430Snjl vo->vo_brightness = -1; 521126430Snjl vo->vo_fullpower = -1; /* TODO: override with tunables */ 522126430Snjl vo->vo_economy = -1; 523126430Snjl vo->vo_numlevels = 0; 524126430Snjl vo->vo_levels = NULL; 525137438Snjl snprintf(env, sizeof(env), "hw.acpi.video.%s.fullpower", name); 526126430Snjl if (getenv_int(env, &x)) 527126430Snjl vo->vo_fullpower = x; 528137438Snjl snprintf(env, sizeof(env), "hw.acpi.video.%s.economy", name); 529126430Snjl if (getenv_int(env, &x)) 530126430Snjl vo->vo_economy = x; 531126430Snjl 532126430Snjl sysctl_ctx_init(&vo->vo_sysctl_ctx); 533126430Snjl if (vp != NULL) 534126430Snjl STAILQ_INSERT_AFTER(voqh, vp, vo, vo_unit.next); 535126430Snjl else 536126430Snjl STAILQ_INSERT_TAIL(voqh, vo, vo_unit.next); 537126430Snjl if (acpi_video_sysctl_tree != NULL) 538126430Snjl vo->vo_sysctl_tree = 539126430Snjl SYSCTL_ADD_NODE(&vo->vo_sysctl_ctx, 540132526Snjl SYSCTL_CHILDREN(acpi_video_sysctl_tree), 541132526Snjl OID_AUTO, name, CTLFLAG_RD, 0, desc); 542126430Snjl if (vo->vo_sysctl_tree != NULL) { 543126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 544132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 545132526Snjl OID_AUTO, "active", 546132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 0, 547132526Snjl acpi_video_vo_active_sysctl, "I", 548132526Snjl "current activity of this device"); 549126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 550132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 551132526Snjl OID_AUTO, "brightness", 552132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 0, 553132526Snjl acpi_video_vo_bright_sysctl, "I", 554132526Snjl "current brightness level"); 555126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 556132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 557132526Snjl OID_AUTO, "fullpower", 558132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 559132526Snjl POWER_PROFILE_PERFORMANCE, 560132526Snjl acpi_video_vo_presets_sysctl, "I", 561132526Snjl "preset level for full power mode"); 562126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 563132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 564132526Snjl OID_AUTO, "economy", 565132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 566132526Snjl POWER_PROFILE_ECONOMY, 567132526Snjl acpi_video_vo_presets_sysctl, "I", 568132526Snjl "preset level for economy mode"); 569126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 570132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 571132526Snjl OID_AUTO, "levels", 572217566Smdf CTLTYPE_INT | CTLFLAG_RD, vo, 0, 573132526Snjl acpi_video_vo_levels_sysctl, "I", 574132526Snjl "supported brightness levels"); 575126430Snjl } else 576126430Snjl printf("%s: sysctl node creation failed\n", type); 577126430Snjl } else 578126430Snjl printf("%s: softc allocation failed\n", type); 579126430Snjl 580126430Snjl if (bootverbose) { 581161184Sbruno printf("found %s(%x)", desc, adr & DOD_DEVID_MASK_FULL); 582161184Sbruno printf(", idx#%x", adr & DOD_DEVID_MASK_DISPIDX); 583161184Sbruno printf(", port#%x", (adr & DOD_DEVID_MASK_DISPPORT) >> 4); 584126430Snjl if (adr & DOD_BIOS) 585126430Snjl printf(", detectable by BIOS"); 586126430Snjl if (adr & DOD_NONVGA) 587161184Sbruno printf(" (Non-VGA output device whose power " 588161184Sbruno "is related to the VGA device)"); 589126430Snjl printf(", head #%d\n", 590161184Sbruno (adr & DOD_HEAD_ID_MASK) >> DOD_HEAD_ID_SHIFT); 591126430Snjl } 592132526Snjl return (vo); 593126430Snjl} 594126430Snjl 595126430Snjlstatic void 596126430Snjlacpi_video_vo_bind(struct acpi_video_output *vo, ACPI_HANDLE handle) 597126430Snjl{ 598126430Snjl 599197648Sjhb ACPI_SERIAL_BEGIN(video_output); 600295938Sjkim if (vo->vo_levels != NULL) { 601295938Sjkim AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY, 602295938Sjkim acpi_video_vo_notify_handler); 603126430Snjl AcpiOsFree(vo->vo_levels); 604295938Sjkim vo->vo_levels = NULL; 605295938Sjkim } 606126430Snjl vo->handle = handle; 607133625Snjl vo->vo_numlevels = vo_get_brightness_levels(handle, &vo->vo_levels); 608126430Snjl if (vo->vo_numlevels >= 2) { 609295935Sjkim if (vo->vo_fullpower == -1 || 610295935Sjkim acpi_video_vo_check_level(vo, vo->vo_fullpower) != 0) { 611126430Snjl /* XXX - can't deal with rebinding... */ 612126430Snjl vo->vo_fullpower = vo->vo_levels[BCL_FULLPOWER]; 613295935Sjkim } 614295935Sjkim if (vo->vo_economy == -1 || 615295936Sjkim acpi_video_vo_check_level(vo, vo->vo_economy) != 0) { 616126430Snjl /* XXX - see above. */ 617126430Snjl vo->vo_economy = vo->vo_levels[BCL_ECONOMY]; 618295935Sjkim } 619204965Sjkim AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, 620204965Sjkim acpi_video_vo_notify_handler, vo); 621295939Sjkim } 622197648Sjhb ACPI_SERIAL_END(video_output); 623126430Snjl} 624126430Snjl 625126430Snjlstatic void 626126430Snjlacpi_video_vo_destroy(struct acpi_video_output *vo) 627126430Snjl{ 628126430Snjl struct acpi_video_output_queue *voqh; 629126430Snjl 630133625Snjl ACPI_SERIAL_ASSERT(video); 631126430Snjl if (vo->vo_sysctl_tree != NULL) { 632126430Snjl vo->vo_sysctl_tree = NULL; 633126430Snjl sysctl_ctx_free(&vo->vo_sysctl_ctx); 634126430Snjl } 635203810Sjkim if (vo->vo_levels != NULL) { 636203810Sjkim AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY, 637203810Sjkim acpi_video_vo_notify_handler); 638126430Snjl AcpiOsFree(vo->vo_levels); 639203810Sjkim } 640126430Snjl 641126430Snjl switch (vo->adr & DOD_DEVID_MASK) { 642126430Snjl case DOD_DEVID_MONITOR: 643126430Snjl voqh = &crt_units; 644126430Snjl break; 645126430Snjl case DOD_DEVID_TV: 646126430Snjl voqh = &tv_units; 647126430Snjl break; 648161184Sbruno case DOD_DEVID_EXT: 649161184Sbruno voqh = &ext_units; 650161184Sbruno break; 651161184Sbruno case DOD_DEVID_INTDFP: 652161184Sbruno voqh = &lcd_units; 653161184Sbruno break; 654126430Snjl default: 655126430Snjl voqh = &other_units; 656126430Snjl } 657126430Snjl STAILQ_REMOVE(voqh, vo, acpi_video_output, vo_unit.next); 658126430Snjl free(vo, M_ACPIVIDEO); 659126430Snjl} 660126430Snjl 661126430Snjlstatic int 662126430Snjlacpi_video_vo_check_level(struct acpi_video_output *vo, int level) 663126430Snjl{ 664126430Snjl int i; 665126430Snjl 666197648Sjhb ACPI_SERIAL_ASSERT(video_output); 667126430Snjl if (vo->vo_levels == NULL) 668126430Snjl return (ENODEV); 669126430Snjl for (i = 0; i < vo->vo_numlevels; i++) 670126430Snjl if (vo->vo_levels[i] == level) 671126430Snjl return (0); 672126430Snjl return (EINVAL); 673126430Snjl} 674126430Snjl 675203810Sjkimstatic void 676203810Sjkimacpi_video_vo_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 677203810Sjkim{ 678203810Sjkim struct acpi_video_output *vo; 679203810Sjkim int i, j, level, new_level; 680203810Sjkim 681203810Sjkim vo = context; 682203810Sjkim ACPI_SERIAL_BEGIN(video_output); 683203935Sjkim if (vo->handle != handle) 684203935Sjkim goto out; 685203810Sjkim 686203810Sjkim switch (notify) { 687203936Sjkim case VID_NOTIFY_CYCLE_BRN: 688203935Sjkim if (vo->vo_numlevels <= 3) 689203935Sjkim goto out; 690203935Sjkim /* FALLTHROUGH */ 691203810Sjkim case VID_NOTIFY_INC_BRN: 692203810Sjkim case VID_NOTIFY_DEC_BRN: 693203936Sjkim case VID_NOTIFY_ZERO_BRN: 694203810Sjkim if (vo->vo_levels == NULL) 695203935Sjkim goto out; 696203813Sjkim level = vo_get_brightness(handle); 697203810Sjkim if (level < 0) 698203935Sjkim goto out; 699203935Sjkim break; 700203935Sjkim default: 701203935Sjkim printf("unknown notify event 0x%x from %s\n", 702203935Sjkim notify, acpi_name(handle)); 703203935Sjkim goto out; 704203935Sjkim } 705203935Sjkim 706203935Sjkim new_level = level; 707203935Sjkim switch (notify) { 708203936Sjkim case VID_NOTIFY_CYCLE_BRN: 709203935Sjkim for (i = 2; i < vo->vo_numlevels; i++) 710203935Sjkim if (vo->vo_levels[i] == level) { 711203935Sjkim new_level = vo->vo_numlevels > i + 1 ? 712203935Sjkim vo->vo_levels[i + 1] : vo->vo_levels[2]; 713203935Sjkim break; 714203935Sjkim } 715203935Sjkim break; 716203935Sjkim case VID_NOTIFY_INC_BRN: 717203935Sjkim case VID_NOTIFY_DEC_BRN: 718203810Sjkim for (i = 0; i < vo->vo_numlevels; i++) { 719203810Sjkim j = vo->vo_levels[i]; 720203810Sjkim if (notify == VID_NOTIFY_INC_BRN) { 721203810Sjkim if (j > level && 722203810Sjkim (j < new_level || level == new_level)) 723203810Sjkim new_level = j; 724203810Sjkim } else { 725203810Sjkim if (j < level && 726203810Sjkim (j > new_level || level == new_level)) 727203810Sjkim new_level = j; 728203810Sjkim } 729203810Sjkim } 730203810Sjkim break; 731203936Sjkim case VID_NOTIFY_ZERO_BRN: 732203935Sjkim for (i = 0; i < vo->vo_numlevels; i++) 733203935Sjkim if (vo->vo_levels[i] == 0) { 734203935Sjkim new_level = 0; 735203935Sjkim break; 736203935Sjkim } 737203935Sjkim break; 738203810Sjkim } 739203935Sjkim if (new_level != level) { 740203935Sjkim vo_set_brightness(handle, new_level); 741203935Sjkim vo->vo_brightness = new_level; 742203935Sjkim } 743203935Sjkim 744203935Sjkimout: 745203810Sjkim ACPI_SERIAL_END(video_output); 746203810Sjkim} 747203810Sjkim 748126430Snjl/* ARGSUSED */ 749126430Snjlstatic int 750126430Snjlacpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS) 751126430Snjl{ 752126430Snjl struct acpi_video_output *vo; 753126430Snjl int state, err; 754126430Snjl 755126430Snjl vo = (struct acpi_video_output *)arg1; 756133625Snjl if (vo->handle == NULL) 757133625Snjl return (ENXIO); 758197648Sjhb ACPI_SERIAL_BEGIN(video_output); 759133625Snjl state = (vo_get_device_status(vo->handle) & DCS_ACTIVE) ? 1 : 0; 760126430Snjl err = sysctl_handle_int(oidp, &state, 0, req); 761126430Snjl if (err != 0 || req->newptr == NULL) 762126430Snjl goto out; 763126430Snjl vo_set_device_state(vo->handle, 764137438Snjl DSS_COMMIT | (state ? DSS_ACTIVE : DSS_INACTIVE)); 765126430Snjlout: 766197648Sjhb ACPI_SERIAL_END(video_output); 767126430Snjl return (err); 768126430Snjl} 769126430Snjl 770126430Snjl/* ARGSUSED */ 771126430Snjlstatic int 772126430Snjlacpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS) 773126430Snjl{ 774126430Snjl struct acpi_video_output *vo; 775126430Snjl int level, preset, err; 776126430Snjl 777126430Snjl vo = (struct acpi_video_output *)arg1; 778197648Sjhb ACPI_SERIAL_BEGIN(video_output); 779126430Snjl if (vo->handle == NULL) { 780126430Snjl err = ENXIO; 781126430Snjl goto out; 782126430Snjl } 783126430Snjl if (vo->vo_levels == NULL) { 784126430Snjl err = ENODEV; 785126430Snjl goto out; 786126430Snjl } 787126430Snjl 788132526Snjl preset = (power_profile_get_state() == POWER_PROFILE_ECONOMY) ? 789132526Snjl vo->vo_economy : vo->vo_fullpower; 790126430Snjl level = vo->vo_brightness; 791126430Snjl if (level == -1) 792126430Snjl level = preset; 793126430Snjl 794126430Snjl err = sysctl_handle_int(oidp, &level, 0, req); 795126430Snjl if (err != 0 || req->newptr == NULL) 796126430Snjl goto out; 797126430Snjl if (level < -1 || level > 100) { 798126430Snjl err = EINVAL; 799126430Snjl goto out; 800126430Snjl } 801126430Snjl 802126430Snjl if (level != -1 && (err = acpi_video_vo_check_level(vo, level))) 803126430Snjl goto out; 804126430Snjl vo->vo_brightness = level; 805133625Snjl vo_set_brightness(vo->handle, (level == -1) ? preset : level); 806133625Snjl 807126430Snjlout: 808197648Sjhb ACPI_SERIAL_END(video_output); 809126430Snjl return (err); 810126430Snjl} 811126430Snjl 812126430Snjlstatic int 813126430Snjlacpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS) 814126430Snjl{ 815126430Snjl struct acpi_video_output *vo; 816137438Snjl int i, level, *preset, err; 817126430Snjl 818126430Snjl vo = (struct acpi_video_output *)arg1; 819197648Sjhb ACPI_SERIAL_BEGIN(video_output); 820126430Snjl if (vo->handle == NULL) { 821126430Snjl err = ENXIO; 822126430Snjl goto out; 823126430Snjl } 824126430Snjl if (vo->vo_levels == NULL) { 825126430Snjl err = ENODEV; 826126430Snjl goto out; 827126430Snjl } 828132526Snjl preset = (arg2 == POWER_PROFILE_ECONOMY) ? 829132526Snjl &vo->vo_economy : &vo->vo_fullpower; 830126430Snjl level = *preset; 831126430Snjl err = sysctl_handle_int(oidp, &level, 0, req); 832126430Snjl if (err != 0 || req->newptr == NULL) 833126430Snjl goto out; 834126430Snjl if (level < -1 || level > 100) { 835126430Snjl err = EINVAL; 836126430Snjl goto out; 837126430Snjl } 838133625Snjl if (level == -1) { 839133625Snjl i = (arg2 == POWER_PROFILE_ECONOMY) ? 840133625Snjl BCL_ECONOMY : BCL_FULLPOWER; 841133625Snjl level = vo->vo_levels[i]; 842137438Snjl } else if ((err = acpi_video_vo_check_level(vo, level)) != 0) 843126430Snjl goto out; 844126430Snjl 845126430Snjl if (vo->vo_brightness == -1 && (power_profile_get_state() == arg2)) 846126430Snjl vo_set_brightness(vo->handle, level); 847126430Snjl *preset = level; 848133625Snjl 849126430Snjlout: 850197648Sjhb ACPI_SERIAL_END(video_output); 851126430Snjl return (err); 852126430Snjl} 853126430Snjl 854126430Snjl/* ARGSUSED */ 855126430Snjlstatic int 856126430Snjlacpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS) 857126430Snjl{ 858126430Snjl struct acpi_video_output *vo; 859126430Snjl int err; 860126430Snjl 861126430Snjl vo = (struct acpi_video_output *)arg1; 862197648Sjhb ACPI_SERIAL_BEGIN(video_output); 863126430Snjl if (vo->vo_levels == NULL) { 864126430Snjl err = ENODEV; 865126430Snjl goto out; 866126430Snjl } 867126430Snjl if (req->newptr != NULL) { 868126430Snjl err = EPERM; 869126430Snjl goto out; 870126430Snjl } 871126430Snjl err = sysctl_handle_opaque(oidp, vo->vo_levels, 872133625Snjl vo->vo_numlevels * sizeof(*vo->vo_levels), req); 873133625Snjl 874126430Snjlout: 875197648Sjhb ACPI_SERIAL_END(video_output); 876126430Snjl return (err); 877126430Snjl} 878126430Snjl 879126430Snjlstatic void 880126430Snjlvid_set_switch_policy(ACPI_HANDLE handle, UINT32 policy) 881126430Snjl{ 882126430Snjl ACPI_STATUS status; 883126430Snjl 884126560Snjl status = acpi_SetInteger(handle, "_DOS", policy); 885126430Snjl if (ACPI_FAILURE(status)) 886126430Snjl printf("can't evaluate %s._DOS - %s\n", 887126430Snjl acpi_name(handle), AcpiFormatException(status)); 888126430Snjl} 889126430Snjl 890126430Snjlstruct enum_callback_arg { 891126430Snjl void (*callback)(ACPI_HANDLE, UINT32, void *); 892126430Snjl void *context; 893126430Snjl ACPI_OBJECT *dod_pkg; 894132605Snjl int count; 895126430Snjl}; 896126430Snjl 897126430Snjlstatic ACPI_STATUS 898126430Snjlvid_enum_outputs_subr(ACPI_HANDLE handle, UINT32 level __unused, 899132605Snjl void *context, void **retp __unused) 900126430Snjl{ 901126430Snjl ACPI_STATUS status; 902132605Snjl UINT32 adr, val; 903126430Snjl struct enum_callback_arg *argset; 904126430Snjl size_t i; 905126430Snjl 906133625Snjl ACPI_SERIAL_ASSERT(video); 907126430Snjl argset = context; 908126560Snjl status = acpi_GetInteger(handle, "_ADR", &adr); 909132605Snjl if (ACPI_FAILURE(status)) 910132605Snjl return (AE_OK); 911132605Snjl 912132605Snjl for (i = 0; i < argset->dod_pkg->Package.Count; i++) { 913132605Snjl if (acpi_PkgInt32(argset->dod_pkg, i, &val) == 0 && 914241748Sjhb (val & DOD_DEVID_MASK_FULL) == 915241748Sjhb (adr & DOD_DEVID_MASK_FULL)) { 916132605Snjl argset->callback(handle, val, argset->context); 917132605Snjl argset->count++; 918126430Snjl } 919126430Snjl } 920126430Snjl 921126430Snjl return (AE_OK); 922126430Snjl} 923126430Snjl 924126430Snjlstatic int 925126430Snjlvid_enum_outputs(ACPI_HANDLE handle, 926126430Snjl void (*callback)(ACPI_HANDLE, UINT32, void *), void *context) 927126430Snjl{ 928126430Snjl ACPI_STATUS status; 929126430Snjl ACPI_BUFFER dod_buf; 930126430Snjl ACPI_OBJECT *res; 931126430Snjl struct enum_callback_arg argset; 932126430Snjl 933133625Snjl ACPI_SERIAL_ASSERT(video); 934126430Snjl dod_buf.Length = ACPI_ALLOCATE_BUFFER; 935126430Snjl dod_buf.Pointer = NULL; 936126430Snjl status = AcpiEvaluateObject(handle, "_DOD", NULL, &dod_buf); 937126430Snjl if (ACPI_FAILURE(status)) { 938126430Snjl if (status != AE_NOT_FOUND) 939126430Snjl printf("can't evaluate %s._DOD - %s\n", 940126430Snjl acpi_name(handle), AcpiFormatException(status)); 941132605Snjl argset.count = -1; 942126430Snjl goto out; 943126430Snjl } 944126430Snjl res = (ACPI_OBJECT *)dod_buf.Pointer; 945132605Snjl if (!ACPI_PKG_VALID(res, 1)) { 946126430Snjl printf("evaluation of %s._DOD makes no sense\n", 947126430Snjl acpi_name(handle)); 948132605Snjl argset.count = -1; 949126430Snjl goto out; 950126430Snjl } 951126430Snjl if (callback == NULL) { 952132605Snjl argset.count = res->Package.Count; 953126430Snjl goto out; 954126430Snjl } 955126430Snjl argset.callback = callback; 956126430Snjl argset.context = context; 957126430Snjl argset.dod_pkg = res; 958132605Snjl argset.count = 0; 959126430Snjl status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1, 960199337Sjkim vid_enum_outputs_subr, NULL, &argset, NULL); 961126430Snjl if (ACPI_FAILURE(status)) 962126430Snjl printf("failed walking down %s - %s\n", 963126430Snjl acpi_name(handle), AcpiFormatException(status)); 964126430Snjlout: 965126430Snjl if (dod_buf.Pointer != NULL) 966126430Snjl AcpiOsFree(dod_buf.Pointer); 967132605Snjl return (argset.count); 968126430Snjl} 969126430Snjl 970126430Snjlstatic int 971133625Snjlvo_get_brightness_levels(ACPI_HANDLE handle, int **levelp) 972126430Snjl{ 973126430Snjl ACPI_STATUS status; 974126430Snjl ACPI_BUFFER bcl_buf; 975132605Snjl ACPI_OBJECT *res; 976137438Snjl int num, i, n, *levels; 977126430Snjl 978126430Snjl bcl_buf.Length = ACPI_ALLOCATE_BUFFER; 979126430Snjl bcl_buf.Pointer = NULL; 980126430Snjl status = AcpiEvaluateObject(handle, "_BCL", NULL, &bcl_buf); 981126430Snjl if (ACPI_FAILURE(status)) { 982126430Snjl if (status != AE_NOT_FOUND) 983126430Snjl printf("can't evaluate %s._BCL - %s\n", 984126430Snjl acpi_name(handle), AcpiFormatException(status)); 985126430Snjl goto out; 986126430Snjl } 987126430Snjl res = (ACPI_OBJECT *)bcl_buf.Pointer; 988132605Snjl if (!ACPI_PKG_VALID(res, 2)) { 989126430Snjl printf("evaluation of %s._BCL makes no sense\n", 990126430Snjl acpi_name(handle)); 991126430Snjl goto out; 992126430Snjl } 993126430Snjl num = res->Package.Count; 994209065Sjkim if (num < 2 || levelp == NULL) 995126430Snjl goto out; 996132605Snjl levels = AcpiOsAllocate(num * sizeof(*levels)); 997209065Sjkim if (levels == NULL) 998126430Snjl goto out; 999132605Snjl for (i = 0, n = 0; i < num; i++) 1000132605Snjl if (acpi_PkgInt32(res, i, &levels[n]) == 0) 1001132605Snjl n++; 1002126430Snjl if (n < 2) { 1003126430Snjl AcpiOsFree(levels); 1004209065Sjkim goto out; 1005126430Snjl } 1006209065Sjkim *levelp = levels; 1007209065Sjkim return (n); 1008209065Sjkim 1009126430Snjlout: 1010126430Snjl if (bcl_buf.Pointer != NULL) 1011126430Snjl AcpiOsFree(bcl_buf.Pointer); 1012209065Sjkim return (0); 1013126430Snjl} 1014126430Snjl 1015203810Sjkimstatic int 1016203810Sjkimvo_get_brightness(ACPI_HANDLE handle) 1017203810Sjkim{ 1018203810Sjkim UINT32 level; 1019203810Sjkim ACPI_STATUS status; 1020203810Sjkim 1021203810Sjkim ACPI_SERIAL_ASSERT(video_output); 1022203810Sjkim status = acpi_GetInteger(handle, "_BQC", &level); 1023203810Sjkim if (ACPI_FAILURE(status)) { 1024203810Sjkim printf("can't evaluate %s._BQC - %s\n", acpi_name(handle), 1025203810Sjkim AcpiFormatException(status)); 1026203810Sjkim return (-1); 1027203810Sjkim } 1028203810Sjkim if (level > 100) 1029203810Sjkim return (-1); 1030203810Sjkim 1031203810Sjkim return (level); 1032203810Sjkim} 1033203810Sjkim 1034126430Snjlstatic void 1035126430Snjlvo_set_brightness(ACPI_HANDLE handle, int level) 1036126430Snjl{ 1037126430Snjl ACPI_STATUS status; 1038126430Snjl 1039197648Sjhb ACPI_SERIAL_ASSERT(video_output); 1040126560Snjl status = acpi_SetInteger(handle, "_BCM", level); 1041126430Snjl if (ACPI_FAILURE(status)) 1042126430Snjl printf("can't evaluate %s._BCM - %s\n", 1043126430Snjl acpi_name(handle), AcpiFormatException(status)); 1044126430Snjl} 1045126430Snjl 1046126430Snjlstatic UINT32 1047126430Snjlvo_get_device_status(ACPI_HANDLE handle) 1048126430Snjl{ 1049137438Snjl UINT32 dcs; 1050126430Snjl ACPI_STATUS status; 1051126430Snjl 1052197648Sjhb ACPI_SERIAL_ASSERT(video_output); 1053137438Snjl dcs = 0; 1054126560Snjl status = acpi_GetInteger(handle, "_DCS", &dcs); 1055126430Snjl if (ACPI_FAILURE(status)) 1056126430Snjl printf("can't evaluate %s._DCS - %s\n", 1057126430Snjl acpi_name(handle), AcpiFormatException(status)); 1058126430Snjl 1059126430Snjl return (dcs); 1060126430Snjl} 1061126430Snjl 1062126430Snjlstatic UINT32 1063133625Snjlvo_get_graphics_state(ACPI_HANDLE handle) 1064126430Snjl{ 1065137438Snjl UINT32 dgs; 1066126430Snjl ACPI_STATUS status; 1067126430Snjl 1068137438Snjl dgs = 0; 1069126560Snjl status = acpi_GetInteger(handle, "_DGS", &dgs); 1070126430Snjl if (ACPI_FAILURE(status)) 1071126430Snjl printf("can't evaluate %s._DGS - %s\n", 1072126430Snjl acpi_name(handle), AcpiFormatException(status)); 1073126430Snjl 1074126430Snjl return (dgs); 1075126430Snjl} 1076126430Snjl 1077126430Snjlstatic void 1078126430Snjlvo_set_device_state(ACPI_HANDLE handle, UINT32 state) 1079126430Snjl{ 1080126430Snjl ACPI_STATUS status; 1081126430Snjl 1082197648Sjhb ACPI_SERIAL_ASSERT(video_output); 1083126560Snjl status = acpi_SetInteger(handle, "_DSS", state); 1084126430Snjl if (ACPI_FAILURE(status)) 1085126430Snjl printf("can't evaluate %s._DSS - %s\n", 1086126430Snjl acpi_name(handle), AcpiFormatException(status)); 1087126430Snjl} 1088