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) 135203936Sjkim#define DOD_DEVID_SCHEME_STD (1 << 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) 152203936Sjkim#define DSS_COMMIT (1 << 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); 600126430Snjl if (vo->vo_levels != NULL) 601126430Snjl AcpiOsFree(vo->vo_levels); 602126430Snjl vo->handle = handle; 603133625Snjl vo->vo_numlevels = vo_get_brightness_levels(handle, &vo->vo_levels); 604126430Snjl if (vo->vo_numlevels >= 2) { 605126430Snjl if (vo->vo_fullpower == -1 606126430Snjl || acpi_video_vo_check_level(vo, vo->vo_fullpower) != 0) 607126430Snjl /* XXX - can't deal with rebinding... */ 608126430Snjl vo->vo_fullpower = vo->vo_levels[BCL_FULLPOWER]; 609126430Snjl if (vo->vo_economy == -1 610126430Snjl || acpi_video_vo_check_level(vo, vo->vo_economy) != 0) 611126430Snjl /* XXX - see above. */ 612126430Snjl vo->vo_economy = vo->vo_levels[BCL_ECONOMY]; 613126430Snjl } 614203810Sjkim if (vo->vo_levels != NULL) 615204965Sjkim AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, 616204965Sjkim acpi_video_vo_notify_handler, vo); 617197648Sjhb ACPI_SERIAL_END(video_output); 618126430Snjl} 619126430Snjl 620126430Snjlstatic void 621126430Snjlacpi_video_vo_destroy(struct acpi_video_output *vo) 622126430Snjl{ 623126430Snjl struct acpi_video_output_queue *voqh; 624126430Snjl 625133625Snjl ACPI_SERIAL_ASSERT(video); 626126430Snjl if (vo->vo_sysctl_tree != NULL) { 627126430Snjl vo->vo_sysctl_tree = NULL; 628126430Snjl sysctl_ctx_free(&vo->vo_sysctl_ctx); 629126430Snjl } 630203810Sjkim if (vo->vo_levels != NULL) { 631203810Sjkim AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY, 632203810Sjkim acpi_video_vo_notify_handler); 633126430Snjl AcpiOsFree(vo->vo_levels); 634203810Sjkim } 635126430Snjl 636126430Snjl switch (vo->adr & DOD_DEVID_MASK) { 637126430Snjl case DOD_DEVID_MONITOR: 638126430Snjl voqh = &crt_units; 639126430Snjl break; 640126430Snjl case DOD_DEVID_TV: 641126430Snjl voqh = &tv_units; 642126430Snjl break; 643161184Sbruno case DOD_DEVID_EXT: 644161184Sbruno voqh = &ext_units; 645161184Sbruno break; 646161184Sbruno case DOD_DEVID_INTDFP: 647161184Sbruno voqh = &lcd_units; 648161184Sbruno break; 649126430Snjl default: 650126430Snjl voqh = &other_units; 651126430Snjl } 652126430Snjl STAILQ_REMOVE(voqh, vo, acpi_video_output, vo_unit.next); 653126430Snjl free(vo, M_ACPIVIDEO); 654126430Snjl} 655126430Snjl 656126430Snjlstatic int 657126430Snjlacpi_video_vo_check_level(struct acpi_video_output *vo, int level) 658126430Snjl{ 659126430Snjl int i; 660126430Snjl 661197648Sjhb ACPI_SERIAL_ASSERT(video_output); 662126430Snjl if (vo->vo_levels == NULL) 663126430Snjl return (ENODEV); 664126430Snjl for (i = 0; i < vo->vo_numlevels; i++) 665126430Snjl if (vo->vo_levels[i] == level) 666126430Snjl return (0); 667126430Snjl return (EINVAL); 668126430Snjl} 669126430Snjl 670203810Sjkimstatic void 671203810Sjkimacpi_video_vo_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 672203810Sjkim{ 673203810Sjkim struct acpi_video_output *vo; 674203810Sjkim int i, j, level, new_level; 675203810Sjkim 676203810Sjkim vo = context; 677203810Sjkim ACPI_SERIAL_BEGIN(video_output); 678203935Sjkim if (vo->handle != handle) 679203935Sjkim goto out; 680203810Sjkim 681203810Sjkim switch (notify) { 682203936Sjkim case VID_NOTIFY_CYCLE_BRN: 683203935Sjkim if (vo->vo_numlevels <= 3) 684203935Sjkim goto out; 685203935Sjkim /* FALLTHROUGH */ 686203810Sjkim case VID_NOTIFY_INC_BRN: 687203810Sjkim case VID_NOTIFY_DEC_BRN: 688203936Sjkim case VID_NOTIFY_ZERO_BRN: 689203810Sjkim if (vo->vo_levels == NULL) 690203935Sjkim goto out; 691203813Sjkim level = vo_get_brightness(handle); 692203810Sjkim if (level < 0) 693203935Sjkim goto out; 694203935Sjkim break; 695203935Sjkim default: 696203935Sjkim printf("unknown notify event 0x%x from %s\n", 697203935Sjkim notify, acpi_name(handle)); 698203935Sjkim goto out; 699203935Sjkim } 700203935Sjkim 701203935Sjkim new_level = level; 702203935Sjkim switch (notify) { 703203936Sjkim case VID_NOTIFY_CYCLE_BRN: 704203935Sjkim for (i = 2; i < vo->vo_numlevels; i++) 705203935Sjkim if (vo->vo_levels[i] == level) { 706203935Sjkim new_level = vo->vo_numlevels > i + 1 ? 707203935Sjkim vo->vo_levels[i + 1] : vo->vo_levels[2]; 708203935Sjkim break; 709203935Sjkim } 710203935Sjkim break; 711203935Sjkim case VID_NOTIFY_INC_BRN: 712203935Sjkim case VID_NOTIFY_DEC_BRN: 713203810Sjkim for (i = 0; i < vo->vo_numlevels; i++) { 714203810Sjkim j = vo->vo_levels[i]; 715203810Sjkim if (notify == VID_NOTIFY_INC_BRN) { 716203810Sjkim if (j > level && 717203810Sjkim (j < new_level || level == new_level)) 718203810Sjkim new_level = j; 719203810Sjkim } else { 720203810Sjkim if (j < level && 721203810Sjkim (j > new_level || level == new_level)) 722203810Sjkim new_level = j; 723203810Sjkim } 724203810Sjkim } 725203810Sjkim break; 726203936Sjkim case VID_NOTIFY_ZERO_BRN: 727203935Sjkim for (i = 0; i < vo->vo_numlevels; i++) 728203935Sjkim if (vo->vo_levels[i] == 0) { 729203935Sjkim new_level = 0; 730203935Sjkim break; 731203935Sjkim } 732203935Sjkim break; 733203810Sjkim } 734203935Sjkim if (new_level != level) { 735203935Sjkim vo_set_brightness(handle, new_level); 736203935Sjkim vo->vo_brightness = new_level; 737203935Sjkim } 738203935Sjkim 739203935Sjkimout: 740203810Sjkim ACPI_SERIAL_END(video_output); 741203810Sjkim} 742203810Sjkim 743126430Snjl/* ARGSUSED */ 744126430Snjlstatic int 745126430Snjlacpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS) 746126430Snjl{ 747126430Snjl struct acpi_video_output *vo; 748126430Snjl int state, err; 749126430Snjl 750126430Snjl vo = (struct acpi_video_output *)arg1; 751133625Snjl if (vo->handle == NULL) 752133625Snjl return (ENXIO); 753197648Sjhb ACPI_SERIAL_BEGIN(video_output); 754133625Snjl state = (vo_get_device_status(vo->handle) & DCS_ACTIVE) ? 1 : 0; 755126430Snjl err = sysctl_handle_int(oidp, &state, 0, req); 756126430Snjl if (err != 0 || req->newptr == NULL) 757126430Snjl goto out; 758126430Snjl vo_set_device_state(vo->handle, 759137438Snjl DSS_COMMIT | (state ? DSS_ACTIVE : DSS_INACTIVE)); 760126430Snjlout: 761197648Sjhb ACPI_SERIAL_END(video_output); 762126430Snjl return (err); 763126430Snjl} 764126430Snjl 765126430Snjl/* ARGSUSED */ 766126430Snjlstatic int 767126430Snjlacpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS) 768126430Snjl{ 769126430Snjl struct acpi_video_output *vo; 770126430Snjl int level, preset, err; 771126430Snjl 772126430Snjl vo = (struct acpi_video_output *)arg1; 773197648Sjhb ACPI_SERIAL_BEGIN(video_output); 774126430Snjl if (vo->handle == NULL) { 775126430Snjl err = ENXIO; 776126430Snjl goto out; 777126430Snjl } 778126430Snjl if (vo->vo_levels == NULL) { 779126430Snjl err = ENODEV; 780126430Snjl goto out; 781126430Snjl } 782126430Snjl 783132526Snjl preset = (power_profile_get_state() == POWER_PROFILE_ECONOMY) ? 784132526Snjl vo->vo_economy : vo->vo_fullpower; 785126430Snjl level = vo->vo_brightness; 786126430Snjl if (level == -1) 787126430Snjl level = preset; 788126430Snjl 789126430Snjl err = sysctl_handle_int(oidp, &level, 0, req); 790126430Snjl if (err != 0 || req->newptr == NULL) 791126430Snjl goto out; 792126430Snjl if (level < -1 || level > 100) { 793126430Snjl err = EINVAL; 794126430Snjl goto out; 795126430Snjl } 796126430Snjl 797126430Snjl if (level != -1 && (err = acpi_video_vo_check_level(vo, level))) 798126430Snjl goto out; 799126430Snjl vo->vo_brightness = level; 800133625Snjl vo_set_brightness(vo->handle, (level == -1) ? preset : level); 801133625Snjl 802126430Snjlout: 803197648Sjhb ACPI_SERIAL_END(video_output); 804126430Snjl return (err); 805126430Snjl} 806126430Snjl 807126430Snjlstatic int 808126430Snjlacpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS) 809126430Snjl{ 810126430Snjl struct acpi_video_output *vo; 811137438Snjl int i, level, *preset, err; 812126430Snjl 813126430Snjl vo = (struct acpi_video_output *)arg1; 814197648Sjhb ACPI_SERIAL_BEGIN(video_output); 815126430Snjl if (vo->handle == NULL) { 816126430Snjl err = ENXIO; 817126430Snjl goto out; 818126430Snjl } 819126430Snjl if (vo->vo_levels == NULL) { 820126430Snjl err = ENODEV; 821126430Snjl goto out; 822126430Snjl } 823132526Snjl preset = (arg2 == POWER_PROFILE_ECONOMY) ? 824132526Snjl &vo->vo_economy : &vo->vo_fullpower; 825126430Snjl level = *preset; 826126430Snjl err = sysctl_handle_int(oidp, &level, 0, req); 827126430Snjl if (err != 0 || req->newptr == NULL) 828126430Snjl goto out; 829126430Snjl if (level < -1 || level > 100) { 830126430Snjl err = EINVAL; 831126430Snjl goto out; 832126430Snjl } 833133625Snjl if (level == -1) { 834133625Snjl i = (arg2 == POWER_PROFILE_ECONOMY) ? 835133625Snjl BCL_ECONOMY : BCL_FULLPOWER; 836133625Snjl level = vo->vo_levels[i]; 837137438Snjl } else if ((err = acpi_video_vo_check_level(vo, level)) != 0) 838126430Snjl goto out; 839126430Snjl 840126430Snjl if (vo->vo_brightness == -1 && (power_profile_get_state() == arg2)) 841126430Snjl vo_set_brightness(vo->handle, level); 842126430Snjl *preset = level; 843133625Snjl 844126430Snjlout: 845197648Sjhb ACPI_SERIAL_END(video_output); 846126430Snjl return (err); 847126430Snjl} 848126430Snjl 849126430Snjl/* ARGSUSED */ 850126430Snjlstatic int 851126430Snjlacpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS) 852126430Snjl{ 853126430Snjl struct acpi_video_output *vo; 854126430Snjl int err; 855126430Snjl 856126430Snjl vo = (struct acpi_video_output *)arg1; 857197648Sjhb ACPI_SERIAL_BEGIN(video_output); 858126430Snjl if (vo->vo_levels == NULL) { 859126430Snjl err = ENODEV; 860126430Snjl goto out; 861126430Snjl } 862126430Snjl if (req->newptr != NULL) { 863126430Snjl err = EPERM; 864126430Snjl goto out; 865126430Snjl } 866126430Snjl err = sysctl_handle_opaque(oidp, vo->vo_levels, 867133625Snjl vo->vo_numlevels * sizeof(*vo->vo_levels), req); 868133625Snjl 869126430Snjlout: 870197648Sjhb ACPI_SERIAL_END(video_output); 871126430Snjl return (err); 872126430Snjl} 873126430Snjl 874126430Snjlstatic void 875126430Snjlvid_set_switch_policy(ACPI_HANDLE handle, UINT32 policy) 876126430Snjl{ 877126430Snjl ACPI_STATUS status; 878126430Snjl 879126560Snjl status = acpi_SetInteger(handle, "_DOS", policy); 880126430Snjl if (ACPI_FAILURE(status)) 881126430Snjl printf("can't evaluate %s._DOS - %s\n", 882126430Snjl acpi_name(handle), AcpiFormatException(status)); 883126430Snjl} 884126430Snjl 885126430Snjlstruct enum_callback_arg { 886126430Snjl void (*callback)(ACPI_HANDLE, UINT32, void *); 887126430Snjl void *context; 888126430Snjl ACPI_OBJECT *dod_pkg; 889132605Snjl int count; 890126430Snjl}; 891126430Snjl 892126430Snjlstatic ACPI_STATUS 893126430Snjlvid_enum_outputs_subr(ACPI_HANDLE handle, UINT32 level __unused, 894132605Snjl void *context, void **retp __unused) 895126430Snjl{ 896126430Snjl ACPI_STATUS status; 897132605Snjl UINT32 adr, val; 898126430Snjl struct enum_callback_arg *argset; 899126430Snjl size_t i; 900126430Snjl 901133625Snjl ACPI_SERIAL_ASSERT(video); 902126430Snjl argset = context; 903126560Snjl status = acpi_GetInteger(handle, "_ADR", &adr); 904132605Snjl if (ACPI_FAILURE(status)) 905132605Snjl return (AE_OK); 906132605Snjl 907132605Snjl for (i = 0; i < argset->dod_pkg->Package.Count; i++) { 908132605Snjl if (acpi_PkgInt32(argset->dod_pkg, i, &val) == 0 && 909241748Sjhb (val & DOD_DEVID_MASK_FULL) == 910241748Sjhb (adr & DOD_DEVID_MASK_FULL)) { 911132605Snjl argset->callback(handle, val, argset->context); 912132605Snjl argset->count++; 913126430Snjl } 914126430Snjl } 915126430Snjl 916126430Snjl return (AE_OK); 917126430Snjl} 918126430Snjl 919126430Snjlstatic int 920126430Snjlvid_enum_outputs(ACPI_HANDLE handle, 921126430Snjl void (*callback)(ACPI_HANDLE, UINT32, void *), void *context) 922126430Snjl{ 923126430Snjl ACPI_STATUS status; 924126430Snjl ACPI_BUFFER dod_buf; 925126430Snjl ACPI_OBJECT *res; 926126430Snjl struct enum_callback_arg argset; 927126430Snjl 928133625Snjl ACPI_SERIAL_ASSERT(video); 929126430Snjl dod_buf.Length = ACPI_ALLOCATE_BUFFER; 930126430Snjl dod_buf.Pointer = NULL; 931126430Snjl status = AcpiEvaluateObject(handle, "_DOD", NULL, &dod_buf); 932126430Snjl if (ACPI_FAILURE(status)) { 933126430Snjl if (status != AE_NOT_FOUND) 934126430Snjl printf("can't evaluate %s._DOD - %s\n", 935126430Snjl acpi_name(handle), AcpiFormatException(status)); 936132605Snjl argset.count = -1; 937126430Snjl goto out; 938126430Snjl } 939126430Snjl res = (ACPI_OBJECT *)dod_buf.Pointer; 940132605Snjl if (!ACPI_PKG_VALID(res, 1)) { 941126430Snjl printf("evaluation of %s._DOD makes no sense\n", 942126430Snjl acpi_name(handle)); 943132605Snjl argset.count = -1; 944126430Snjl goto out; 945126430Snjl } 946126430Snjl if (callback == NULL) { 947132605Snjl argset.count = res->Package.Count; 948126430Snjl goto out; 949126430Snjl } 950126430Snjl argset.callback = callback; 951126430Snjl argset.context = context; 952126430Snjl argset.dod_pkg = res; 953132605Snjl argset.count = 0; 954126430Snjl status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1, 955199337Sjkim vid_enum_outputs_subr, NULL, &argset, NULL); 956126430Snjl if (ACPI_FAILURE(status)) 957126430Snjl printf("failed walking down %s - %s\n", 958126430Snjl acpi_name(handle), AcpiFormatException(status)); 959126430Snjlout: 960126430Snjl if (dod_buf.Pointer != NULL) 961126430Snjl AcpiOsFree(dod_buf.Pointer); 962132605Snjl return (argset.count); 963126430Snjl} 964126430Snjl 965126430Snjlstatic int 966133625Snjlvo_get_brightness_levels(ACPI_HANDLE handle, int **levelp) 967126430Snjl{ 968126430Snjl ACPI_STATUS status; 969126430Snjl ACPI_BUFFER bcl_buf; 970132605Snjl ACPI_OBJECT *res; 971137438Snjl int num, i, n, *levels; 972126430Snjl 973126430Snjl bcl_buf.Length = ACPI_ALLOCATE_BUFFER; 974126430Snjl bcl_buf.Pointer = NULL; 975126430Snjl status = AcpiEvaluateObject(handle, "_BCL", NULL, &bcl_buf); 976126430Snjl if (ACPI_FAILURE(status)) { 977126430Snjl if (status != AE_NOT_FOUND) 978126430Snjl printf("can't evaluate %s._BCL - %s\n", 979126430Snjl acpi_name(handle), AcpiFormatException(status)); 980126430Snjl goto out; 981126430Snjl } 982126430Snjl res = (ACPI_OBJECT *)bcl_buf.Pointer; 983132605Snjl if (!ACPI_PKG_VALID(res, 2)) { 984126430Snjl printf("evaluation of %s._BCL makes no sense\n", 985126430Snjl acpi_name(handle)); 986126430Snjl goto out; 987126430Snjl } 988126430Snjl num = res->Package.Count; 989209065Sjkim if (num < 2 || levelp == NULL) 990126430Snjl goto out; 991132605Snjl levels = AcpiOsAllocate(num * sizeof(*levels)); 992209065Sjkim if (levels == NULL) 993126430Snjl goto out; 994132605Snjl for (i = 0, n = 0; i < num; i++) 995132605Snjl if (acpi_PkgInt32(res, i, &levels[n]) == 0) 996132605Snjl n++; 997126430Snjl if (n < 2) { 998126430Snjl AcpiOsFree(levels); 999209065Sjkim goto out; 1000126430Snjl } 1001209065Sjkim *levelp = levels; 1002209065Sjkim return (n); 1003209065Sjkim 1004126430Snjlout: 1005126430Snjl if (bcl_buf.Pointer != NULL) 1006126430Snjl AcpiOsFree(bcl_buf.Pointer); 1007209065Sjkim return (0); 1008126430Snjl} 1009126430Snjl 1010203810Sjkimstatic int 1011203810Sjkimvo_get_brightness(ACPI_HANDLE handle) 1012203810Sjkim{ 1013203810Sjkim UINT32 level; 1014203810Sjkim ACPI_STATUS status; 1015203810Sjkim 1016203810Sjkim ACPI_SERIAL_ASSERT(video_output); 1017203810Sjkim status = acpi_GetInteger(handle, "_BQC", &level); 1018203810Sjkim if (ACPI_FAILURE(status)) { 1019203810Sjkim printf("can't evaluate %s._BQC - %s\n", acpi_name(handle), 1020203810Sjkim AcpiFormatException(status)); 1021203810Sjkim return (-1); 1022203810Sjkim } 1023203810Sjkim if (level > 100) 1024203810Sjkim return (-1); 1025203810Sjkim 1026203810Sjkim return (level); 1027203810Sjkim} 1028203810Sjkim 1029126430Snjlstatic void 1030126430Snjlvo_set_brightness(ACPI_HANDLE handle, int level) 1031126430Snjl{ 1032126430Snjl ACPI_STATUS status; 1033126430Snjl 1034197648Sjhb ACPI_SERIAL_ASSERT(video_output); 1035126560Snjl status = acpi_SetInteger(handle, "_BCM", level); 1036126430Snjl if (ACPI_FAILURE(status)) 1037126430Snjl printf("can't evaluate %s._BCM - %s\n", 1038126430Snjl acpi_name(handle), AcpiFormatException(status)); 1039126430Snjl} 1040126430Snjl 1041126430Snjlstatic UINT32 1042126430Snjlvo_get_device_status(ACPI_HANDLE handle) 1043126430Snjl{ 1044137438Snjl UINT32 dcs; 1045126430Snjl ACPI_STATUS status; 1046126430Snjl 1047197648Sjhb ACPI_SERIAL_ASSERT(video_output); 1048137438Snjl dcs = 0; 1049126560Snjl status = acpi_GetInteger(handle, "_DCS", &dcs); 1050126430Snjl if (ACPI_FAILURE(status)) 1051126430Snjl printf("can't evaluate %s._DCS - %s\n", 1052126430Snjl acpi_name(handle), AcpiFormatException(status)); 1053126430Snjl 1054126430Snjl return (dcs); 1055126430Snjl} 1056126430Snjl 1057126430Snjlstatic UINT32 1058133625Snjlvo_get_graphics_state(ACPI_HANDLE handle) 1059126430Snjl{ 1060137438Snjl UINT32 dgs; 1061126430Snjl ACPI_STATUS status; 1062126430Snjl 1063137438Snjl dgs = 0; 1064126560Snjl status = acpi_GetInteger(handle, "_DGS", &dgs); 1065126430Snjl if (ACPI_FAILURE(status)) 1066126430Snjl printf("can't evaluate %s._DGS - %s\n", 1067126430Snjl acpi_name(handle), AcpiFormatException(status)); 1068126430Snjl 1069126430Snjl return (dgs); 1070126430Snjl} 1071126430Snjl 1072126430Snjlstatic void 1073126430Snjlvo_set_device_state(ACPI_HANDLE handle, UINT32 state) 1074126430Snjl{ 1075126430Snjl ACPI_STATUS status; 1076126430Snjl 1077197648Sjhb ACPI_SERIAL_ASSERT(video_output); 1078126560Snjl status = acpi_SetInteger(handle, "_DSS", state); 1079126430Snjl if (ACPI_FAILURE(status)) 1080126430Snjl printf("can't evaluate %s._DSS - %s\n", 1081126430Snjl acpi_name(handle), AcpiFormatException(status)); 1082126430Snjl} 1083