acpi_video.c revision 203936
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: head/sys/dev/acpica/acpi_video.c 203936 2010-02-15 20:46:01Z jkim $"); 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); 78126430Snjlstatic int acpi_video_shutdown(device_t); 79126430Snjlstatic void acpi_video_notify_handler(ACPI_HANDLE, UINT32, void *); 80126430Snjlstatic void acpi_video_power_profile(void *); 81126430Snjlstatic void acpi_video_bind_outputs(struct acpi_video_softc *); 82132526Snjlstatic struct acpi_video_output *acpi_video_vo_init(UINT32); 83126430Snjlstatic void acpi_video_vo_bind(struct acpi_video_output *, ACPI_HANDLE); 84126430Snjlstatic void acpi_video_vo_destroy(struct acpi_video_output *); 85126430Snjlstatic int acpi_video_vo_check_level(struct acpi_video_output *, int); 86203810Sjkimstatic void acpi_video_vo_notify_handler(ACPI_HANDLE, UINT32, void *); 87126430Snjlstatic int acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS); 88126430Snjlstatic int acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS); 89126430Snjlstatic int acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS); 90126430Snjlstatic int acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS); 91126430Snjl 92126430Snjl/* operations */ 93126430Snjlstatic void vid_set_switch_policy(ACPI_HANDLE, UINT32); 94126430Snjlstatic int vid_enum_outputs(ACPI_HANDLE, 95132526Snjl void(*)(ACPI_HANDLE, UINT32, void *), void *); 96133625Snjlstatic int vo_get_brightness_levels(ACPI_HANDLE, int **); 97203810Sjkimstatic int vo_get_brightness(ACPI_HANDLE); 98126430Snjlstatic void vo_set_brightness(ACPI_HANDLE, int); 99126430Snjlstatic UINT32 vo_get_device_status(ACPI_HANDLE); 100133625Snjlstatic UINT32 vo_get_graphics_state(ACPI_HANDLE); 101126430Snjlstatic void vo_set_device_state(ACPI_HANDLE, UINT32); 102126430Snjl 103126430Snjl/* events */ 104203936Sjkim#define VID_NOTIFY_SWITCHED 0x80 105203936Sjkim#define VID_NOTIFY_REPROBE 0x81 106203936Sjkim#define VID_NOTIFY_CYCLE_BRN 0x85 107203810Sjkim#define VID_NOTIFY_INC_BRN 0x86 108203810Sjkim#define VID_NOTIFY_DEC_BRN 0x87 109203936Sjkim#define VID_NOTIFY_ZERO_BRN 0x88 110126430Snjl 111126430Snjl/* _DOS (Enable/Disable Output Switching) argument bits */ 112203936Sjkim#define DOS_SWITCH_MASK 3 113203936Sjkim#define DOS_SWITCH_BY_OSPM 0 114203936Sjkim#define DOS_SWITCH_BY_BIOS 1 115203936Sjkim#define DOS_SWITCH_LOCKED 2 116203936Sjkim#define DOS_BRIGHTNESS_BY_OSPM (1 << 2) 117126430Snjl 118126430Snjl/* _DOD and subdev's _ADR */ 119203936Sjkim#define DOD_DEVID_MASK 0x0f00 120203936Sjkim#define DOD_DEVID_MASK_FULL 0xffff 121203936Sjkim#define DOD_DEVID_MASK_DISPIDX 0x000f 122203936Sjkim#define DOD_DEVID_MASK_DISPPORT 0x00f0 123203936Sjkim#define DOD_DEVID_MONITOR 0x0100 124203936Sjkim#define DOD_DEVID_LCD 0x0110 125203936Sjkim#define DOD_DEVID_TV 0x0200 126203936Sjkim#define DOD_DEVID_EXT 0x0300 127203936Sjkim#define DOD_DEVID_INTDFP 0x0400 128203936Sjkim#define DOD_BIOS (1 << 16) 129203936Sjkim#define DOD_NONVGA (1 << 17) 130203936Sjkim#define DOD_HEAD_ID_SHIFT 18 131203936Sjkim#define DOD_HEAD_ID_BITS 3 132203936Sjkim#define DOD_HEAD_ID_MASK \ 133132526Snjl (((1 << DOD_HEAD_ID_BITS) - 1) << DOD_HEAD_ID_SHIFT) 134203936Sjkim#define DOD_DEVID_SCHEME_STD (1 << 31) 135126430Snjl 136126430Snjl/* _BCL related constants */ 137203936Sjkim#define BCL_FULLPOWER 0 138203936Sjkim#define BCL_ECONOMY 1 139126430Snjl 140126430Snjl/* _DCS (Device Currrent Status) value bits and masks. */ 141203936Sjkim#define DCS_EXISTS (1 << 0) 142203936Sjkim#define DCS_ACTIVE (1 << 1) 143203936Sjkim#define DCS_READY (1 << 2) 144203936Sjkim#define DCS_FUNCTIONAL (1 << 3) 145203936Sjkim#define DCS_ATTACHED (1 << 4) 146126430Snjl 147126430Snjl/* _DSS (Device Set Status) argument bits and masks. */ 148203936Sjkim#define DSS_INACTIVE 0 149203936Sjkim#define DSS_ACTIVE (1 << 0) 150203936Sjkim#define DSS_SETNEXT (1 << 30) 151203936Sjkim#define DSS_COMMIT (1 << 31) 152126430Snjl 153126430Snjlstatic device_method_t acpi_video_methods[] = { 154153578Sjhb DEVMETHOD(device_identify, acpi_video_identify), 155126430Snjl DEVMETHOD(device_probe, acpi_video_probe), 156126430Snjl DEVMETHOD(device_attach, acpi_video_attach), 157126430Snjl DEVMETHOD(device_detach, acpi_video_detach), 158126430Snjl DEVMETHOD(device_shutdown, acpi_video_shutdown), 159126430Snjl { 0, 0 } 160126430Snjl}; 161126430Snjl 162126430Snjlstatic driver_t acpi_video_driver = { 163126430Snjl "acpi_video", 164126430Snjl acpi_video_methods, 165126430Snjl sizeof(struct acpi_video_softc), 166126430Snjl}; 167126430Snjl 168126430Snjlstatic devclass_t acpi_video_devclass; 169126430Snjl 170153578SjhbDRIVER_MODULE(acpi_video, vgapci, acpi_video_driver, acpi_video_devclass, 171126430Snjl acpi_video_modevent, NULL); 172128036SnjlMODULE_DEPEND(acpi_video, acpi, 1, 1, 1); 173126430Snjl 174132526Snjlstatic struct sysctl_ctx_list acpi_video_sysctl_ctx; 175132526Snjlstatic struct sysctl_oid *acpi_video_sysctl_tree; 176161184Sbrunostatic struct acpi_video_output_queue crt_units, tv_units, 177161184Sbruno ext_units, lcd_units, other_units; 178126430Snjl 179197648Sjhb/* 180197648Sjhb * The 'video' lock protects the hierarchy of video output devices 181197648Sjhb * (the video "bus"). The 'video_output' lock protects per-output 182197648Sjhb * data is equivalent to a softc lock for each video output. 183197648Sjhb */ 184133625SnjlACPI_SERIAL_DECL(video, "ACPI video"); 185197648SjhbACPI_SERIAL_DECL(video_output, "ACPI video output"); 186126430SnjlMALLOC_DEFINE(M_ACPIVIDEO, "acpivideo", "ACPI video extension"); 187126430Snjl 188126430Snjlstatic int 189126430Snjlacpi_video_modevent(struct module *mod __unused, int evt, void *cookie __unused) 190126430Snjl{ 191137438Snjl int err; 192126430Snjl 193137438Snjl err = 0; 194126430Snjl switch (evt) { 195126430Snjl case MOD_LOAD: 196126430Snjl sysctl_ctx_init(&acpi_video_sysctl_ctx); 197126430Snjl STAILQ_INIT(&crt_units); 198126430Snjl STAILQ_INIT(&tv_units); 199161184Sbruno STAILQ_INIT(&ext_units); 200161184Sbruno STAILQ_INIT(&lcd_units); 201126430Snjl STAILQ_INIT(&other_units); 202126430Snjl break; 203126430Snjl case MOD_UNLOAD: 204132256Snjl sysctl_ctx_free(&acpi_video_sysctl_ctx); 205126430Snjl acpi_video_sysctl_tree = NULL; 206126430Snjl break; 207126430Snjl default: 208126430Snjl err = EINVAL; 209126430Snjl } 210126430Snjl 211126430Snjl return (err); 212126430Snjl} 213126430Snjl 214153578Sjhbstatic void 215153578Sjhbacpi_video_identify(driver_t *driver, device_t parent) 216153578Sjhb{ 217153578Sjhb 218153578Sjhb if (device_find_child(parent, "acpi_video", -1) == NULL) 219153578Sjhb device_add_child(parent, "acpi_video", -1); 220153578Sjhb} 221153578Sjhb 222126430Snjlstatic int 223126430Snjlacpi_video_probe(device_t dev) 224126430Snjl{ 225132526Snjl ACPI_HANDLE devh, h; 226132526Snjl ACPI_OBJECT_TYPE t_dos; 227126430Snjl 228132526Snjl devh = acpi_get_handle(dev); 229132526Snjl if (acpi_disabled("video") || 230132526Snjl ACPI_FAILURE(AcpiGetHandle(devh, "_DOD", &h)) || 231132526Snjl ACPI_FAILURE(AcpiGetHandle(devh, "_DOS", &h)) || 232132526Snjl ACPI_FAILURE(AcpiGetType(h, &t_dos)) || 233132526Snjl t_dos != ACPI_TYPE_METHOD) 234132526Snjl return (ENXIO); 235126430Snjl 236132526Snjl device_set_desc(dev, "ACPI video extension"); 237132526Snjl return (0); 238126430Snjl} 239126430Snjl 240126430Snjlstatic int 241126430Snjlacpi_video_attach(device_t dev) 242126430Snjl{ 243126430Snjl struct acpi_softc *acpi_sc; 244126430Snjl struct acpi_video_softc *sc; 245126430Snjl 246126430Snjl sc = device_get_softc(dev); 247126430Snjl 248132256Snjl acpi_sc = devclass_get_softc(devclass_find("acpi"), 0); 249132605Snjl if (acpi_sc == NULL) 250132605Snjl return (ENXIO); 251197648Sjhb ACPI_SERIAL_BEGIN(video); 252132605Snjl if (acpi_video_sysctl_tree == NULL) { 253126430Snjl acpi_video_sysctl_tree = SYSCTL_ADD_NODE(&acpi_video_sysctl_ctx, 254126430Snjl SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 255126430Snjl OID_AUTO, "video", CTLFLAG_RD, 0, 256126430Snjl "video extension control"); 257126430Snjl } 258197648Sjhb ACPI_SERIAL_END(video); 259126430Snjl 260126430Snjl sc->device = dev; 261126430Snjl sc->handle = acpi_get_handle(dev); 262126430Snjl STAILQ_INIT(&sc->vid_outputs); 263126430Snjl 264126430Snjl AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 265126430Snjl acpi_video_notify_handler, sc); 266126430Snjl sc->vid_pwr_evh = EVENTHANDLER_REGISTER(power_profile_change, 267126430Snjl acpi_video_power_profile, sc, 0); 268126430Snjl 269133625Snjl ACPI_SERIAL_BEGIN(video); 270126430Snjl acpi_video_bind_outputs(sc); 271133625Snjl ACPI_SERIAL_END(video); 272126430Snjl 273137438Snjl /* 274137438Snjl * Notify the BIOS that we want to switch both active outputs and 275137438Snjl * brightness levels. 276137438Snjl */ 277137438Snjl vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_OSPM | 278203936Sjkim DOS_BRIGHTNESS_BY_OSPM); 279137438Snjl 280126430Snjl acpi_video_power_profile(sc); 281126430Snjl 282126430Snjl return (0); 283126430Snjl} 284126430Snjl 285126430Snjlstatic int 286126430Snjlacpi_video_detach(device_t dev) 287126430Snjl{ 288126430Snjl struct acpi_video_softc *sc; 289126430Snjl struct acpi_video_output *vo, *vn; 290126430Snjl 291126430Snjl sc = device_get_softc(dev); 292126430Snjl 293126430Snjl vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); 294126430Snjl EVENTHANDLER_DEREGISTER(power_profile_change, sc->vid_pwr_evh); 295126430Snjl AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 296126430Snjl acpi_video_notify_handler); 297126430Snjl 298133625Snjl ACPI_SERIAL_BEGIN(video); 299197438Sjhb STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vn) { 300126430Snjl acpi_video_vo_destroy(vo); 301126430Snjl } 302133625Snjl ACPI_SERIAL_END(video); 303126430Snjl 304126430Snjl return (0); 305126430Snjl} 306126430Snjl 307126430Snjlstatic int 308126430Snjlacpi_video_shutdown(device_t dev) 309126430Snjl{ 310126430Snjl struct acpi_video_softc *sc; 311126430Snjl 312126430Snjl sc = device_get_softc(dev); 313126430Snjl vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); 314126430Snjl 315126430Snjl return (0); 316126430Snjl} 317126430Snjl 318126430Snjlstatic void 319137438Snjlacpi_video_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 320126430Snjl{ 321126430Snjl struct acpi_video_softc *sc; 322132605Snjl struct acpi_video_output *vo, *vo_tmp; 323137438Snjl ACPI_HANDLE lasthand; 324137438Snjl UINT32 dcs, dss, dss_p; 325126430Snjl 326137438Snjl sc = (struct acpi_video_softc *)context; 327126430Snjl 328126430Snjl switch (notify) { 329126430Snjl case VID_NOTIFY_SWITCHED: 330137438Snjl dss_p = 0; 331137438Snjl lasthand = NULL; 332133625Snjl ACPI_SERIAL_BEGIN(video); 333197648Sjhb ACPI_SERIAL_BEGIN(video_output); 334126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 335133625Snjl dss = vo_get_graphics_state(vo->handle); 336126430Snjl dcs = vo_get_device_status(vo->handle); 337126430Snjl if (!(dcs & DCS_READY)) 338126430Snjl dss = DSS_INACTIVE; 339126430Snjl if (((dcs & DCS_ACTIVE) && dss == DSS_INACTIVE) || 340126430Snjl (!(dcs & DCS_ACTIVE) && dss == DSS_ACTIVE)) { 341126430Snjl if (lasthand != NULL) 342126430Snjl vo_set_device_state(lasthand, dss_p); 343126430Snjl dss_p = dss; 344126430Snjl lasthand = vo->handle; 345126430Snjl } 346126430Snjl } 347126430Snjl if (lasthand != NULL) 348126430Snjl vo_set_device_state(lasthand, dss_p|DSS_COMMIT); 349197648Sjhb ACPI_SERIAL_END(video_output); 350133625Snjl ACPI_SERIAL_END(video); 351126430Snjl break; 352126430Snjl case VID_NOTIFY_REPROBE: 353133625Snjl ACPI_SERIAL_BEGIN(video); 354126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) 355126430Snjl vo->handle = NULL; 356126430Snjl acpi_video_bind_outputs(sc); 357132605Snjl STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vo_tmp) { 358126430Snjl if (vo->handle == NULL) { 359126430Snjl STAILQ_REMOVE(&sc->vid_outputs, vo, 360137438Snjl acpi_video_output, vo_next); 361126430Snjl acpi_video_vo_destroy(vo); 362126430Snjl } 363126430Snjl } 364133625Snjl ACPI_SERIAL_END(video); 365126430Snjl break; 366126430Snjl default: 367137438Snjl device_printf(sc->device, "unknown notify event 0x%x\n", 368137438Snjl notify); 369126430Snjl } 370126430Snjl} 371126430Snjl 372126430Snjlstatic void 373126430Snjlacpi_video_power_profile(void *context) 374126430Snjl{ 375126430Snjl int state; 376126430Snjl struct acpi_video_softc *sc; 377126430Snjl struct acpi_video_output *vo; 378126430Snjl 379126430Snjl sc = context; 380126430Snjl state = power_profile_get_state(); 381126430Snjl if (state != POWER_PROFILE_PERFORMANCE && 382126430Snjl state != POWER_PROFILE_ECONOMY) 383126430Snjl return; 384126430Snjl 385133625Snjl ACPI_SERIAL_BEGIN(video); 386197648Sjhb ACPI_SERIAL_BEGIN(video_output); 387126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 388126430Snjl if (vo->vo_levels != NULL && vo->vo_brightness == -1) 389126430Snjl vo_set_brightness(vo->handle, 390137438Snjl state == POWER_PROFILE_ECONOMY ? 391137438Snjl vo->vo_economy : vo->vo_fullpower); 392126430Snjl } 393197648Sjhb ACPI_SERIAL_END(video_output); 394133625Snjl ACPI_SERIAL_END(video); 395126430Snjl} 396126430Snjl 397126430Snjlstatic void 398126430Snjlacpi_video_bind_outputs_subr(ACPI_HANDLE handle, UINT32 adr, void *context) 399126430Snjl{ 400126430Snjl struct acpi_video_softc *sc; 401126430Snjl struct acpi_video_output *vo; 402126430Snjl 403133625Snjl ACPI_SERIAL_ASSERT(video); 404126430Snjl sc = context; 405126430Snjl 406126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 407126430Snjl if (vo->adr == adr) { 408126430Snjl acpi_video_vo_bind(vo, handle); 409126430Snjl return; 410126430Snjl } 411126430Snjl } 412126430Snjl vo = acpi_video_vo_init(adr); 413126430Snjl if (vo != NULL) { 414126430Snjl acpi_video_vo_bind(vo, handle); 415126430Snjl STAILQ_INSERT_TAIL(&sc->vid_outputs, vo, vo_next); 416126430Snjl } 417126430Snjl} 418126430Snjl 419126430Snjlstatic void 420126430Snjlacpi_video_bind_outputs(struct acpi_video_softc *sc) 421126430Snjl{ 422126430Snjl 423133625Snjl ACPI_SERIAL_ASSERT(video); 424126430Snjl vid_enum_outputs(sc->handle, acpi_video_bind_outputs_subr, sc); 425126430Snjl} 426126430Snjl 427126430Snjlstatic struct acpi_video_output * 428126430Snjlacpi_video_vo_init(UINT32 adr) 429126430Snjl{ 430126430Snjl struct acpi_video_output *vn, *vo, *vp; 431126430Snjl int n, x; 432161184Sbruno int display_index; 433161184Sbruno int display_port; 434137438Snjl char name[8], env[32]; 435126430Snjl const char *type, *desc; 436126430Snjl struct acpi_video_output_queue *voqh; 437126430Snjl 438133625Snjl ACPI_SERIAL_ASSERT(video); 439161184Sbruno display_index = adr & DOD_DEVID_MASK_DISPIDX; 440161184Sbruno display_port = (adr & DOD_DEVID_MASK_DISPPORT) >> 4; 441161184Sbruno 442126430Snjl switch (adr & DOD_DEVID_MASK) { 443126430Snjl case DOD_DEVID_MONITOR: 444161184Sbruno if ((adr & DOD_DEVID_MASK_FULL) == DOD_DEVID_LCD) { 445161184Sbruno /* DOD_DEVID_LCD is a common, backward compatible ID */ 446161184Sbruno desc = "Internal/Integrated Digital Flat Panel"; 447161184Sbruno type = "lcd"; 448161184Sbruno voqh = &lcd_units; 449161184Sbruno } else { 450161184Sbruno desc = "VGA CRT or VESA Compatible Analog Monitor"; 451161184Sbruno type = "crt"; 452161184Sbruno voqh = &crt_units; 453161184Sbruno } 454126430Snjl break; 455126430Snjl case DOD_DEVID_TV: 456161184Sbruno desc = "TV/HDTV or Analog-Video Monitor"; 457126430Snjl type = "tv"; 458126430Snjl voqh = &tv_units; 459126430Snjl break; 460161184Sbruno case DOD_DEVID_EXT: 461161184Sbruno desc = "External Digital Monitor"; 462161184Sbruno type = "ext"; 463161184Sbruno voqh = &ext_units; 464161184Sbruno break; 465161184Sbruno case DOD_DEVID_INTDFP: 466161184Sbruno desc = "Internal/Integrated Digital Flat Panel"; 467161184Sbruno type = "lcd"; 468161184Sbruno voqh = &lcd_units; 469161184Sbruno break; 470126430Snjl default: 471126430Snjl desc = "unknown output"; 472126430Snjl type = "out"; 473126430Snjl voqh = &other_units; 474126430Snjl } 475126430Snjl 476126430Snjl n = 0; 477126430Snjl vn = vp = NULL; 478126430Snjl STAILQ_FOREACH(vn, voqh, vo_unit.next) { 479126430Snjl if (vn->vo_unit.num != n) 480126430Snjl break; 481126430Snjl vp = vn; 482126430Snjl n++; 483126430Snjl } 484126430Snjl 485132526Snjl snprintf(name, sizeof(name), "%s%d", type, n); 486126430Snjl 487126430Snjl vo = malloc(sizeof(*vo), M_ACPIVIDEO, M_NOWAIT); 488126430Snjl if (vo != NULL) { 489126430Snjl vo->handle = NULL; 490126430Snjl vo->adr = adr; 491126430Snjl vo->vo_unit.num = n; 492126430Snjl vo->vo_brightness = -1; 493126430Snjl vo->vo_fullpower = -1; /* TODO: override with tunables */ 494126430Snjl vo->vo_economy = -1; 495126430Snjl vo->vo_numlevels = 0; 496126430Snjl vo->vo_levels = NULL; 497137438Snjl snprintf(env, sizeof(env), "hw.acpi.video.%s.fullpower", name); 498126430Snjl if (getenv_int(env, &x)) 499126430Snjl vo->vo_fullpower = x; 500137438Snjl snprintf(env, sizeof(env), "hw.acpi.video.%s.economy", name); 501126430Snjl if (getenv_int(env, &x)) 502126430Snjl vo->vo_economy = x; 503126430Snjl 504126430Snjl sysctl_ctx_init(&vo->vo_sysctl_ctx); 505126430Snjl if (vp != NULL) 506126430Snjl STAILQ_INSERT_AFTER(voqh, vp, vo, vo_unit.next); 507126430Snjl else 508126430Snjl STAILQ_INSERT_TAIL(voqh, vo, vo_unit.next); 509126430Snjl if (acpi_video_sysctl_tree != NULL) 510126430Snjl vo->vo_sysctl_tree = 511126430Snjl SYSCTL_ADD_NODE(&vo->vo_sysctl_ctx, 512132526Snjl SYSCTL_CHILDREN(acpi_video_sysctl_tree), 513132526Snjl OID_AUTO, name, CTLFLAG_RD, 0, desc); 514126430Snjl if (vo->vo_sysctl_tree != NULL) { 515126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 516132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 517132526Snjl OID_AUTO, "active", 518132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 0, 519132526Snjl acpi_video_vo_active_sysctl, "I", 520132526Snjl "current activity of this device"); 521126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 522132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 523132526Snjl OID_AUTO, "brightness", 524132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 0, 525132526Snjl acpi_video_vo_bright_sysctl, "I", 526132526Snjl "current brightness level"); 527126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 528132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 529132526Snjl OID_AUTO, "fullpower", 530132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 531132526Snjl POWER_PROFILE_PERFORMANCE, 532132526Snjl acpi_video_vo_presets_sysctl, "I", 533132526Snjl "preset level for full power mode"); 534126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 535132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 536132526Snjl OID_AUTO, "economy", 537132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 538132526Snjl POWER_PROFILE_ECONOMY, 539132526Snjl acpi_video_vo_presets_sysctl, "I", 540132526Snjl "preset level for economy mode"); 541126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 542132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 543132526Snjl OID_AUTO, "levels", 544132526Snjl CTLTYPE_OPAQUE|CTLFLAG_RD, vo, 0, 545132526Snjl acpi_video_vo_levels_sysctl, "I", 546132526Snjl "supported brightness levels"); 547126430Snjl } else 548126430Snjl printf("%s: sysctl node creation failed\n", type); 549126430Snjl } else 550126430Snjl printf("%s: softc allocation failed\n", type); 551126430Snjl 552126430Snjl if (bootverbose) { 553161184Sbruno printf("found %s(%x)", desc, adr & DOD_DEVID_MASK_FULL); 554161184Sbruno printf(", idx#%x", adr & DOD_DEVID_MASK_DISPIDX); 555161184Sbruno printf(", port#%x", (adr & DOD_DEVID_MASK_DISPPORT) >> 4); 556126430Snjl if (adr & DOD_BIOS) 557126430Snjl printf(", detectable by BIOS"); 558126430Snjl if (adr & DOD_NONVGA) 559161184Sbruno printf(" (Non-VGA output device whose power " 560161184Sbruno "is related to the VGA device)"); 561126430Snjl printf(", head #%d\n", 562161184Sbruno (adr & DOD_HEAD_ID_MASK) >> DOD_HEAD_ID_SHIFT); 563126430Snjl } 564132526Snjl return (vo); 565126430Snjl} 566126430Snjl 567126430Snjlstatic void 568126430Snjlacpi_video_vo_bind(struct acpi_video_output *vo, ACPI_HANDLE handle) 569126430Snjl{ 570126430Snjl 571197648Sjhb ACPI_SERIAL_BEGIN(video_output); 572126430Snjl if (vo->vo_levels != NULL) 573126430Snjl AcpiOsFree(vo->vo_levels); 574126430Snjl vo->handle = handle; 575133625Snjl vo->vo_numlevels = vo_get_brightness_levels(handle, &vo->vo_levels); 576126430Snjl if (vo->vo_numlevels >= 2) { 577126430Snjl if (vo->vo_fullpower == -1 578126430Snjl || acpi_video_vo_check_level(vo, vo->vo_fullpower) != 0) 579126430Snjl /* XXX - can't deal with rebinding... */ 580126430Snjl vo->vo_fullpower = vo->vo_levels[BCL_FULLPOWER]; 581126430Snjl if (vo->vo_economy == -1 582126430Snjl || acpi_video_vo_check_level(vo, vo->vo_economy) != 0) 583126430Snjl /* XXX - see above. */ 584126430Snjl vo->vo_economy = vo->vo_levels[BCL_ECONOMY]; 585126430Snjl } 586203810Sjkim if (vo->vo_levels != NULL) 587203810Sjkim AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, 588203810Sjkim acpi_video_vo_notify_handler, vo); 589197648Sjhb ACPI_SERIAL_END(video_output); 590126430Snjl} 591126430Snjl 592126430Snjlstatic void 593126430Snjlacpi_video_vo_destroy(struct acpi_video_output *vo) 594126430Snjl{ 595126430Snjl struct acpi_video_output_queue *voqh; 596126430Snjl 597133625Snjl ACPI_SERIAL_ASSERT(video); 598126430Snjl if (vo->vo_sysctl_tree != NULL) { 599126430Snjl vo->vo_sysctl_tree = NULL; 600126430Snjl sysctl_ctx_free(&vo->vo_sysctl_ctx); 601126430Snjl } 602203810Sjkim if (vo->vo_levels != NULL) { 603203810Sjkim AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY, 604203810Sjkim acpi_video_vo_notify_handler); 605126430Snjl AcpiOsFree(vo->vo_levels); 606203810Sjkim } 607126430Snjl 608126430Snjl switch (vo->adr & DOD_DEVID_MASK) { 609126430Snjl case DOD_DEVID_MONITOR: 610126430Snjl voqh = &crt_units; 611126430Snjl break; 612126430Snjl case DOD_DEVID_TV: 613126430Snjl voqh = &tv_units; 614126430Snjl break; 615161184Sbruno case DOD_DEVID_EXT: 616161184Sbruno voqh = &ext_units; 617161184Sbruno break; 618161184Sbruno case DOD_DEVID_INTDFP: 619161184Sbruno voqh = &lcd_units; 620161184Sbruno break; 621126430Snjl default: 622126430Snjl voqh = &other_units; 623126430Snjl } 624126430Snjl STAILQ_REMOVE(voqh, vo, acpi_video_output, vo_unit.next); 625126430Snjl free(vo, M_ACPIVIDEO); 626126430Snjl} 627126430Snjl 628126430Snjlstatic int 629126430Snjlacpi_video_vo_check_level(struct acpi_video_output *vo, int level) 630126430Snjl{ 631126430Snjl int i; 632126430Snjl 633197648Sjhb ACPI_SERIAL_ASSERT(video_output); 634126430Snjl if (vo->vo_levels == NULL) 635126430Snjl return (ENODEV); 636126430Snjl for (i = 0; i < vo->vo_numlevels; i++) 637126430Snjl if (vo->vo_levels[i] == level) 638126430Snjl return (0); 639126430Snjl return (EINVAL); 640126430Snjl} 641126430Snjl 642203810Sjkimstatic void 643203810Sjkimacpi_video_vo_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 644203810Sjkim{ 645203810Sjkim struct acpi_video_output *vo; 646203810Sjkim int i, j, level, new_level; 647203810Sjkim 648203810Sjkim vo = context; 649203810Sjkim ACPI_SERIAL_BEGIN(video_output); 650203935Sjkim if (vo->handle != handle) 651203935Sjkim goto out; 652203810Sjkim 653203810Sjkim switch (notify) { 654203936Sjkim case VID_NOTIFY_CYCLE_BRN: 655203935Sjkim if (vo->vo_numlevels <= 3) 656203935Sjkim goto out; 657203935Sjkim /* FALLTHROUGH */ 658203810Sjkim case VID_NOTIFY_INC_BRN: 659203810Sjkim case VID_NOTIFY_DEC_BRN: 660203936Sjkim case VID_NOTIFY_ZERO_BRN: 661203810Sjkim if (vo->vo_levels == NULL) 662203935Sjkim goto out; 663203813Sjkim level = vo_get_brightness(handle); 664203810Sjkim if (level < 0) 665203935Sjkim goto out; 666203935Sjkim break; 667203935Sjkim default: 668203935Sjkim printf("unknown notify event 0x%x from %s\n", 669203935Sjkim notify, acpi_name(handle)); 670203935Sjkim goto out; 671203935Sjkim } 672203935Sjkim 673203935Sjkim new_level = level; 674203935Sjkim switch (notify) { 675203936Sjkim case VID_NOTIFY_CYCLE_BRN: 676203935Sjkim for (i = 2; i < vo->vo_numlevels; i++) 677203935Sjkim if (vo->vo_levels[i] == level) { 678203935Sjkim new_level = vo->vo_numlevels > i + 1 ? 679203935Sjkim vo->vo_levels[i + 1] : vo->vo_levels[2]; 680203935Sjkim break; 681203935Sjkim } 682203935Sjkim break; 683203935Sjkim case VID_NOTIFY_INC_BRN: 684203935Sjkim case VID_NOTIFY_DEC_BRN: 685203810Sjkim for (i = 0; i < vo->vo_numlevels; i++) { 686203810Sjkim j = vo->vo_levels[i]; 687203810Sjkim if (notify == VID_NOTIFY_INC_BRN) { 688203810Sjkim if (j > level && 689203810Sjkim (j < new_level || level == new_level)) 690203810Sjkim new_level = j; 691203810Sjkim } else { 692203810Sjkim if (j < level && 693203810Sjkim (j > new_level || level == new_level)) 694203810Sjkim new_level = j; 695203810Sjkim } 696203810Sjkim } 697203810Sjkim break; 698203936Sjkim case VID_NOTIFY_ZERO_BRN: 699203935Sjkim for (i = 0; i < vo->vo_numlevels; i++) 700203935Sjkim if (vo->vo_levels[i] == 0) { 701203935Sjkim new_level = 0; 702203935Sjkim break; 703203935Sjkim } 704203935Sjkim break; 705203810Sjkim } 706203935Sjkim if (new_level != level) { 707203935Sjkim vo_set_brightness(handle, new_level); 708203935Sjkim vo->vo_brightness = new_level; 709203935Sjkim } 710203935Sjkim 711203935Sjkimout: 712203810Sjkim ACPI_SERIAL_END(video_output); 713203810Sjkim} 714203810Sjkim 715126430Snjl/* ARGSUSED */ 716126430Snjlstatic int 717126430Snjlacpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS) 718126430Snjl{ 719126430Snjl struct acpi_video_output *vo; 720126430Snjl int state, err; 721126430Snjl 722126430Snjl vo = (struct acpi_video_output *)arg1; 723133625Snjl if (vo->handle == NULL) 724133625Snjl return (ENXIO); 725197648Sjhb ACPI_SERIAL_BEGIN(video_output); 726133625Snjl state = (vo_get_device_status(vo->handle) & DCS_ACTIVE) ? 1 : 0; 727126430Snjl err = sysctl_handle_int(oidp, &state, 0, req); 728126430Snjl if (err != 0 || req->newptr == NULL) 729126430Snjl goto out; 730126430Snjl vo_set_device_state(vo->handle, 731137438Snjl DSS_COMMIT | (state ? DSS_ACTIVE : DSS_INACTIVE)); 732126430Snjlout: 733197648Sjhb ACPI_SERIAL_END(video_output); 734126430Snjl return (err); 735126430Snjl} 736126430Snjl 737126430Snjl/* ARGSUSED */ 738126430Snjlstatic int 739126430Snjlacpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS) 740126430Snjl{ 741126430Snjl struct acpi_video_output *vo; 742126430Snjl int level, preset, err; 743126430Snjl 744126430Snjl vo = (struct acpi_video_output *)arg1; 745197648Sjhb ACPI_SERIAL_BEGIN(video_output); 746126430Snjl if (vo->handle == NULL) { 747126430Snjl err = ENXIO; 748126430Snjl goto out; 749126430Snjl } 750126430Snjl if (vo->vo_levels == NULL) { 751126430Snjl err = ENODEV; 752126430Snjl goto out; 753126430Snjl } 754126430Snjl 755132526Snjl preset = (power_profile_get_state() == POWER_PROFILE_ECONOMY) ? 756132526Snjl vo->vo_economy : vo->vo_fullpower; 757126430Snjl level = vo->vo_brightness; 758126430Snjl if (level == -1) 759126430Snjl level = preset; 760126430Snjl 761126430Snjl err = sysctl_handle_int(oidp, &level, 0, req); 762126430Snjl if (err != 0 || req->newptr == NULL) 763126430Snjl goto out; 764126430Snjl if (level < -1 || level > 100) { 765126430Snjl err = EINVAL; 766126430Snjl goto out; 767126430Snjl } 768126430Snjl 769126430Snjl if (level != -1 && (err = acpi_video_vo_check_level(vo, level))) 770126430Snjl goto out; 771126430Snjl vo->vo_brightness = level; 772133625Snjl vo_set_brightness(vo->handle, (level == -1) ? preset : level); 773133625Snjl 774126430Snjlout: 775197648Sjhb ACPI_SERIAL_END(video_output); 776126430Snjl return (err); 777126430Snjl} 778126430Snjl 779126430Snjlstatic int 780126430Snjlacpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS) 781126430Snjl{ 782126430Snjl struct acpi_video_output *vo; 783137438Snjl int i, level, *preset, err; 784126430Snjl 785137438Snjl err = 0; 786126430Snjl vo = (struct acpi_video_output *)arg1; 787197648Sjhb ACPI_SERIAL_BEGIN(video_output); 788126430Snjl if (vo->handle == NULL) { 789126430Snjl err = ENXIO; 790126430Snjl goto out; 791126430Snjl } 792126430Snjl if (vo->vo_levels == NULL) { 793126430Snjl err = ENODEV; 794126430Snjl goto out; 795126430Snjl } 796132526Snjl preset = (arg2 == POWER_PROFILE_ECONOMY) ? 797132526Snjl &vo->vo_economy : &vo->vo_fullpower; 798126430Snjl level = *preset; 799126430Snjl err = sysctl_handle_int(oidp, &level, 0, req); 800126430Snjl if (err != 0 || req->newptr == NULL) 801126430Snjl goto out; 802126430Snjl if (level < -1 || level > 100) { 803126430Snjl err = EINVAL; 804126430Snjl goto out; 805126430Snjl } 806133625Snjl if (level == -1) { 807133625Snjl i = (arg2 == POWER_PROFILE_ECONOMY) ? 808133625Snjl BCL_ECONOMY : BCL_FULLPOWER; 809133625Snjl level = vo->vo_levels[i]; 810137438Snjl } else if ((err = acpi_video_vo_check_level(vo, level)) != 0) 811126430Snjl goto out; 812126430Snjl 813126430Snjl if (vo->vo_brightness == -1 && (power_profile_get_state() == arg2)) 814126430Snjl vo_set_brightness(vo->handle, level); 815126430Snjl *preset = level; 816133625Snjl 817126430Snjlout: 818197648Sjhb ACPI_SERIAL_END(video_output); 819126430Snjl return (err); 820126430Snjl} 821126430Snjl 822126430Snjl/* ARGSUSED */ 823126430Snjlstatic int 824126430Snjlacpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS) 825126430Snjl{ 826126430Snjl struct acpi_video_output *vo; 827126430Snjl int err; 828126430Snjl 829126430Snjl vo = (struct acpi_video_output *)arg1; 830197648Sjhb ACPI_SERIAL_BEGIN(video_output); 831126430Snjl if (vo->vo_levels == NULL) { 832126430Snjl err = ENODEV; 833126430Snjl goto out; 834126430Snjl } 835126430Snjl if (req->newptr != NULL) { 836126430Snjl err = EPERM; 837126430Snjl goto out; 838126430Snjl } 839126430Snjl err = sysctl_handle_opaque(oidp, vo->vo_levels, 840133625Snjl vo->vo_numlevels * sizeof(*vo->vo_levels), req); 841133625Snjl 842126430Snjlout: 843197648Sjhb ACPI_SERIAL_END(video_output); 844126430Snjl return (err); 845126430Snjl} 846126430Snjl 847126430Snjlstatic void 848126430Snjlvid_set_switch_policy(ACPI_HANDLE handle, UINT32 policy) 849126430Snjl{ 850126430Snjl ACPI_STATUS status; 851126430Snjl 852126560Snjl status = acpi_SetInteger(handle, "_DOS", policy); 853126430Snjl if (ACPI_FAILURE(status)) 854126430Snjl printf("can't evaluate %s._DOS - %s\n", 855126430Snjl acpi_name(handle), AcpiFormatException(status)); 856126430Snjl} 857126430Snjl 858126430Snjlstruct enum_callback_arg { 859126430Snjl void (*callback)(ACPI_HANDLE, UINT32, void *); 860126430Snjl void *context; 861126430Snjl ACPI_OBJECT *dod_pkg; 862132605Snjl int count; 863126430Snjl}; 864126430Snjl 865126430Snjlstatic ACPI_STATUS 866126430Snjlvid_enum_outputs_subr(ACPI_HANDLE handle, UINT32 level __unused, 867132605Snjl void *context, void **retp __unused) 868126430Snjl{ 869126430Snjl ACPI_STATUS status; 870132605Snjl UINT32 adr, val; 871126430Snjl struct enum_callback_arg *argset; 872126430Snjl size_t i; 873126430Snjl 874133625Snjl ACPI_SERIAL_ASSERT(video); 875126430Snjl argset = context; 876126560Snjl status = acpi_GetInteger(handle, "_ADR", &adr); 877132605Snjl if (ACPI_FAILURE(status)) 878132605Snjl return (AE_OK); 879132605Snjl 880132605Snjl for (i = 0; i < argset->dod_pkg->Package.Count; i++) { 881132605Snjl if (acpi_PkgInt32(argset->dod_pkg, i, &val) == 0 && 882161184Sbruno (val & DOD_DEVID_MASK_FULL) == adr) { 883132605Snjl argset->callback(handle, val, argset->context); 884132605Snjl argset->count++; 885126430Snjl } 886126430Snjl } 887126430Snjl 888126430Snjl return (AE_OK); 889126430Snjl} 890126430Snjl 891126430Snjlstatic int 892126430Snjlvid_enum_outputs(ACPI_HANDLE handle, 893126430Snjl void (*callback)(ACPI_HANDLE, UINT32, void *), void *context) 894126430Snjl{ 895126430Snjl ACPI_STATUS status; 896126430Snjl ACPI_BUFFER dod_buf; 897126430Snjl ACPI_OBJECT *res; 898126430Snjl struct enum_callback_arg argset; 899126430Snjl 900133625Snjl ACPI_SERIAL_ASSERT(video); 901126430Snjl dod_buf.Length = ACPI_ALLOCATE_BUFFER; 902126430Snjl dod_buf.Pointer = NULL; 903126430Snjl status = AcpiEvaluateObject(handle, "_DOD", NULL, &dod_buf); 904126430Snjl if (ACPI_FAILURE(status)) { 905126430Snjl if (status != AE_NOT_FOUND) 906126430Snjl printf("can't evaluate %s._DOD - %s\n", 907126430Snjl acpi_name(handle), AcpiFormatException(status)); 908132605Snjl argset.count = -1; 909126430Snjl goto out; 910126430Snjl } 911126430Snjl res = (ACPI_OBJECT *)dod_buf.Pointer; 912132605Snjl if (!ACPI_PKG_VALID(res, 1)) { 913126430Snjl printf("evaluation of %s._DOD makes no sense\n", 914126430Snjl acpi_name(handle)); 915132605Snjl argset.count = -1; 916126430Snjl goto out; 917126430Snjl } 918126430Snjl if (callback == NULL) { 919132605Snjl argset.count = res->Package.Count; 920126430Snjl goto out; 921126430Snjl } 922126430Snjl argset.callback = callback; 923126430Snjl argset.context = context; 924126430Snjl argset.dod_pkg = res; 925132605Snjl argset.count = 0; 926126430Snjl status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1, 927199337Sjkim vid_enum_outputs_subr, NULL, &argset, NULL); 928126430Snjl if (ACPI_FAILURE(status)) 929126430Snjl printf("failed walking down %s - %s\n", 930126430Snjl acpi_name(handle), AcpiFormatException(status)); 931126430Snjlout: 932126430Snjl if (dod_buf.Pointer != NULL) 933126430Snjl AcpiOsFree(dod_buf.Pointer); 934132605Snjl return (argset.count); 935126430Snjl} 936126430Snjl 937126430Snjlstatic int 938133625Snjlvo_get_brightness_levels(ACPI_HANDLE handle, int **levelp) 939126430Snjl{ 940126430Snjl ACPI_STATUS status; 941126430Snjl ACPI_BUFFER bcl_buf; 942132605Snjl ACPI_OBJECT *res; 943137438Snjl int num, i, n, *levels; 944126430Snjl 945137438Snjl num = 0; 946126430Snjl bcl_buf.Length = ACPI_ALLOCATE_BUFFER; 947126430Snjl bcl_buf.Pointer = NULL; 948126430Snjl status = AcpiEvaluateObject(handle, "_BCL", NULL, &bcl_buf); 949126430Snjl if (ACPI_FAILURE(status)) { 950126430Snjl if (status != AE_NOT_FOUND) 951126430Snjl printf("can't evaluate %s._BCL - %s\n", 952126430Snjl acpi_name(handle), AcpiFormatException(status)); 953126430Snjl num = -1; 954126430Snjl goto out; 955126430Snjl } 956126430Snjl res = (ACPI_OBJECT *)bcl_buf.Pointer; 957132605Snjl if (!ACPI_PKG_VALID(res, 2)) { 958126430Snjl printf("evaluation of %s._BCL makes no sense\n", 959126430Snjl acpi_name(handle)); 960126430Snjl num = -1; 961126430Snjl goto out; 962126430Snjl } 963126430Snjl num = res->Package.Count; 964126430Snjl if (levelp == NULL) 965126430Snjl goto out; 966132605Snjl levels = AcpiOsAllocate(num * sizeof(*levels)); 967126430Snjl if (levels == NULL) { 968126430Snjl num = -1; 969126430Snjl goto out; 970126430Snjl } 971132605Snjl for (i = 0, n = 0; i < num; i++) 972132605Snjl if (acpi_PkgInt32(res, i, &levels[n]) == 0) 973132605Snjl n++; 974126430Snjl if (n < 2) { 975126430Snjl num = -1; 976126430Snjl AcpiOsFree(levels); 977126430Snjl } else { 978126430Snjl num = n; 979126430Snjl *levelp = levels; 980126430Snjl } 981126430Snjlout: 982126430Snjl if (bcl_buf.Pointer != NULL) 983126430Snjl AcpiOsFree(bcl_buf.Pointer); 984126430Snjl 985126430Snjl return (num); 986126430Snjl} 987126430Snjl 988203810Sjkimstatic int 989203810Sjkimvo_get_brightness(ACPI_HANDLE handle) 990203810Sjkim{ 991203810Sjkim UINT32 level; 992203810Sjkim ACPI_STATUS status; 993203810Sjkim 994203810Sjkim ACPI_SERIAL_ASSERT(video_output); 995203810Sjkim status = acpi_GetInteger(handle, "_BQC", &level); 996203810Sjkim if (ACPI_FAILURE(status)) { 997203810Sjkim printf("can't evaluate %s._BQC - %s\n", acpi_name(handle), 998203810Sjkim AcpiFormatException(status)); 999203810Sjkim return (-1); 1000203810Sjkim } 1001203810Sjkim if (level > 100) 1002203810Sjkim return (-1); 1003203810Sjkim 1004203810Sjkim return (level); 1005203810Sjkim} 1006203810Sjkim 1007126430Snjlstatic void 1008126430Snjlvo_set_brightness(ACPI_HANDLE handle, int level) 1009126430Snjl{ 1010126430Snjl ACPI_STATUS status; 1011126430Snjl 1012197648Sjhb ACPI_SERIAL_ASSERT(video_output); 1013126560Snjl status = acpi_SetInteger(handle, "_BCM", level); 1014126430Snjl if (ACPI_FAILURE(status)) 1015126430Snjl printf("can't evaluate %s._BCM - %s\n", 1016126430Snjl acpi_name(handle), AcpiFormatException(status)); 1017126430Snjl} 1018126430Snjl 1019126430Snjlstatic UINT32 1020126430Snjlvo_get_device_status(ACPI_HANDLE handle) 1021126430Snjl{ 1022137438Snjl UINT32 dcs; 1023126430Snjl ACPI_STATUS status; 1024126430Snjl 1025197648Sjhb ACPI_SERIAL_ASSERT(video_output); 1026137438Snjl dcs = 0; 1027126560Snjl status = acpi_GetInteger(handle, "_DCS", &dcs); 1028126430Snjl if (ACPI_FAILURE(status)) 1029126430Snjl printf("can't evaluate %s._DCS - %s\n", 1030126430Snjl acpi_name(handle), AcpiFormatException(status)); 1031126430Snjl 1032126430Snjl return (dcs); 1033126430Snjl} 1034126430Snjl 1035126430Snjlstatic UINT32 1036133625Snjlvo_get_graphics_state(ACPI_HANDLE handle) 1037126430Snjl{ 1038137438Snjl UINT32 dgs; 1039126430Snjl ACPI_STATUS status; 1040126430Snjl 1041137438Snjl dgs = 0; 1042126560Snjl status = acpi_GetInteger(handle, "_DGS", &dgs); 1043126430Snjl if (ACPI_FAILURE(status)) 1044126430Snjl printf("can't evaluate %s._DGS - %s\n", 1045126430Snjl acpi_name(handle), AcpiFormatException(status)); 1046126430Snjl 1047126430Snjl return (dgs); 1048126430Snjl} 1049126430Snjl 1050126430Snjlstatic void 1051126430Snjlvo_set_device_state(ACPI_HANDLE handle, UINT32 state) 1052126430Snjl{ 1053126430Snjl ACPI_STATUS status; 1054126430Snjl 1055197648Sjhb ACPI_SERIAL_ASSERT(video_output); 1056126560Snjl status = acpi_SetInteger(handle, "_DSS", state); 1057126430Snjl if (ACPI_FAILURE(status)) 1058126430Snjl printf("can't evaluate %s._DSS - %s\n", 1059126430Snjl acpi_name(handle), AcpiFormatException(status)); 1060126430Snjl} 1061