acpi_video.c revision 129879
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 * $FreeBSD: head/sys/dev/acpica/acpi_video.c 129879 2004-05-30 20:08:47Z phk $ 28126430Snjl */ 29126430Snjl 30126430Snjl#include <sys/param.h> 31126430Snjl#include <sys/kernel.h> 32126430Snjl#include <sys/malloc.h> 33129879Sphk#include <sys/module.h> 34126430Snjl#include <sys/bus.h> 35126430Snjl#include <sys/power.h> 36126430Snjl#include <sys/queue.h> 37126430Snjl#include <sys/sysctl.h> 38126430Snjl 39126430Snjl#include "acpi.h" 40126430Snjl#include <dev/acpica/acpivar.h> 41126430Snjl 42126430Snjl/* ACPI video extension driver. */ 43126430Snjlstruct acpi_video_output { 44126430Snjl ACPI_HANDLE handle; 45126430Snjl UINT32 adr; 46126430Snjl STAILQ_ENTRY(acpi_video_output) vo_next; 47126430Snjl struct { 48126430Snjl int num; 49126430Snjl STAILQ_ENTRY(acpi_video_output) next; 50126430Snjl } vo_unit; 51126430Snjl int vo_brightness; 52126430Snjl int vo_fullpower; 53126430Snjl int vo_economy; 54126430Snjl int vo_numlevels; 55126430Snjl int *vo_levels; 56126430Snjl struct sysctl_ctx_list vo_sysctl_ctx; 57126430Snjl struct sysctl_oid *vo_sysctl_tree; 58126430Snjl}; 59126430Snjl 60126430SnjlSTAILQ_HEAD(acpi_video_output_queue, acpi_video_output); 61126430Snjl 62126430Snjlstruct acpi_video_softc { 63126430Snjl device_t device; 64126430Snjl ACPI_HANDLE handle; 65126430Snjl STAILQ_HEAD(, acpi_video_output) vid_outputs; 66126430Snjl eventhandler_tag vid_pwr_evh; 67126430Snjl}; 68126430Snjl 69126430Snjl/* interfaces */ 70126430Snjlstatic int acpi_video_modevent(struct module*, int, void *); 71126430Snjlstatic int acpi_video_probe(device_t); 72126430Snjlstatic int acpi_video_attach(device_t); 73126430Snjlstatic int acpi_video_detach(device_t); 74126430Snjlstatic int acpi_video_shutdown(device_t); 75126430Snjlstatic void acpi_video_notify_handler(ACPI_HANDLE, UINT32, void *); 76126430Snjlstatic void acpi_video_power_profile(void *); 77126430Snjlstatic void acpi_video_bind_outputs(struct acpi_video_softc *); 78126430Snjlstatic struct acpi_video_output 79126430Snjl *acpi_video_vo_init(UINT32); 80126430Snjlstatic void acpi_video_vo_bind(struct acpi_video_output *, ACPI_HANDLE); 81126430Snjlstatic void acpi_video_vo_destroy(struct acpi_video_output *); 82126430Snjlstatic int acpi_video_vo_check_level(struct acpi_video_output *, int); 83126430Snjlstatic int acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS); 84126430Snjlstatic int acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS); 85126430Snjlstatic int acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS); 86126430Snjlstatic int acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS); 87126430Snjl 88126430Snjl/* operations */ 89126430Snjlstatic int vid_check_requirements(ACPI_HANDLE); 90126430Snjlstatic void vid_set_switch_policy(ACPI_HANDLE, UINT32); 91126430Snjlstatic int vid_enum_outputs(ACPI_HANDLE, 92126430Snjl void(*)(ACPI_HANDLE, UINT32, void *), void *); 93126430Snjlstatic int vo_query_brightness_levels(ACPI_HANDLE, int **); 94126430Snjlstatic void vo_set_brightness(ACPI_HANDLE, int); 95126430Snjlstatic UINT32 vo_get_device_status(ACPI_HANDLE); 96126430Snjlstatic UINT32 vo_query_graphics_state(ACPI_HANDLE); 97126430Snjlstatic void vo_set_device_state(ACPI_HANDLE, UINT32); 98126430Snjl 99126430Snjl/* events */ 100126430Snjl#define VID_NOTIFY_SWITCHED 0x80 101126430Snjl#define VID_NOTIFY_REPROBE 0x81 102126430Snjl 103126430Snjl/* _DOS (Enable/Disable Output Switching) argument bits */ 104126430Snjl#define DOS_SWITCH_MASK ((UINT32)3) 105126430Snjl#define DOS_SWITCH_BY_OSPM ((UINT32)0) 106126430Snjl#define DOS_SWITCH_BY_BIOS ((UINT32)1) 107126430Snjl#define DOS_SWITCH_LOCKED ((UINT32)2) 108126430Snjl#define DOS_BRIGHTNESS_BY_BIOS ((UINT32)1 << 2) 109126430Snjl 110126430Snjl/* _DOD and subdev's _ADR */ 111126430Snjl#define DOD_DEVID_MASK ((UINT32)0xffff) 112126430Snjl#define DOD_DEVID_MONITOR ((UINT32)0x0100) 113126430Snjl#define DOD_DEVID_PANEL ((UINT32)0x0110) 114126430Snjl#define DOD_DEVID_TV ((UINT32)0x0200) 115126430Snjl#define DOD_BIOS ((UINT32)1 << 16) 116126430Snjl#define DOD_NONVGA ((UINT32)1 << 17) 117126430Snjl#define DOD_HEAD_ID_SHIFT 18 118126430Snjl#define DOD_HEAD_ID_BITS 3 119126430Snjl#define DOD_HEAD_ID_MASK \ 120126430Snjl ((((UINT32)1 << DOD_HEAD_ID_BITS) - 1) << DOD_HEAD_ID_SHIFT) 121126430Snjl 122126430Snjl/* _BCL related constants */ 123126430Snjl#define BCL_FULLPOWER 0 124126430Snjl#define BCL_ECONOMY 1 125126430Snjl 126126430Snjl/* _DCS (Device Currrent Status) value bits and masks. */ 127126430Snjl#define DCS_EXISTS ((UINT32)1 << 0) 128126430Snjl#define DCS_ACTIVE ((UINT32)1 << 1) 129126430Snjl#define DCS_READY ((UINT32)1 << 2) 130126430Snjl#define DCS_FUNCTIONAL ((UINT32)1 << 3) 131126430Snjl#define DCS_ATTACHED ((UINT32)1 << 4) 132126430Snjl 133126430Snjl/* _DSS (Device Set Status) argument bits and masks. */ 134126430Snjl#define DSS_INACTIVE ((UINT32)0) 135126430Snjl#define DSS_ACTIVE ((UINT32)1 << 0) 136126430Snjl#define DSS_ACTIVITY ((UINT32)1 << 0) 137126430Snjl#define DSS_SETNEXT ((UINT32)1 << 30) 138126430Snjl#define DSS_COMMIT ((UINT32)1 << 31) 139126430Snjl 140126430Snjlstatic device_method_t acpi_video_methods[] = { 141126430Snjl DEVMETHOD(device_probe, acpi_video_probe), 142126430Snjl DEVMETHOD(device_attach, acpi_video_attach), 143126430Snjl DEVMETHOD(device_detach, acpi_video_detach), 144126430Snjl DEVMETHOD(device_shutdown, acpi_video_shutdown), 145126430Snjl { 0, 0 } 146126430Snjl}; 147126430Snjl 148126430Snjlstatic driver_t acpi_video_driver = { 149126430Snjl "acpi_video", 150126430Snjl acpi_video_methods, 151126430Snjl sizeof(struct acpi_video_softc), 152126430Snjl}; 153126430Snjl 154126430Snjlstatic devclass_t acpi_video_devclass; 155126430Snjl 156126430SnjlDRIVER_MODULE(acpi_video, acpi, acpi_video_driver, acpi_video_devclass, 157126430Snjl acpi_video_modevent, NULL); 158128036SnjlMODULE_DEPEND(acpi_video, acpi, 1, 1, 1); 159126430Snjl 160126430Snjlstruct sysctl_ctx_list acpi_video_sysctl_ctx; 161126430Snjlstruct sysctl_oid *acpi_video_sysctl_tree; 162126430Snjl 163126430Snjlstatic struct acpi_video_output_queue 164126430Snjl lcd_units, crt_units, tv_units, other_units; 165126430Snjl 166126430SnjlMALLOC_DEFINE(M_ACPIVIDEO, "acpivideo", "ACPI video extension"); 167126430Snjl 168126430Snjlstatic int 169126430Snjlacpi_video_modevent(struct module *mod __unused, int evt, void *cookie __unused) 170126430Snjl{ 171126430Snjl int err = 0; 172126430Snjl 173126430Snjl switch (evt) { 174126430Snjl case MOD_LOAD: 175126430Snjl acpi_video_sysctl_tree = NULL; 176126430Snjl sysctl_ctx_init(&acpi_video_sysctl_ctx); 177126430Snjl STAILQ_INIT(&lcd_units); 178126430Snjl STAILQ_INIT(&crt_units); 179126430Snjl STAILQ_INIT(&tv_units); 180126430Snjl STAILQ_INIT(&other_units); 181126430Snjl break; 182126430Snjl case MOD_UNLOAD: 183126430Snjl acpi_video_sysctl_tree = NULL; 184126430Snjl sysctl_ctx_free(&acpi_video_sysctl_ctx); 185126430Snjl break; 186126430Snjl default: 187126430Snjl err = EINVAL; 188126430Snjl } 189126430Snjl 190126430Snjl return (err); 191126430Snjl} 192126430Snjl 193126430Snjlstatic int 194126430Snjlacpi_video_probe(device_t dev) 195126430Snjl{ 196126430Snjl int err = ENXIO; 197126430Snjl ACPI_HANDLE handle; 198126430Snjl ACPI_LOCK_DECL; 199126430Snjl 200126430Snjl ACPI_LOCK; 201126430Snjl handle = acpi_get_handle(dev); 202126430Snjl if (acpi_get_type(dev) == ACPI_TYPE_DEVICE && 203126430Snjl !acpi_disabled("video") && 204126430Snjl vid_check_requirements(handle)) { 205126430Snjl device_set_desc(dev, "ACPI video extension"); 206126430Snjl err = 0; 207126430Snjl } 208126430Snjl ACPI_UNLOCK; 209126430Snjl 210126430Snjl return (err); 211126430Snjl} 212126430Snjl 213126430Snjlstatic int 214126430Snjlacpi_video_attach(device_t dev) 215126430Snjl{ 216126430Snjl struct acpi_softc *acpi_sc; 217126430Snjl struct acpi_video_softc *sc; 218126430Snjl ACPI_LOCK_DECL; 219126430Snjl 220126430Snjl sc = device_get_softc(dev); 221126430Snjl ACPI_LOCK; 222126430Snjl 223126430Snjl acpi_sc = acpi_device_get_parent_softc(dev); 224126430Snjl if (acpi_video_sysctl_tree == NULL && acpi_sc != NULL) { 225126430Snjl acpi_video_sysctl_tree = SYSCTL_ADD_NODE(&acpi_video_sysctl_ctx, 226126430Snjl SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 227126430Snjl OID_AUTO, "video", CTLFLAG_RD, 0, 228126430Snjl "video extension control"); 229126430Snjl } 230126430Snjl 231126430Snjl sc->device = dev; 232126430Snjl sc->handle = acpi_get_handle(dev); 233126430Snjl STAILQ_INIT(&sc->vid_outputs); 234126430Snjl 235126430Snjl AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 236126430Snjl acpi_video_notify_handler, sc); 237126430Snjl sc->vid_pwr_evh = EVENTHANDLER_REGISTER(power_profile_change, 238126430Snjl acpi_video_power_profile, sc, 0); 239126430Snjl 240126430Snjl acpi_video_bind_outputs(sc); 241126430Snjl vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_OSPM); 242126430Snjl 243126430Snjl ACPI_UNLOCK; 244126430Snjl acpi_video_power_profile(sc); 245126430Snjl 246126430Snjl return (0); 247126430Snjl} 248126430Snjl 249126430Snjlstatic int 250126430Snjlacpi_video_detach(device_t dev) 251126430Snjl{ 252126430Snjl struct acpi_video_softc *sc; 253126430Snjl struct acpi_video_output *vo, *vn; 254126430Snjl ACPI_LOCK_DECL; 255126430Snjl 256126430Snjl sc = device_get_softc(dev); 257126430Snjl ACPI_LOCK; 258126430Snjl 259126430Snjl vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); 260126430Snjl EVENTHANDLER_DEREGISTER(power_profile_change, sc->vid_pwr_evh); 261126430Snjl AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 262126430Snjl acpi_video_notify_handler); 263126430Snjl 264126430Snjl for (vo = STAILQ_FIRST(&sc->vid_outputs); vo != NULL; vo = vn) { 265126430Snjl vn = STAILQ_NEXT(vo, vo_next); 266126430Snjl acpi_video_vo_destroy(vo); 267126430Snjl } 268126430Snjl 269126430Snjl ACPI_UNLOCK; 270126430Snjl return (0); 271126430Snjl} 272126430Snjl 273126430Snjlstatic int 274126430Snjlacpi_video_shutdown(device_t dev) 275126430Snjl{ 276126430Snjl struct acpi_video_softc *sc; 277126430Snjl ACPI_LOCK_DECL; 278126430Snjl 279126430Snjl sc = device_get_softc(dev); 280126430Snjl ACPI_LOCK; 281126430Snjl vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); 282126430Snjl ACPI_UNLOCK; 283126430Snjl 284126430Snjl return (0); 285126430Snjl} 286126430Snjl 287126430Snjlstatic void 288126430Snjlacpi_video_notify_handler(ACPI_HANDLE handle __unused, UINT32 notify, void *context) 289126430Snjl{ 290126430Snjl struct acpi_video_softc *sc; 291126430Snjl struct acpi_video_output *vo; 292126430Snjl ACPI_HANDLE lasthand = NULL; 293126430Snjl UINT32 dcs, dss, dss_p = 0; 294126430Snjl 295126430Snjl ACPI_ASSERTLOCK; 296126430Snjl sc = context; 297126430Snjl 298126430Snjl switch (notify) { 299126430Snjl case VID_NOTIFY_SWITCHED: 300126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 301126430Snjl dss = vo_query_graphics_state(vo->handle); 302126430Snjl dcs = vo_get_device_status(vo->handle); 303126430Snjl if (!(dcs & DCS_READY)) 304126430Snjl dss = DSS_INACTIVE; 305126430Snjl if (((dcs & DCS_ACTIVE) && dss == DSS_INACTIVE) || 306126430Snjl (!(dcs & DCS_ACTIVE) && dss == DSS_ACTIVE)) { 307126430Snjl if (lasthand != NULL) 308126430Snjl vo_set_device_state(lasthand, dss_p); 309126430Snjl dss_p = dss; 310126430Snjl lasthand = vo->handle; 311126430Snjl } 312126430Snjl } 313126430Snjl if (lasthand != NULL) 314126430Snjl vo_set_device_state(lasthand, dss_p|DSS_COMMIT); 315126430Snjl break; 316126430Snjl case VID_NOTIFY_REPROBE: 317126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) 318126430Snjl vo->handle = NULL; 319126430Snjl acpi_video_bind_outputs(sc); 320126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 321126430Snjl if (vo->handle == NULL) { 322126430Snjl STAILQ_REMOVE(&sc->vid_outputs, vo, 323126430Snjl acpi_video_output, vo_next); 324126430Snjl acpi_video_vo_destroy(vo); 325126430Snjl } 326126430Snjl } 327126430Snjl break; 328126430Snjl default: 329126430Snjl device_printf(sc->device, 330126430Snjl "unknown notify event 0x%x\n", notify); 331126430Snjl } 332126430Snjl} 333126430Snjl 334126430Snjlstatic void 335126430Snjlacpi_video_power_profile(void *context) 336126430Snjl{ 337126430Snjl int state; 338126430Snjl struct acpi_video_softc *sc; 339126430Snjl struct acpi_video_output *vo; 340126430Snjl ACPI_LOCK_DECL; 341126430Snjl 342126430Snjl sc = context; 343126430Snjl state = power_profile_get_state(); 344126430Snjl if (state != POWER_PROFILE_PERFORMANCE && 345126430Snjl state != POWER_PROFILE_ECONOMY) 346126430Snjl return; 347126430Snjl 348126430Snjl ACPI_LOCK; 349126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 350126430Snjl if (vo->vo_levels != NULL && vo->vo_brightness == -1) 351126430Snjl vo_set_brightness(vo->handle, 352126430Snjl state == POWER_PROFILE_ECONOMY 353126430Snjl ? vo->vo_economy : vo->vo_fullpower); 354126430Snjl } 355126430Snjl ACPI_UNLOCK; 356126430Snjl} 357126430Snjl 358126430Snjlstatic void 359126430Snjlacpi_video_bind_outputs_subr(ACPI_HANDLE handle, UINT32 adr, void *context) 360126430Snjl{ 361126430Snjl struct acpi_video_softc *sc; 362126430Snjl struct acpi_video_output *vo; 363126430Snjl 364126430Snjl sc = context; 365126430Snjl 366126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 367126430Snjl if (vo->adr == adr) { 368126430Snjl acpi_video_vo_bind(vo, handle); 369126430Snjl return; 370126430Snjl } 371126430Snjl } 372126430Snjl vo = acpi_video_vo_init(adr); 373126430Snjl if (vo != NULL) { 374126430Snjl acpi_video_vo_bind(vo, handle); 375126430Snjl STAILQ_INSERT_TAIL(&sc->vid_outputs, vo, vo_next); 376126430Snjl } 377126430Snjl} 378126430Snjl 379126430Snjlstatic void 380126430Snjlacpi_video_bind_outputs(struct acpi_video_softc *sc) 381126430Snjl{ 382126430Snjl ACPI_ASSERTLOCK; 383126430Snjl 384126430Snjl vid_enum_outputs(sc->handle, acpi_video_bind_outputs_subr, sc); 385126430Snjl} 386126430Snjl 387126430Snjlstatic struct acpi_video_output * 388126430Snjlacpi_video_vo_init(UINT32 adr) 389126430Snjl{ 390126430Snjl struct acpi_video_output *vn, *vo, *vp; 391126430Snjl int n, x; 392126430Snjl char name[64], env[128]; 393126430Snjl const char *type, *desc; 394126430Snjl struct acpi_video_output_queue *voqh; 395126430Snjl 396126430Snjl switch (adr & DOD_DEVID_MASK) { 397126430Snjl case DOD_DEVID_MONITOR: 398126430Snjl desc = "CRT monitor"; 399126430Snjl type = "crt"; 400126430Snjl voqh = &crt_units; 401126430Snjl break; 402126430Snjl case DOD_DEVID_PANEL: 403126430Snjl desc = "LCD panel"; 404126430Snjl type = "lcd"; 405126430Snjl voqh = &lcd_units; 406126430Snjl break; 407126430Snjl case DOD_DEVID_TV: 408126430Snjl desc = "TV"; 409126430Snjl type = "tv"; 410126430Snjl voqh = &tv_units; 411126430Snjl break; 412126430Snjl default: 413126430Snjl desc = "unknown output"; 414126430Snjl type = "out"; 415126430Snjl voqh = &other_units; 416126430Snjl } 417126430Snjl 418126430Snjl n = 0; 419126430Snjl vn = vp = NULL; 420126430Snjl /* XXX - needs locking for protecting STAILQ xxx_units. */ 421126430Snjl STAILQ_FOREACH(vn, voqh, vo_unit.next) { 422126430Snjl if (vn->vo_unit.num != n) 423126430Snjl break; 424126430Snjl vp = vn; 425126430Snjl n++; 426126430Snjl } 427126430Snjl 428126430Snjl snprintf(name, 64, "%s%d", type, n); 429126430Snjl 430126430Snjl vo = malloc(sizeof(*vo), M_ACPIVIDEO, M_NOWAIT); 431126430Snjl if (vo != NULL) { 432126430Snjl vo->handle = NULL; 433126430Snjl vo->adr = adr; 434126430Snjl vo->vo_unit.num = n; 435126430Snjl vo->vo_brightness = -1; 436126430Snjl vo->vo_fullpower = -1; /* TODO: override with tunables */ 437126430Snjl vo->vo_economy = -1; 438126430Snjl vo->vo_numlevels = 0; 439126430Snjl vo->vo_levels = NULL; 440126430Snjl snprintf(env, 128, "hw.acpi.video.%s.fullpower", name); 441126430Snjl if (getenv_int(env, &x)) 442126430Snjl vo->vo_fullpower = x; 443126430Snjl snprintf(env, 128, "hw.acpi.video.%s.economy", name); 444126430Snjl if (getenv_int(env, &x)) 445126430Snjl vo->vo_economy = x; 446126430Snjl 447126430Snjl sysctl_ctx_init(&vo->vo_sysctl_ctx); 448126430Snjl if (vp != NULL) 449126430Snjl STAILQ_INSERT_AFTER(voqh, vp, vo, vo_unit.next); 450126430Snjl else 451126430Snjl STAILQ_INSERT_TAIL(voqh, vo, vo_unit.next); 452126430Snjl if (acpi_video_sysctl_tree != NULL) 453126430Snjl vo->vo_sysctl_tree = 454126430Snjl SYSCTL_ADD_NODE(&vo->vo_sysctl_ctx, 455126430Snjl SYSCTL_CHILDREN(acpi_video_sysctl_tree), 456126430Snjl OID_AUTO, name, 457126430Snjl CTLFLAG_RD, 0, desc); 458126430Snjl if (vo->vo_sysctl_tree != NULL) { 459126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 460126430Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 461126430Snjl OID_AUTO, "active", 462126430Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 0, 463126430Snjl acpi_video_vo_active_sysctl, "I", 464126430Snjl "current activity of this device"); 465126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 466126430Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 467126430Snjl OID_AUTO, "brightness", 468126430Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 0, 469126430Snjl acpi_video_vo_bright_sysctl, "I", 470126430Snjl "current brightness level"); 471126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 472126430Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 473126430Snjl OID_AUTO, "fullpower", 474126430Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 475126430Snjl POWER_PROFILE_PERFORMANCE, 476126430Snjl acpi_video_vo_presets_sysctl, "I", 477126430Snjl "preset level for full power mode"); 478126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 479126430Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 480126430Snjl OID_AUTO, "economy", 481126430Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 482126430Snjl POWER_PROFILE_ECONOMY, 483126430Snjl acpi_video_vo_presets_sysctl, "I", 484126430Snjl "preset level for economy mode"); 485126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 486126430Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 487126430Snjl OID_AUTO, "levels", 488126430Snjl CTLTYPE_OPAQUE|CTLFLAG_RD, vo, 0, 489126430Snjl acpi_video_vo_levels_sysctl, "I", 490126430Snjl "supported brightness levels"); 491126430Snjl } else 492126430Snjl printf("%s: sysctl node creation failed\n", type); 493126430Snjl } else 494126430Snjl printf("%s: softc allocation failed\n", type); 495126430Snjl 496126430Snjl /* XXX unlock here - needs locking for protecting STAILQ xxx_units. */ 497126430Snjl 498126430Snjl if (bootverbose) { 499126430Snjl printf("found %s(%x)", desc, 500126430Snjl (unsigned int)(adr & DOD_DEVID_MASK)); 501126430Snjl if (adr & DOD_BIOS) 502126430Snjl printf(", detectable by BIOS"); 503126430Snjl if (adr & DOD_NONVGA) 504126430Snjl printf(" (not a VGA output)"); 505126430Snjl printf(", head #%d\n", 506126430Snjl (int)((adr & DOD_HEAD_ID_MASK) >> DOD_HEAD_ID_SHIFT)); 507126430Snjl } 508126430Snjl return vo; 509126430Snjl} 510126430Snjl 511126430Snjlstatic void 512126430Snjlacpi_video_vo_bind(struct acpi_video_output *vo, ACPI_HANDLE handle) 513126430Snjl{ 514126430Snjl ACPI_ASSERTLOCK; 515126430Snjl 516126430Snjl if (vo->vo_levels != NULL) 517126430Snjl AcpiOsFree(vo->vo_levels); 518126430Snjl vo->handle = handle; 519126430Snjl vo->vo_numlevels 520126430Snjl = vo_query_brightness_levels(handle, &vo->vo_levels); 521126430Snjl if (vo->vo_numlevels >= 2) { 522126430Snjl if (vo->vo_fullpower == -1 523126430Snjl || acpi_video_vo_check_level(vo, vo->vo_fullpower) != 0) 524126430Snjl /* XXX - can't deal with rebinding... */ 525126430Snjl vo->vo_fullpower = vo->vo_levels[BCL_FULLPOWER]; 526126430Snjl if (vo->vo_economy == -1 527126430Snjl || acpi_video_vo_check_level(vo, vo->vo_economy) != 0) 528126430Snjl /* XXX - see above. */ 529126430Snjl vo->vo_economy = vo->vo_levels[BCL_ECONOMY]; 530126430Snjl } 531126430Snjl} 532126430Snjl 533126430Snjlstatic void 534126430Snjlacpi_video_vo_destroy(struct acpi_video_output *vo) 535126430Snjl{ 536126430Snjl struct acpi_video_output_queue *voqh; 537126430Snjl 538126430Snjl ACPI_ASSERTLOCK; 539126430Snjl 540126430Snjl if (vo->vo_sysctl_tree != NULL) { 541126430Snjl vo->vo_sysctl_tree = NULL; 542126430Snjl sysctl_ctx_free(&vo->vo_sysctl_ctx); 543126430Snjl } 544126430Snjl if (vo->vo_levels != NULL) 545126430Snjl AcpiOsFree(vo->vo_levels); 546126430Snjl 547126430Snjl switch (vo->adr & DOD_DEVID_MASK) { 548126430Snjl case DOD_DEVID_MONITOR: 549126430Snjl voqh = &crt_units; 550126430Snjl break; 551126430Snjl case DOD_DEVID_PANEL: 552126430Snjl voqh = &lcd_units; 553126430Snjl break; 554126430Snjl case DOD_DEVID_TV: 555126430Snjl voqh = &tv_units; 556126430Snjl break; 557126430Snjl default: 558126430Snjl voqh = &other_units; 559126430Snjl } 560126430Snjl /* XXX - needs locking for protecting STAILQ xxx_units. */ 561126430Snjl STAILQ_REMOVE(voqh, vo, acpi_video_output, vo_unit.next); 562126430Snjl free(vo, M_ACPIVIDEO); 563126430Snjl} 564126430Snjl 565126430Snjlstatic int 566126430Snjlacpi_video_vo_check_level(struct acpi_video_output *vo, int level) 567126430Snjl{ 568126430Snjl int i; 569126430Snjl 570126430Snjl if (vo->vo_levels == NULL) 571126430Snjl return (ENODEV); 572126430Snjl for (i = 0; i < vo->vo_numlevels; i++) 573126430Snjl if (vo->vo_levels[i] == level) 574126430Snjl return (0); 575126430Snjl return (EINVAL); 576126430Snjl} 577126430Snjl 578126430Snjl/* ARGSUSED */ 579126430Snjlstatic int 580126430Snjlacpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS) 581126430Snjl{ 582126430Snjl struct acpi_video_output *vo; 583126430Snjl int state, err; 584126430Snjl ACPI_LOCK_DECL; 585126430Snjl 586126430Snjl ACPI_LOCK; 587126430Snjl vo = (struct acpi_video_output *)arg1; 588126430Snjl if (vo->handle == NULL) { 589126430Snjl err = ENXIO; 590126430Snjl goto out; 591126430Snjl } 592126430Snjl state = vo_get_device_status(vo->handle) & DCS_ACTIVE? 1 : 0; 593126430Snjl err = sysctl_handle_int(oidp, &state, 0, req); 594126430Snjl if (err != 0 || req->newptr == NULL) 595126430Snjl goto out; 596126430Snjl vo_set_device_state(vo->handle, 597126430Snjl DSS_COMMIT | (state? DSS_ACTIVE : DSS_INACTIVE)); 598126430Snjlout: 599126430Snjl ACPI_UNLOCK; 600126430Snjl return (err); 601126430Snjl} 602126430Snjl 603126430Snjl/* ARGSUSED */ 604126430Snjlstatic int 605126430Snjlacpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS) 606126430Snjl{ 607126430Snjl struct acpi_video_output *vo; 608126430Snjl int level, preset, err; 609126430Snjl ACPI_LOCK_DECL; 610126430Snjl 611126430Snjl ACPI_LOCK; 612126430Snjl vo = (struct acpi_video_output *)arg1; 613126430Snjl if (vo->handle == NULL) { 614126430Snjl err = ENXIO; 615126430Snjl goto out; 616126430Snjl } 617126430Snjl if (vo->vo_levels == NULL) { 618126430Snjl err = ENODEV; 619126430Snjl goto out; 620126430Snjl } 621126430Snjl 622126430Snjl preset = (power_profile_get_state() == POWER_PROFILE_ECONOMY 623126430Snjl ? vo->vo_economy 624126430Snjl : vo->vo_fullpower); 625126430Snjl level = vo->vo_brightness; 626126430Snjl if (level == -1) 627126430Snjl level = preset; 628126430Snjl 629126430Snjl err = sysctl_handle_int(oidp, &level, 0, req); 630126430Snjl if (err != 0 || req->newptr == NULL) 631126430Snjl goto out; 632126430Snjl if (level < -1 || level > 100) { 633126430Snjl err = EINVAL; 634126430Snjl goto out; 635126430Snjl } 636126430Snjl 637126430Snjl if (level != -1 && (err = acpi_video_vo_check_level(vo, level))) 638126430Snjl goto out; 639126430Snjl vo->vo_brightness = level; 640126430Snjl vo_set_brightness(vo->handle, level == -1? preset : level); 641126430Snjlout: 642126430Snjl ACPI_UNLOCK; 643126430Snjl return (err); 644126430Snjl} 645126430Snjl 646126430Snjlstatic int 647126430Snjlacpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS) 648126430Snjl{ 649126430Snjl struct acpi_video_output *vo; 650126430Snjl int level, *preset, err = 0; 651126430Snjl ACPI_LOCK_DECL; 652126430Snjl 653126430Snjl ACPI_LOCK; 654126430Snjl vo = (struct acpi_video_output *)arg1; 655126430Snjl if (vo->handle == NULL) { 656126430Snjl err = ENXIO; 657126430Snjl goto out; 658126430Snjl } 659126430Snjl if (vo->vo_levels == NULL) { 660126430Snjl err = ENODEV; 661126430Snjl goto out; 662126430Snjl } 663126430Snjl preset = (arg2 == POWER_PROFILE_ECONOMY 664126430Snjl ? &vo->vo_economy 665126430Snjl : &vo->vo_fullpower); 666126430Snjl level = *preset; 667126430Snjl err = sysctl_handle_int(oidp, &level, 0, req); 668126430Snjl if (err != 0 || req->newptr == NULL) 669126430Snjl goto out; 670126430Snjl if (level < -1 || level > 100) { 671126430Snjl err = EINVAL; 672126430Snjl goto out; 673126430Snjl } 674126430Snjl if (level == -1) 675126430Snjl level = vo->vo_levels 676126430Snjl [arg2 == POWER_PROFILE_ECONOMY 677126430Snjl ? BCL_ECONOMY : BCL_FULLPOWER]; 678126430Snjl else if ((err = acpi_video_vo_check_level(vo, level)) != 0) 679126430Snjl goto out; 680126430Snjl 681126430Snjl if (vo->vo_brightness == -1 && (power_profile_get_state() == arg2)) 682126430Snjl vo_set_brightness(vo->handle, level); 683126430Snjl *preset = level; 684126430Snjlout: 685126430Snjl ACPI_UNLOCK; 686126430Snjl return (err); 687126430Snjl} 688126430Snjl 689126430Snjl/* ARGSUSED */ 690126430Snjlstatic int 691126430Snjlacpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS) 692126430Snjl{ 693126430Snjl struct acpi_video_output *vo; 694126430Snjl int err; 695126430Snjl ACPI_LOCK_DECL; 696126430Snjl 697126430Snjl ACPI_LOCK; 698126430Snjl vo = (struct acpi_video_output *)arg1; 699126430Snjl if (vo->vo_levels == NULL) { 700126430Snjl err = ENODEV; 701126430Snjl goto out; 702126430Snjl } 703126430Snjl if (req->newptr != NULL) { 704126430Snjl err = EPERM; 705126430Snjl goto out; 706126430Snjl } 707126430Snjl err = sysctl_handle_opaque(oidp, vo->vo_levels, 708126430Snjl vo->vo_numlevels * sizeof *vo->vo_levels, req); 709126430Snjlout: 710126430Snjl ACPI_UNLOCK; 711126430Snjl return (err); 712126430Snjl} 713126430Snjl 714126430Snjlstatic int 715126430Snjlvid_check_requirements(ACPI_HANDLE handle) 716126430Snjl{ 717126430Snjl ACPI_HANDLE h_dod, h_dos; 718126430Snjl ACPI_OBJECT_TYPE t_dos; 719126430Snjl 720126430Snjl ACPI_ASSERTLOCK; 721126430Snjl 722126430Snjl /* check for _DOD, _DOS methods */ 723126430Snjl return (ACPI_SUCCESS(AcpiGetHandle(handle, "_DOD", &h_dod)) 724126430Snjl && ACPI_SUCCESS(AcpiGetHandle(handle, "_DOS", &h_dos)) 725126430Snjl && ACPI_SUCCESS(AcpiGetType(h_dos, &t_dos)) 726126430Snjl && t_dos == ACPI_TYPE_METHOD); 727126430Snjl} 728126430Snjl 729126430Snjlstatic void 730126430Snjlvid_set_switch_policy(ACPI_HANDLE handle, UINT32 policy) 731126430Snjl{ 732126430Snjl ACPI_STATUS status; 733126430Snjl 734126430Snjl ACPI_ASSERTLOCK; 735126560Snjl 736126560Snjl status = acpi_SetInteger(handle, "_DOS", policy); 737126430Snjl if (ACPI_FAILURE(status)) 738126430Snjl printf("can't evaluate %s._DOS - %s\n", 739126430Snjl acpi_name(handle), AcpiFormatException(status)); 740126430Snjl} 741126430Snjl 742126430Snjlstruct enum_callback_arg { 743126430Snjl void (*callback)(ACPI_HANDLE, UINT32, void *); 744126430Snjl void *context; 745126430Snjl ACPI_OBJECT *dod_pkg; 746126430Snjl}; 747126430Snjl 748126430Snjlstatic ACPI_STATUS 749126430Snjlvid_enum_outputs_subr(ACPI_HANDLE handle, UINT32 level __unused, 750126430Snjl void *context, void **retp) 751126430Snjl{ 752126430Snjl ACPI_STATUS status; 753126430Snjl ACPI_OBJECT *tmp; 754126430Snjl UINT32 adr; 755126430Snjl struct enum_callback_arg *argset; 756126430Snjl size_t i; 757126430Snjl 758126430Snjl argset = context; 759126560Snjl status = acpi_GetInteger(handle, "_ADR", &adr); 760126430Snjl if (ACPI_SUCCESS(status)) { 761126430Snjl for (i = 0; i < argset->dod_pkg->Package.Count; i++) { 762126430Snjl tmp = &argset->dod_pkg->Package.Elements[i]; 763126430Snjl if (tmp != NULL && tmp->Type == ACPI_TYPE_INTEGER && 764126430Snjl (tmp->Integer.Value & DOD_DEVID_MASK) == adr) { 765126430Snjl argset->callback(handle, tmp->Integer.Value, 766126430Snjl argset->context); 767126430Snjl (**(int**)retp)++; 768126430Snjl } 769126430Snjl } 770126430Snjl } 771126430Snjl 772126430Snjl return (AE_OK); 773126430Snjl} 774126430Snjl 775126430Snjlstatic int 776126430Snjlvid_enum_outputs(ACPI_HANDLE handle, 777126430Snjl void (*callback)(ACPI_HANDLE, UINT32, void *), void *context) 778126430Snjl{ 779126430Snjl ACPI_STATUS status; 780126430Snjl ACPI_BUFFER dod_buf; 781126430Snjl ACPI_OBJECT *res; 782126430Snjl int num = 0; 783126430Snjl void *pnum; 784126430Snjl struct enum_callback_arg argset; 785126430Snjl 786126430Snjl ACPI_ASSERTLOCK; 787126430Snjl dod_buf.Length = ACPI_ALLOCATE_BUFFER; 788126430Snjl dod_buf.Pointer = NULL; 789126430Snjl status = AcpiEvaluateObject(handle, "_DOD", NULL, &dod_buf); 790126430Snjl if (ACPI_FAILURE(status)) { 791126430Snjl if (status != AE_NOT_FOUND) 792126430Snjl printf("can't evaluate %s._DOD - %s\n", 793126430Snjl acpi_name(handle), AcpiFormatException(status)); 794126430Snjl num = -1; 795126430Snjl goto out; 796126430Snjl } 797126430Snjl res = (ACPI_OBJECT *)dod_buf.Pointer; 798126430Snjl if (res == NULL || res->Type != ACPI_TYPE_PACKAGE) { 799126430Snjl printf("evaluation of %s._DOD makes no sense\n", 800126430Snjl acpi_name(handle)); 801126430Snjl num = -1; 802126430Snjl goto out; 803126430Snjl } 804126430Snjl if (callback == NULL) { 805126430Snjl num = res->Package.Count; 806126430Snjl goto out; 807126430Snjl } 808126430Snjl argset.callback = callback; 809126430Snjl argset.context = context; 810126430Snjl argset.dod_pkg = res; 811126430Snjl pnum = # 812126430Snjl status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1, 813126430Snjl vid_enum_outputs_subr, &argset, 814126430Snjl &pnum); 815126430Snjl if (ACPI_FAILURE(status)) 816126430Snjl printf("failed walking down %s - %s\n", 817126430Snjl acpi_name(handle), AcpiFormatException(status)); 818126430Snjlout: 819126430Snjl if (dod_buf.Pointer != NULL) 820126430Snjl AcpiOsFree(dod_buf.Pointer); 821126430Snjl return (num); 822126430Snjl} 823126430Snjl 824126430Snjlstatic int 825126430Snjlvo_query_brightness_levels(ACPI_HANDLE handle, int **levelp) 826126430Snjl{ 827126430Snjl ACPI_STATUS status; 828126430Snjl ACPI_BUFFER bcl_buf; 829126430Snjl ACPI_OBJECT *res, *tmp; 830126430Snjl int num = 0, i, n, *levels; 831126430Snjl 832126430Snjl ACPI_ASSERTLOCK; 833126430Snjl bcl_buf.Length = ACPI_ALLOCATE_BUFFER; 834126430Snjl bcl_buf.Pointer = NULL; 835126430Snjl status = AcpiEvaluateObject(handle, "_BCL", NULL, &bcl_buf); 836126430Snjl if (ACPI_FAILURE(status)) { 837126430Snjl if (status != AE_NOT_FOUND) 838126430Snjl printf("can't evaluate %s._BCL - %s\n", 839126430Snjl acpi_name(handle), AcpiFormatException(status)); 840126430Snjl num = -1; 841126430Snjl goto out; 842126430Snjl } 843126430Snjl res = (ACPI_OBJECT *)bcl_buf.Pointer; 844126430Snjl if (res == NULL || res->Type != ACPI_TYPE_PACKAGE || 845126430Snjl res->Package.Count < 2) { 846126430Snjl printf("evaluation of %s._BCL makes no sense\n", 847126430Snjl acpi_name(handle)); 848126430Snjl num = -1; 849126430Snjl goto out; 850126430Snjl } 851126430Snjl num = res->Package.Count; 852126430Snjl if (levelp == NULL) 853126430Snjl goto out; 854126430Snjl levels = AcpiOsAllocate(num * sizeof *levels); 855126430Snjl if (levels == NULL) { 856126430Snjl num = -1; 857126430Snjl goto out; 858126430Snjl } 859126430Snjl for (i = 0, n = 0; i < num; i++) { 860126430Snjl tmp = &res->Package.Elements[i]; 861126430Snjl if (tmp != NULL && tmp->Type == ACPI_TYPE_INTEGER) 862126430Snjl levels[n++] = tmp->Integer.Value; 863126430Snjl } 864126430Snjl if (n < 2) { 865126430Snjl num = -1; 866126430Snjl AcpiOsFree(levels); 867126430Snjl } else { 868126430Snjl num = n; 869126430Snjl *levelp = levels; 870126430Snjl } 871126430Snjlout: 872126430Snjl if (bcl_buf.Pointer != NULL) 873126430Snjl AcpiOsFree(bcl_buf.Pointer); 874126430Snjl 875126430Snjl return (num); 876126430Snjl} 877126430Snjl 878126430Snjlstatic void 879126430Snjlvo_set_brightness(ACPI_HANDLE handle, int level) 880126430Snjl{ 881126430Snjl ACPI_STATUS status; 882126430Snjl 883126430Snjl ACPI_ASSERTLOCK; 884126560Snjl 885126560Snjl status = acpi_SetInteger(handle, "_BCM", level); 886126430Snjl if (ACPI_FAILURE(status)) 887126430Snjl printf("can't evaluate %s._BCM - %s\n", 888126430Snjl acpi_name(handle), AcpiFormatException(status)); 889126430Snjl} 890126430Snjl 891126430Snjlstatic UINT32 892126430Snjlvo_get_device_status(ACPI_HANDLE handle) 893126430Snjl{ 894126430Snjl UINT32 dcs = 0; 895126430Snjl ACPI_STATUS status; 896126430Snjl 897126430Snjl ACPI_ASSERTLOCK; 898126560Snjl status = acpi_GetInteger(handle, "_DCS", &dcs); 899126430Snjl if (ACPI_FAILURE(status)) 900126430Snjl printf("can't evaluate %s._DCS - %s\n", 901126430Snjl acpi_name(handle), AcpiFormatException(status)); 902126430Snjl 903126430Snjl return (dcs); 904126430Snjl} 905126430Snjl 906126430Snjlstatic UINT32 907126430Snjlvo_query_graphics_state(ACPI_HANDLE handle) 908126430Snjl{ 909126430Snjl UINT32 dgs = 0; 910126430Snjl ACPI_STATUS status; 911126430Snjl 912126430Snjl ACPI_ASSERTLOCK; 913126560Snjl status = acpi_GetInteger(handle, "_DGS", &dgs); 914126430Snjl if (ACPI_FAILURE(status)) 915126430Snjl printf("can't evaluate %s._DGS - %s\n", 916126430Snjl acpi_name(handle), AcpiFormatException(status)); 917126430Snjl 918126430Snjl return (dgs); 919126430Snjl} 920126430Snjl 921126430Snjlstatic void 922126430Snjlvo_set_device_state(ACPI_HANDLE handle, UINT32 state) 923126430Snjl{ 924126430Snjl ACPI_STATUS status; 925126430Snjl 926126430Snjl ACPI_ASSERTLOCK; 927126560Snjl 928126560Snjl status = acpi_SetInteger(handle, "_DSS", state); 929126430Snjl if (ACPI_FAILURE(status)) 930126430Snjl printf("can't evaluate %s._DSS - %s\n", 931126430Snjl acpi_name(handle), AcpiFormatException(status)); 932126430Snjl} 933