acpi_video.c revision 227293
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 227293 2011-11-07 06:44:47Z ed $"); 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"); 186227293Sedstatic MALLOC_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; 432137438Snjl char name[8], env[32]; 433126430Snjl const char *type, *desc; 434126430Snjl struct acpi_video_output_queue *voqh; 435126430Snjl 436133625Snjl ACPI_SERIAL_ASSERT(video); 437161184Sbruno 438126430Snjl switch (adr & DOD_DEVID_MASK) { 439126430Snjl case DOD_DEVID_MONITOR: 440161184Sbruno if ((adr & DOD_DEVID_MASK_FULL) == DOD_DEVID_LCD) { 441161184Sbruno /* DOD_DEVID_LCD is a common, backward compatible ID */ 442161184Sbruno desc = "Internal/Integrated Digital Flat Panel"; 443161184Sbruno type = "lcd"; 444161184Sbruno voqh = &lcd_units; 445161184Sbruno } else { 446161184Sbruno desc = "VGA CRT or VESA Compatible Analog Monitor"; 447161184Sbruno type = "crt"; 448161184Sbruno voqh = &crt_units; 449161184Sbruno } 450126430Snjl break; 451126430Snjl case DOD_DEVID_TV: 452161184Sbruno desc = "TV/HDTV or Analog-Video Monitor"; 453126430Snjl type = "tv"; 454126430Snjl voqh = &tv_units; 455126430Snjl break; 456161184Sbruno case DOD_DEVID_EXT: 457161184Sbruno desc = "External Digital Monitor"; 458161184Sbruno type = "ext"; 459161184Sbruno voqh = &ext_units; 460161184Sbruno break; 461161184Sbruno case DOD_DEVID_INTDFP: 462161184Sbruno desc = "Internal/Integrated Digital Flat Panel"; 463161184Sbruno type = "lcd"; 464161184Sbruno voqh = &lcd_units; 465161184Sbruno break; 466126430Snjl default: 467126430Snjl desc = "unknown output"; 468126430Snjl type = "out"; 469126430Snjl voqh = &other_units; 470126430Snjl } 471126430Snjl 472126430Snjl n = 0; 473209064Sjkim vp = NULL; 474126430Snjl STAILQ_FOREACH(vn, voqh, vo_unit.next) { 475126430Snjl if (vn->vo_unit.num != n) 476126430Snjl break; 477126430Snjl vp = vn; 478126430Snjl n++; 479126430Snjl } 480126430Snjl 481132526Snjl snprintf(name, sizeof(name), "%s%d", type, n); 482126430Snjl 483126430Snjl vo = malloc(sizeof(*vo), M_ACPIVIDEO, M_NOWAIT); 484126430Snjl if (vo != NULL) { 485126430Snjl vo->handle = NULL; 486126430Snjl vo->adr = adr; 487126430Snjl vo->vo_unit.num = n; 488126430Snjl vo->vo_brightness = -1; 489126430Snjl vo->vo_fullpower = -1; /* TODO: override with tunables */ 490126430Snjl vo->vo_economy = -1; 491126430Snjl vo->vo_numlevels = 0; 492126430Snjl vo->vo_levels = NULL; 493137438Snjl snprintf(env, sizeof(env), "hw.acpi.video.%s.fullpower", name); 494126430Snjl if (getenv_int(env, &x)) 495126430Snjl vo->vo_fullpower = x; 496137438Snjl snprintf(env, sizeof(env), "hw.acpi.video.%s.economy", name); 497126430Snjl if (getenv_int(env, &x)) 498126430Snjl vo->vo_economy = x; 499126430Snjl 500126430Snjl sysctl_ctx_init(&vo->vo_sysctl_ctx); 501126430Snjl if (vp != NULL) 502126430Snjl STAILQ_INSERT_AFTER(voqh, vp, vo, vo_unit.next); 503126430Snjl else 504126430Snjl STAILQ_INSERT_TAIL(voqh, vo, vo_unit.next); 505126430Snjl if (acpi_video_sysctl_tree != NULL) 506126430Snjl vo->vo_sysctl_tree = 507126430Snjl SYSCTL_ADD_NODE(&vo->vo_sysctl_ctx, 508132526Snjl SYSCTL_CHILDREN(acpi_video_sysctl_tree), 509132526Snjl OID_AUTO, name, CTLFLAG_RD, 0, desc); 510126430Snjl if (vo->vo_sysctl_tree != NULL) { 511126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 512132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 513132526Snjl OID_AUTO, "active", 514132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 0, 515132526Snjl acpi_video_vo_active_sysctl, "I", 516132526Snjl "current activity of this device"); 517126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 518132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 519132526Snjl OID_AUTO, "brightness", 520132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 0, 521132526Snjl acpi_video_vo_bright_sysctl, "I", 522132526Snjl "current brightness level"); 523126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 524132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 525132526Snjl OID_AUTO, "fullpower", 526132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 527132526Snjl POWER_PROFILE_PERFORMANCE, 528132526Snjl acpi_video_vo_presets_sysctl, "I", 529132526Snjl "preset level for full power mode"); 530126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 531132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 532132526Snjl OID_AUTO, "economy", 533132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 534132526Snjl POWER_PROFILE_ECONOMY, 535132526Snjl acpi_video_vo_presets_sysctl, "I", 536132526Snjl "preset level for economy mode"); 537126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 538132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 539132526Snjl OID_AUTO, "levels", 540217566Smdf CTLTYPE_INT | CTLFLAG_RD, vo, 0, 541132526Snjl acpi_video_vo_levels_sysctl, "I", 542132526Snjl "supported brightness levels"); 543126430Snjl } else 544126430Snjl printf("%s: sysctl node creation failed\n", type); 545126430Snjl } else 546126430Snjl printf("%s: softc allocation failed\n", type); 547126430Snjl 548126430Snjl if (bootverbose) { 549161184Sbruno printf("found %s(%x)", desc, adr & DOD_DEVID_MASK_FULL); 550161184Sbruno printf(", idx#%x", adr & DOD_DEVID_MASK_DISPIDX); 551161184Sbruno printf(", port#%x", (adr & DOD_DEVID_MASK_DISPPORT) >> 4); 552126430Snjl if (adr & DOD_BIOS) 553126430Snjl printf(", detectable by BIOS"); 554126430Snjl if (adr & DOD_NONVGA) 555161184Sbruno printf(" (Non-VGA output device whose power " 556161184Sbruno "is related to the VGA device)"); 557126430Snjl printf(", head #%d\n", 558161184Sbruno (adr & DOD_HEAD_ID_MASK) >> DOD_HEAD_ID_SHIFT); 559126430Snjl } 560132526Snjl return (vo); 561126430Snjl} 562126430Snjl 563126430Snjlstatic void 564126430Snjlacpi_video_vo_bind(struct acpi_video_output *vo, ACPI_HANDLE handle) 565126430Snjl{ 566126430Snjl 567197648Sjhb ACPI_SERIAL_BEGIN(video_output); 568126430Snjl if (vo->vo_levels != NULL) 569126430Snjl AcpiOsFree(vo->vo_levels); 570126430Snjl vo->handle = handle; 571133625Snjl vo->vo_numlevels = vo_get_brightness_levels(handle, &vo->vo_levels); 572126430Snjl if (vo->vo_numlevels >= 2) { 573126430Snjl if (vo->vo_fullpower == -1 574126430Snjl || acpi_video_vo_check_level(vo, vo->vo_fullpower) != 0) 575126430Snjl /* XXX - can't deal with rebinding... */ 576126430Snjl vo->vo_fullpower = vo->vo_levels[BCL_FULLPOWER]; 577126430Snjl if (vo->vo_economy == -1 578126430Snjl || acpi_video_vo_check_level(vo, vo->vo_economy) != 0) 579126430Snjl /* XXX - see above. */ 580126430Snjl vo->vo_economy = vo->vo_levels[BCL_ECONOMY]; 581126430Snjl } 582203810Sjkim if (vo->vo_levels != NULL) 583204965Sjkim AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, 584204965Sjkim acpi_video_vo_notify_handler, vo); 585197648Sjhb ACPI_SERIAL_END(video_output); 586126430Snjl} 587126430Snjl 588126430Snjlstatic void 589126430Snjlacpi_video_vo_destroy(struct acpi_video_output *vo) 590126430Snjl{ 591126430Snjl struct acpi_video_output_queue *voqh; 592126430Snjl 593133625Snjl ACPI_SERIAL_ASSERT(video); 594126430Snjl if (vo->vo_sysctl_tree != NULL) { 595126430Snjl vo->vo_sysctl_tree = NULL; 596126430Snjl sysctl_ctx_free(&vo->vo_sysctl_ctx); 597126430Snjl } 598203810Sjkim if (vo->vo_levels != NULL) { 599203810Sjkim AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY, 600203810Sjkim acpi_video_vo_notify_handler); 601126430Snjl AcpiOsFree(vo->vo_levels); 602203810Sjkim } 603126430Snjl 604126430Snjl switch (vo->adr & DOD_DEVID_MASK) { 605126430Snjl case DOD_DEVID_MONITOR: 606126430Snjl voqh = &crt_units; 607126430Snjl break; 608126430Snjl case DOD_DEVID_TV: 609126430Snjl voqh = &tv_units; 610126430Snjl break; 611161184Sbruno case DOD_DEVID_EXT: 612161184Sbruno voqh = &ext_units; 613161184Sbruno break; 614161184Sbruno case DOD_DEVID_INTDFP: 615161184Sbruno voqh = &lcd_units; 616161184Sbruno break; 617126430Snjl default: 618126430Snjl voqh = &other_units; 619126430Snjl } 620126430Snjl STAILQ_REMOVE(voqh, vo, acpi_video_output, vo_unit.next); 621126430Snjl free(vo, M_ACPIVIDEO); 622126430Snjl} 623126430Snjl 624126430Snjlstatic int 625126430Snjlacpi_video_vo_check_level(struct acpi_video_output *vo, int level) 626126430Snjl{ 627126430Snjl int i; 628126430Snjl 629197648Sjhb ACPI_SERIAL_ASSERT(video_output); 630126430Snjl if (vo->vo_levels == NULL) 631126430Snjl return (ENODEV); 632126430Snjl for (i = 0; i < vo->vo_numlevels; i++) 633126430Snjl if (vo->vo_levels[i] == level) 634126430Snjl return (0); 635126430Snjl return (EINVAL); 636126430Snjl} 637126430Snjl 638203810Sjkimstatic void 639203810Sjkimacpi_video_vo_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 640203810Sjkim{ 641203810Sjkim struct acpi_video_output *vo; 642203810Sjkim int i, j, level, new_level; 643203810Sjkim 644203810Sjkim vo = context; 645203810Sjkim ACPI_SERIAL_BEGIN(video_output); 646203935Sjkim if (vo->handle != handle) 647203935Sjkim goto out; 648203810Sjkim 649203810Sjkim switch (notify) { 650203936Sjkim case VID_NOTIFY_CYCLE_BRN: 651203935Sjkim if (vo->vo_numlevels <= 3) 652203935Sjkim goto out; 653203935Sjkim /* FALLTHROUGH */ 654203810Sjkim case VID_NOTIFY_INC_BRN: 655203810Sjkim case VID_NOTIFY_DEC_BRN: 656203936Sjkim case VID_NOTIFY_ZERO_BRN: 657203810Sjkim if (vo->vo_levels == NULL) 658203935Sjkim goto out; 659203813Sjkim level = vo_get_brightness(handle); 660203810Sjkim if (level < 0) 661203935Sjkim goto out; 662203935Sjkim break; 663203935Sjkim default: 664203935Sjkim printf("unknown notify event 0x%x from %s\n", 665203935Sjkim notify, acpi_name(handle)); 666203935Sjkim goto out; 667203935Sjkim } 668203935Sjkim 669203935Sjkim new_level = level; 670203935Sjkim switch (notify) { 671203936Sjkim case VID_NOTIFY_CYCLE_BRN: 672203935Sjkim for (i = 2; i < vo->vo_numlevels; i++) 673203935Sjkim if (vo->vo_levels[i] == level) { 674203935Sjkim new_level = vo->vo_numlevels > i + 1 ? 675203935Sjkim vo->vo_levels[i + 1] : vo->vo_levels[2]; 676203935Sjkim break; 677203935Sjkim } 678203935Sjkim break; 679203935Sjkim case VID_NOTIFY_INC_BRN: 680203935Sjkim case VID_NOTIFY_DEC_BRN: 681203810Sjkim for (i = 0; i < vo->vo_numlevels; i++) { 682203810Sjkim j = vo->vo_levels[i]; 683203810Sjkim if (notify == VID_NOTIFY_INC_BRN) { 684203810Sjkim if (j > level && 685203810Sjkim (j < new_level || level == new_level)) 686203810Sjkim new_level = j; 687203810Sjkim } else { 688203810Sjkim if (j < level && 689203810Sjkim (j > new_level || level == new_level)) 690203810Sjkim new_level = j; 691203810Sjkim } 692203810Sjkim } 693203810Sjkim break; 694203936Sjkim case VID_NOTIFY_ZERO_BRN: 695203935Sjkim for (i = 0; i < vo->vo_numlevels; i++) 696203935Sjkim if (vo->vo_levels[i] == 0) { 697203935Sjkim new_level = 0; 698203935Sjkim break; 699203935Sjkim } 700203935Sjkim break; 701203810Sjkim } 702203935Sjkim if (new_level != level) { 703203935Sjkim vo_set_brightness(handle, new_level); 704203935Sjkim vo->vo_brightness = new_level; 705203935Sjkim } 706203935Sjkim 707203935Sjkimout: 708203810Sjkim ACPI_SERIAL_END(video_output); 709203810Sjkim} 710203810Sjkim 711126430Snjl/* ARGSUSED */ 712126430Snjlstatic int 713126430Snjlacpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS) 714126430Snjl{ 715126430Snjl struct acpi_video_output *vo; 716126430Snjl int state, err; 717126430Snjl 718126430Snjl vo = (struct acpi_video_output *)arg1; 719133625Snjl if (vo->handle == NULL) 720133625Snjl return (ENXIO); 721197648Sjhb ACPI_SERIAL_BEGIN(video_output); 722133625Snjl state = (vo_get_device_status(vo->handle) & DCS_ACTIVE) ? 1 : 0; 723126430Snjl err = sysctl_handle_int(oidp, &state, 0, req); 724126430Snjl if (err != 0 || req->newptr == NULL) 725126430Snjl goto out; 726126430Snjl vo_set_device_state(vo->handle, 727137438Snjl DSS_COMMIT | (state ? DSS_ACTIVE : DSS_INACTIVE)); 728126430Snjlout: 729197648Sjhb ACPI_SERIAL_END(video_output); 730126430Snjl return (err); 731126430Snjl} 732126430Snjl 733126430Snjl/* ARGSUSED */ 734126430Snjlstatic int 735126430Snjlacpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS) 736126430Snjl{ 737126430Snjl struct acpi_video_output *vo; 738126430Snjl int level, preset, err; 739126430Snjl 740126430Snjl vo = (struct acpi_video_output *)arg1; 741197648Sjhb ACPI_SERIAL_BEGIN(video_output); 742126430Snjl if (vo->handle == NULL) { 743126430Snjl err = ENXIO; 744126430Snjl goto out; 745126430Snjl } 746126430Snjl if (vo->vo_levels == NULL) { 747126430Snjl err = ENODEV; 748126430Snjl goto out; 749126430Snjl } 750126430Snjl 751132526Snjl preset = (power_profile_get_state() == POWER_PROFILE_ECONOMY) ? 752132526Snjl vo->vo_economy : vo->vo_fullpower; 753126430Snjl level = vo->vo_brightness; 754126430Snjl if (level == -1) 755126430Snjl level = preset; 756126430Snjl 757126430Snjl err = sysctl_handle_int(oidp, &level, 0, req); 758126430Snjl if (err != 0 || req->newptr == NULL) 759126430Snjl goto out; 760126430Snjl if (level < -1 || level > 100) { 761126430Snjl err = EINVAL; 762126430Snjl goto out; 763126430Snjl } 764126430Snjl 765126430Snjl if (level != -1 && (err = acpi_video_vo_check_level(vo, level))) 766126430Snjl goto out; 767126430Snjl vo->vo_brightness = level; 768133625Snjl vo_set_brightness(vo->handle, (level == -1) ? preset : level); 769133625Snjl 770126430Snjlout: 771197648Sjhb ACPI_SERIAL_END(video_output); 772126430Snjl return (err); 773126430Snjl} 774126430Snjl 775126430Snjlstatic int 776126430Snjlacpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS) 777126430Snjl{ 778126430Snjl struct acpi_video_output *vo; 779137438Snjl int i, level, *preset, err; 780126430Snjl 781126430Snjl vo = (struct acpi_video_output *)arg1; 782197648Sjhb ACPI_SERIAL_BEGIN(video_output); 783126430Snjl if (vo->handle == NULL) { 784126430Snjl err = ENXIO; 785126430Snjl goto out; 786126430Snjl } 787126430Snjl if (vo->vo_levels == NULL) { 788126430Snjl err = ENODEV; 789126430Snjl goto out; 790126430Snjl } 791132526Snjl preset = (arg2 == POWER_PROFILE_ECONOMY) ? 792132526Snjl &vo->vo_economy : &vo->vo_fullpower; 793126430Snjl level = *preset; 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 } 801133625Snjl if (level == -1) { 802133625Snjl i = (arg2 == POWER_PROFILE_ECONOMY) ? 803133625Snjl BCL_ECONOMY : BCL_FULLPOWER; 804133625Snjl level = vo->vo_levels[i]; 805137438Snjl } else if ((err = acpi_video_vo_check_level(vo, level)) != 0) 806126430Snjl goto out; 807126430Snjl 808126430Snjl if (vo->vo_brightness == -1 && (power_profile_get_state() == arg2)) 809126430Snjl vo_set_brightness(vo->handle, level); 810126430Snjl *preset = level; 811133625Snjl 812126430Snjlout: 813197648Sjhb ACPI_SERIAL_END(video_output); 814126430Snjl return (err); 815126430Snjl} 816126430Snjl 817126430Snjl/* ARGSUSED */ 818126430Snjlstatic int 819126430Snjlacpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS) 820126430Snjl{ 821126430Snjl struct acpi_video_output *vo; 822126430Snjl int err; 823126430Snjl 824126430Snjl vo = (struct acpi_video_output *)arg1; 825197648Sjhb ACPI_SERIAL_BEGIN(video_output); 826126430Snjl if (vo->vo_levels == NULL) { 827126430Snjl err = ENODEV; 828126430Snjl goto out; 829126430Snjl } 830126430Snjl if (req->newptr != NULL) { 831126430Snjl err = EPERM; 832126430Snjl goto out; 833126430Snjl } 834126430Snjl err = sysctl_handle_opaque(oidp, vo->vo_levels, 835133625Snjl vo->vo_numlevels * sizeof(*vo->vo_levels), req); 836133625Snjl 837126430Snjlout: 838197648Sjhb ACPI_SERIAL_END(video_output); 839126430Snjl return (err); 840126430Snjl} 841126430Snjl 842126430Snjlstatic void 843126430Snjlvid_set_switch_policy(ACPI_HANDLE handle, UINT32 policy) 844126430Snjl{ 845126430Snjl ACPI_STATUS status; 846126430Snjl 847126560Snjl status = acpi_SetInteger(handle, "_DOS", policy); 848126430Snjl if (ACPI_FAILURE(status)) 849126430Snjl printf("can't evaluate %s._DOS - %s\n", 850126430Snjl acpi_name(handle), AcpiFormatException(status)); 851126430Snjl} 852126430Snjl 853126430Snjlstruct enum_callback_arg { 854126430Snjl void (*callback)(ACPI_HANDLE, UINT32, void *); 855126430Snjl void *context; 856126430Snjl ACPI_OBJECT *dod_pkg; 857132605Snjl int count; 858126430Snjl}; 859126430Snjl 860126430Snjlstatic ACPI_STATUS 861126430Snjlvid_enum_outputs_subr(ACPI_HANDLE handle, UINT32 level __unused, 862132605Snjl void *context, void **retp __unused) 863126430Snjl{ 864126430Snjl ACPI_STATUS status; 865132605Snjl UINT32 adr, val; 866126430Snjl struct enum_callback_arg *argset; 867126430Snjl size_t i; 868126430Snjl 869133625Snjl ACPI_SERIAL_ASSERT(video); 870126430Snjl argset = context; 871126560Snjl status = acpi_GetInteger(handle, "_ADR", &adr); 872132605Snjl if (ACPI_FAILURE(status)) 873132605Snjl return (AE_OK); 874132605Snjl 875132605Snjl for (i = 0; i < argset->dod_pkg->Package.Count; i++) { 876132605Snjl if (acpi_PkgInt32(argset->dod_pkg, i, &val) == 0 && 877161184Sbruno (val & DOD_DEVID_MASK_FULL) == adr) { 878132605Snjl argset->callback(handle, val, argset->context); 879132605Snjl argset->count++; 880126430Snjl } 881126430Snjl } 882126430Snjl 883126430Snjl return (AE_OK); 884126430Snjl} 885126430Snjl 886126430Snjlstatic int 887126430Snjlvid_enum_outputs(ACPI_HANDLE handle, 888126430Snjl void (*callback)(ACPI_HANDLE, UINT32, void *), void *context) 889126430Snjl{ 890126430Snjl ACPI_STATUS status; 891126430Snjl ACPI_BUFFER dod_buf; 892126430Snjl ACPI_OBJECT *res; 893126430Snjl struct enum_callback_arg argset; 894126430Snjl 895133625Snjl ACPI_SERIAL_ASSERT(video); 896126430Snjl dod_buf.Length = ACPI_ALLOCATE_BUFFER; 897126430Snjl dod_buf.Pointer = NULL; 898126430Snjl status = AcpiEvaluateObject(handle, "_DOD", NULL, &dod_buf); 899126430Snjl if (ACPI_FAILURE(status)) { 900126430Snjl if (status != AE_NOT_FOUND) 901126430Snjl printf("can't evaluate %s._DOD - %s\n", 902126430Snjl acpi_name(handle), AcpiFormatException(status)); 903132605Snjl argset.count = -1; 904126430Snjl goto out; 905126430Snjl } 906126430Snjl res = (ACPI_OBJECT *)dod_buf.Pointer; 907132605Snjl if (!ACPI_PKG_VALID(res, 1)) { 908126430Snjl printf("evaluation of %s._DOD makes no sense\n", 909126430Snjl acpi_name(handle)); 910132605Snjl argset.count = -1; 911126430Snjl goto out; 912126430Snjl } 913126430Snjl if (callback == NULL) { 914132605Snjl argset.count = res->Package.Count; 915126430Snjl goto out; 916126430Snjl } 917126430Snjl argset.callback = callback; 918126430Snjl argset.context = context; 919126430Snjl argset.dod_pkg = res; 920132605Snjl argset.count = 0; 921126430Snjl status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1, 922199337Sjkim vid_enum_outputs_subr, NULL, &argset, NULL); 923126430Snjl if (ACPI_FAILURE(status)) 924126430Snjl printf("failed walking down %s - %s\n", 925126430Snjl acpi_name(handle), AcpiFormatException(status)); 926126430Snjlout: 927126430Snjl if (dod_buf.Pointer != NULL) 928126430Snjl AcpiOsFree(dod_buf.Pointer); 929132605Snjl return (argset.count); 930126430Snjl} 931126430Snjl 932126430Snjlstatic int 933133625Snjlvo_get_brightness_levels(ACPI_HANDLE handle, int **levelp) 934126430Snjl{ 935126430Snjl ACPI_STATUS status; 936126430Snjl ACPI_BUFFER bcl_buf; 937132605Snjl ACPI_OBJECT *res; 938137438Snjl int num, i, n, *levels; 939126430Snjl 940126430Snjl bcl_buf.Length = ACPI_ALLOCATE_BUFFER; 941126430Snjl bcl_buf.Pointer = NULL; 942126430Snjl status = AcpiEvaluateObject(handle, "_BCL", NULL, &bcl_buf); 943126430Snjl if (ACPI_FAILURE(status)) { 944126430Snjl if (status != AE_NOT_FOUND) 945126430Snjl printf("can't evaluate %s._BCL - %s\n", 946126430Snjl acpi_name(handle), AcpiFormatException(status)); 947126430Snjl goto out; 948126430Snjl } 949126430Snjl res = (ACPI_OBJECT *)bcl_buf.Pointer; 950132605Snjl if (!ACPI_PKG_VALID(res, 2)) { 951126430Snjl printf("evaluation of %s._BCL makes no sense\n", 952126430Snjl acpi_name(handle)); 953126430Snjl goto out; 954126430Snjl } 955126430Snjl num = res->Package.Count; 956209065Sjkim if (num < 2 || levelp == NULL) 957126430Snjl goto out; 958132605Snjl levels = AcpiOsAllocate(num * sizeof(*levels)); 959209065Sjkim if (levels == NULL) 960126430Snjl goto out; 961132605Snjl for (i = 0, n = 0; i < num; i++) 962132605Snjl if (acpi_PkgInt32(res, i, &levels[n]) == 0) 963132605Snjl n++; 964126430Snjl if (n < 2) { 965126430Snjl AcpiOsFree(levels); 966209065Sjkim goto out; 967126430Snjl } 968209065Sjkim *levelp = levels; 969209065Sjkim return (n); 970209065Sjkim 971126430Snjlout: 972126430Snjl if (bcl_buf.Pointer != NULL) 973126430Snjl AcpiOsFree(bcl_buf.Pointer); 974209065Sjkim return (0); 975126430Snjl} 976126430Snjl 977203810Sjkimstatic int 978203810Sjkimvo_get_brightness(ACPI_HANDLE handle) 979203810Sjkim{ 980203810Sjkim UINT32 level; 981203810Sjkim ACPI_STATUS status; 982203810Sjkim 983203810Sjkim ACPI_SERIAL_ASSERT(video_output); 984203810Sjkim status = acpi_GetInteger(handle, "_BQC", &level); 985203810Sjkim if (ACPI_FAILURE(status)) { 986203810Sjkim printf("can't evaluate %s._BQC - %s\n", acpi_name(handle), 987203810Sjkim AcpiFormatException(status)); 988203810Sjkim return (-1); 989203810Sjkim } 990203810Sjkim if (level > 100) 991203810Sjkim return (-1); 992203810Sjkim 993203810Sjkim return (level); 994203810Sjkim} 995203810Sjkim 996126430Snjlstatic void 997126430Snjlvo_set_brightness(ACPI_HANDLE handle, int level) 998126430Snjl{ 999126430Snjl ACPI_STATUS status; 1000126430Snjl 1001197648Sjhb ACPI_SERIAL_ASSERT(video_output); 1002126560Snjl status = acpi_SetInteger(handle, "_BCM", level); 1003126430Snjl if (ACPI_FAILURE(status)) 1004126430Snjl printf("can't evaluate %s._BCM - %s\n", 1005126430Snjl acpi_name(handle), AcpiFormatException(status)); 1006126430Snjl} 1007126430Snjl 1008126430Snjlstatic UINT32 1009126430Snjlvo_get_device_status(ACPI_HANDLE handle) 1010126430Snjl{ 1011137438Snjl UINT32 dcs; 1012126430Snjl ACPI_STATUS status; 1013126430Snjl 1014197648Sjhb ACPI_SERIAL_ASSERT(video_output); 1015137438Snjl dcs = 0; 1016126560Snjl status = acpi_GetInteger(handle, "_DCS", &dcs); 1017126430Snjl if (ACPI_FAILURE(status)) 1018126430Snjl printf("can't evaluate %s._DCS - %s\n", 1019126430Snjl acpi_name(handle), AcpiFormatException(status)); 1020126430Snjl 1021126430Snjl return (dcs); 1022126430Snjl} 1023126430Snjl 1024126430Snjlstatic UINT32 1025133625Snjlvo_get_graphics_state(ACPI_HANDLE handle) 1026126430Snjl{ 1027137438Snjl UINT32 dgs; 1028126430Snjl ACPI_STATUS status; 1029126430Snjl 1030137438Snjl dgs = 0; 1031126560Snjl status = acpi_GetInteger(handle, "_DGS", &dgs); 1032126430Snjl if (ACPI_FAILURE(status)) 1033126430Snjl printf("can't evaluate %s._DGS - %s\n", 1034126430Snjl acpi_name(handle), AcpiFormatException(status)); 1035126430Snjl 1036126430Snjl return (dgs); 1037126430Snjl} 1038126430Snjl 1039126430Snjlstatic void 1040126430Snjlvo_set_device_state(ACPI_HANDLE handle, UINT32 state) 1041126430Snjl{ 1042126430Snjl ACPI_STATUS status; 1043126430Snjl 1044197648Sjhb ACPI_SERIAL_ASSERT(video_output); 1045126560Snjl status = acpi_SetInteger(handle, "_DSS", state); 1046126430Snjl if (ACPI_FAILURE(status)) 1047126430Snjl printf("can't evaluate %s._DSS - %s\n", 1048126430Snjl acpi_name(handle), AcpiFormatException(status)); 1049126430Snjl} 1050