acpi_video.c revision 132526
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 132526 2004-07-22 05:18:05Z njl $ 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; 65132526Snjl struct acpi_video_output_queue 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 *); 78132526Snjlstatic struct acpi_video_output *acpi_video_vo_init(UINT32); 79126430Snjlstatic void acpi_video_vo_bind(struct acpi_video_output *, ACPI_HANDLE); 80126430Snjlstatic void acpi_video_vo_destroy(struct acpi_video_output *); 81126430Snjlstatic int acpi_video_vo_check_level(struct acpi_video_output *, int); 82126430Snjlstatic int acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS); 83126430Snjlstatic int acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS); 84126430Snjlstatic int acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS); 85126430Snjlstatic int acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS); 86126430Snjl 87126430Snjl/* operations */ 88126430Snjlstatic void vid_set_switch_policy(ACPI_HANDLE, UINT32); 89126430Snjlstatic int vid_enum_outputs(ACPI_HANDLE, 90132526Snjl void(*)(ACPI_HANDLE, UINT32, void *), void *); 91126430Snjlstatic int vo_query_brightness_levels(ACPI_HANDLE, int **); 92126430Snjlstatic void vo_set_brightness(ACPI_HANDLE, int); 93126430Snjlstatic UINT32 vo_get_device_status(ACPI_HANDLE); 94126430Snjlstatic UINT32 vo_query_graphics_state(ACPI_HANDLE); 95126430Snjlstatic void vo_set_device_state(ACPI_HANDLE, UINT32); 96126430Snjl 97126430Snjl/* events */ 98126430Snjl#define VID_NOTIFY_SWITCHED 0x80 99126430Snjl#define VID_NOTIFY_REPROBE 0x81 100126430Snjl 101126430Snjl/* _DOS (Enable/Disable Output Switching) argument bits */ 102132526Snjl#define DOS_SWITCH_MASK 3 103132526Snjl#define DOS_SWITCH_BY_OSPM 0 104132526Snjl#define DOS_SWITCH_BY_BIOS 1 105132526Snjl#define DOS_SWITCH_LOCKED 2 106132526Snjl#define DOS_BRIGHTNESS_BY_BIOS (1 << 2) 107126430Snjl 108126430Snjl/* _DOD and subdev's _ADR */ 109132526Snjl#define DOD_DEVID_MASK 0xffff 110132526Snjl#define DOD_DEVID_MONITOR 0x0100 111132526Snjl#define DOD_DEVID_PANEL 0x0110 112132526Snjl#define DOD_DEVID_TV 0x0200 113132526Snjl#define DOD_BIOS (1 << 16) 114132526Snjl#define DOD_NONVGA (1 << 17) 115126430Snjl#define DOD_HEAD_ID_SHIFT 18 116126430Snjl#define DOD_HEAD_ID_BITS 3 117126430Snjl#define DOD_HEAD_ID_MASK \ 118132526Snjl (((1 << DOD_HEAD_ID_BITS) - 1) << DOD_HEAD_ID_SHIFT) 119126430Snjl 120126430Snjl/* _BCL related constants */ 121126430Snjl#define BCL_FULLPOWER 0 122126430Snjl#define BCL_ECONOMY 1 123126430Snjl 124126430Snjl/* _DCS (Device Currrent Status) value bits and masks. */ 125132526Snjl#define DCS_EXISTS (1 << 0) 126132526Snjl#define DCS_ACTIVE (1 << 1) 127132526Snjl#define DCS_READY (1 << 2) 128132526Snjl#define DCS_FUNCTIONAL (1 << 3) 129132526Snjl#define DCS_ATTACHED (1 << 4) 130126430Snjl 131126430Snjl/* _DSS (Device Set Status) argument bits and masks. */ 132132526Snjl#define DSS_INACTIVE 0 133132526Snjl#define DSS_ACTIVE (1 << 0) 134132526Snjl#define DSS_ACTIVITY (1 << 0) 135132526Snjl#define DSS_SETNEXT (1 << 30) 136132526Snjl#define DSS_COMMIT (1 << 31) 137126430Snjl 138126430Snjlstatic device_method_t acpi_video_methods[] = { 139126430Snjl DEVMETHOD(device_probe, acpi_video_probe), 140126430Snjl DEVMETHOD(device_attach, acpi_video_attach), 141126430Snjl DEVMETHOD(device_detach, acpi_video_detach), 142126430Snjl DEVMETHOD(device_shutdown, acpi_video_shutdown), 143126430Snjl { 0, 0 } 144126430Snjl}; 145126430Snjl 146126430Snjlstatic driver_t acpi_video_driver = { 147126430Snjl "acpi_video", 148126430Snjl acpi_video_methods, 149126430Snjl sizeof(struct acpi_video_softc), 150126430Snjl}; 151126430Snjl 152126430Snjlstatic devclass_t acpi_video_devclass; 153126430Snjl 154132256SnjlDRIVER_MODULE(acpi_video, pci, acpi_video_driver, acpi_video_devclass, 155126430Snjl acpi_video_modevent, NULL); 156128036SnjlMODULE_DEPEND(acpi_video, acpi, 1, 1, 1); 157126430Snjl 158132526Snjlstatic struct sysctl_ctx_list acpi_video_sysctl_ctx; 159132526Snjlstatic struct sysctl_oid *acpi_video_sysctl_tree; 160132526Snjlstatic struct acpi_video_output_queue lcd_units, crt_units, tv_units, 161132526Snjl other_units; 162126430Snjl 163126430SnjlMALLOC_DEFINE(M_ACPIVIDEO, "acpivideo", "ACPI video extension"); 164126430Snjl 165126430Snjlstatic int 166126430Snjlacpi_video_modevent(struct module *mod __unused, int evt, void *cookie __unused) 167126430Snjl{ 168126430Snjl int err = 0; 169126430Snjl 170126430Snjl switch (evt) { 171126430Snjl case MOD_LOAD: 172126430Snjl acpi_video_sysctl_tree = NULL; 173126430Snjl sysctl_ctx_init(&acpi_video_sysctl_ctx); 174126430Snjl STAILQ_INIT(&lcd_units); 175126430Snjl STAILQ_INIT(&crt_units); 176126430Snjl STAILQ_INIT(&tv_units); 177126430Snjl STAILQ_INIT(&other_units); 178126430Snjl break; 179126430Snjl case MOD_UNLOAD: 180132256Snjl sysctl_ctx_free(&acpi_video_sysctl_ctx); 181126430Snjl acpi_video_sysctl_tree = NULL; 182126430Snjl break; 183126430Snjl default: 184126430Snjl err = EINVAL; 185126430Snjl } 186126430Snjl 187126430Snjl return (err); 188126430Snjl} 189126430Snjl 190126430Snjlstatic int 191126430Snjlacpi_video_probe(device_t dev) 192126430Snjl{ 193132526Snjl ACPI_HANDLE devh, h; 194132526Snjl ACPI_OBJECT_TYPE t_dos; 195126430Snjl 196132526Snjl devh = acpi_get_handle(dev); 197132526Snjl if (acpi_disabled("video") || 198132526Snjl ACPI_FAILURE(AcpiGetHandle(devh, "_DOD", &h)) || 199132526Snjl ACPI_FAILURE(AcpiGetHandle(devh, "_DOS", &h)) || 200132526Snjl ACPI_FAILURE(AcpiGetType(h, &t_dos)) || 201132526Snjl t_dos != ACPI_TYPE_METHOD) 202132526Snjl return (ENXIO); 203126430Snjl 204132526Snjl device_set_desc(dev, "ACPI video extension"); 205132526Snjl return (0); 206126430Snjl} 207126430Snjl 208126430Snjlstatic int 209126430Snjlacpi_video_attach(device_t dev) 210126430Snjl{ 211126430Snjl struct acpi_softc *acpi_sc; 212126430Snjl struct acpi_video_softc *sc; 213126430Snjl 214126430Snjl sc = device_get_softc(dev); 215126430Snjl 216132256Snjl acpi_sc = devclass_get_softc(devclass_find("acpi"), 0); 217126430Snjl if (acpi_video_sysctl_tree == NULL && acpi_sc != NULL) { 218126430Snjl acpi_video_sysctl_tree = SYSCTL_ADD_NODE(&acpi_video_sysctl_ctx, 219126430Snjl SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 220126430Snjl OID_AUTO, "video", CTLFLAG_RD, 0, 221126430Snjl "video extension control"); 222126430Snjl } 223126430Snjl 224126430Snjl sc->device = dev; 225126430Snjl sc->handle = acpi_get_handle(dev); 226126430Snjl STAILQ_INIT(&sc->vid_outputs); 227126430Snjl 228126430Snjl AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 229126430Snjl acpi_video_notify_handler, sc); 230126430Snjl sc->vid_pwr_evh = EVENTHANDLER_REGISTER(power_profile_change, 231126430Snjl acpi_video_power_profile, sc, 0); 232126430Snjl 233126430Snjl acpi_video_bind_outputs(sc); 234126430Snjl vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_OSPM); 235126430Snjl 236126430Snjl acpi_video_power_profile(sc); 237126430Snjl 238126430Snjl return (0); 239126430Snjl} 240126430Snjl 241126430Snjlstatic int 242126430Snjlacpi_video_detach(device_t dev) 243126430Snjl{ 244126430Snjl struct acpi_video_softc *sc; 245126430Snjl struct acpi_video_output *vo, *vn; 246126430Snjl 247126430Snjl sc = device_get_softc(dev); 248126430Snjl 249126430Snjl vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); 250126430Snjl EVENTHANDLER_DEREGISTER(power_profile_change, sc->vid_pwr_evh); 251126430Snjl AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 252126430Snjl acpi_video_notify_handler); 253126430Snjl 254126430Snjl for (vo = STAILQ_FIRST(&sc->vid_outputs); vo != NULL; vo = vn) { 255126430Snjl vn = STAILQ_NEXT(vo, vo_next); 256126430Snjl acpi_video_vo_destroy(vo); 257126430Snjl } 258126430Snjl 259126430Snjl return (0); 260126430Snjl} 261126430Snjl 262126430Snjlstatic int 263126430Snjlacpi_video_shutdown(device_t dev) 264126430Snjl{ 265126430Snjl struct acpi_video_softc *sc; 266126430Snjl 267126430Snjl sc = device_get_softc(dev); 268126430Snjl vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); 269126430Snjl 270126430Snjl return (0); 271126430Snjl} 272126430Snjl 273126430Snjlstatic void 274132526Snjlacpi_video_notify_handler(ACPI_HANDLE handle __unused, UINT32 notify, 275132526Snjl void *context) 276126430Snjl{ 277126430Snjl struct acpi_video_softc *sc; 278126430Snjl struct acpi_video_output *vo; 279126430Snjl ACPI_HANDLE lasthand = NULL; 280126430Snjl UINT32 dcs, dss, dss_p = 0; 281126430Snjl 282126430Snjl sc = context; 283126430Snjl 284126430Snjl switch (notify) { 285126430Snjl case VID_NOTIFY_SWITCHED: 286126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 287126430Snjl dss = vo_query_graphics_state(vo->handle); 288126430Snjl dcs = vo_get_device_status(vo->handle); 289126430Snjl if (!(dcs & DCS_READY)) 290126430Snjl dss = DSS_INACTIVE; 291126430Snjl if (((dcs & DCS_ACTIVE) && dss == DSS_INACTIVE) || 292126430Snjl (!(dcs & DCS_ACTIVE) && dss == DSS_ACTIVE)) { 293126430Snjl if (lasthand != NULL) 294126430Snjl vo_set_device_state(lasthand, dss_p); 295126430Snjl dss_p = dss; 296126430Snjl lasthand = vo->handle; 297126430Snjl } 298126430Snjl } 299126430Snjl if (lasthand != NULL) 300126430Snjl vo_set_device_state(lasthand, dss_p|DSS_COMMIT); 301126430Snjl break; 302126430Snjl case VID_NOTIFY_REPROBE: 303126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) 304126430Snjl vo->handle = NULL; 305126430Snjl acpi_video_bind_outputs(sc); 306126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 307126430Snjl if (vo->handle == NULL) { 308126430Snjl STAILQ_REMOVE(&sc->vid_outputs, vo, 309126430Snjl acpi_video_output, vo_next); 310126430Snjl acpi_video_vo_destroy(vo); 311126430Snjl } 312126430Snjl } 313126430Snjl break; 314126430Snjl default: 315126430Snjl device_printf(sc->device, 316126430Snjl "unknown notify event 0x%x\n", notify); 317126430Snjl } 318126430Snjl} 319126430Snjl 320126430Snjlstatic void 321126430Snjlacpi_video_power_profile(void *context) 322126430Snjl{ 323126430Snjl int state; 324126430Snjl struct acpi_video_softc *sc; 325126430Snjl struct acpi_video_output *vo; 326126430Snjl 327126430Snjl sc = context; 328126430Snjl state = power_profile_get_state(); 329126430Snjl if (state != POWER_PROFILE_PERFORMANCE && 330126430Snjl state != POWER_PROFILE_ECONOMY) 331126430Snjl return; 332126430Snjl 333126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 334126430Snjl if (vo->vo_levels != NULL && vo->vo_brightness == -1) 335126430Snjl vo_set_brightness(vo->handle, 336126430Snjl state == POWER_PROFILE_ECONOMY 337126430Snjl ? vo->vo_economy : vo->vo_fullpower); 338126430Snjl } 339126430Snjl} 340126430Snjl 341126430Snjlstatic void 342126430Snjlacpi_video_bind_outputs_subr(ACPI_HANDLE handle, UINT32 adr, void *context) 343126430Snjl{ 344126430Snjl struct acpi_video_softc *sc; 345126430Snjl struct acpi_video_output *vo; 346126430Snjl 347126430Snjl sc = context; 348126430Snjl 349126430Snjl STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { 350126430Snjl if (vo->adr == adr) { 351126430Snjl acpi_video_vo_bind(vo, handle); 352126430Snjl return; 353126430Snjl } 354126430Snjl } 355126430Snjl vo = acpi_video_vo_init(adr); 356126430Snjl if (vo != NULL) { 357126430Snjl acpi_video_vo_bind(vo, handle); 358126430Snjl STAILQ_INSERT_TAIL(&sc->vid_outputs, vo, vo_next); 359126430Snjl } 360126430Snjl} 361126430Snjl 362126430Snjlstatic void 363126430Snjlacpi_video_bind_outputs(struct acpi_video_softc *sc) 364126430Snjl{ 365126430Snjl 366126430Snjl vid_enum_outputs(sc->handle, acpi_video_bind_outputs_subr, sc); 367126430Snjl} 368126430Snjl 369126430Snjlstatic struct acpi_video_output * 370126430Snjlacpi_video_vo_init(UINT32 adr) 371126430Snjl{ 372126430Snjl struct acpi_video_output *vn, *vo, *vp; 373126430Snjl int n, x; 374126430Snjl char name[64], env[128]; 375126430Snjl const char *type, *desc; 376126430Snjl struct acpi_video_output_queue *voqh; 377126430Snjl 378126430Snjl switch (adr & DOD_DEVID_MASK) { 379126430Snjl case DOD_DEVID_MONITOR: 380126430Snjl desc = "CRT monitor"; 381126430Snjl type = "crt"; 382126430Snjl voqh = &crt_units; 383126430Snjl break; 384126430Snjl case DOD_DEVID_PANEL: 385126430Snjl desc = "LCD panel"; 386126430Snjl type = "lcd"; 387126430Snjl voqh = &lcd_units; 388126430Snjl break; 389126430Snjl case DOD_DEVID_TV: 390126430Snjl desc = "TV"; 391126430Snjl type = "tv"; 392126430Snjl voqh = &tv_units; 393126430Snjl break; 394126430Snjl default: 395126430Snjl desc = "unknown output"; 396126430Snjl type = "out"; 397126430Snjl voqh = &other_units; 398126430Snjl } 399126430Snjl 400126430Snjl n = 0; 401126430Snjl vn = vp = NULL; 402126430Snjl /* XXX - needs locking for protecting STAILQ xxx_units. */ 403126430Snjl STAILQ_FOREACH(vn, voqh, vo_unit.next) { 404126430Snjl if (vn->vo_unit.num != n) 405126430Snjl break; 406126430Snjl vp = vn; 407126430Snjl n++; 408126430Snjl } 409126430Snjl 410132526Snjl snprintf(name, sizeof(name), "%s%d", type, n); 411126430Snjl 412126430Snjl vo = malloc(sizeof(*vo), M_ACPIVIDEO, M_NOWAIT); 413126430Snjl if (vo != NULL) { 414126430Snjl vo->handle = NULL; 415126430Snjl vo->adr = adr; 416126430Snjl vo->vo_unit.num = n; 417126430Snjl vo->vo_brightness = -1; 418126430Snjl vo->vo_fullpower = -1; /* TODO: override with tunables */ 419126430Snjl vo->vo_economy = -1; 420126430Snjl vo->vo_numlevels = 0; 421126430Snjl vo->vo_levels = NULL; 422126430Snjl snprintf(env, 128, "hw.acpi.video.%s.fullpower", name); 423126430Snjl if (getenv_int(env, &x)) 424126430Snjl vo->vo_fullpower = x; 425126430Snjl snprintf(env, 128, "hw.acpi.video.%s.economy", name); 426126430Snjl if (getenv_int(env, &x)) 427126430Snjl vo->vo_economy = x; 428126430Snjl 429126430Snjl sysctl_ctx_init(&vo->vo_sysctl_ctx); 430126430Snjl if (vp != NULL) 431126430Snjl STAILQ_INSERT_AFTER(voqh, vp, vo, vo_unit.next); 432126430Snjl else 433126430Snjl STAILQ_INSERT_TAIL(voqh, vo, vo_unit.next); 434126430Snjl if (acpi_video_sysctl_tree != NULL) 435126430Snjl vo->vo_sysctl_tree = 436126430Snjl SYSCTL_ADD_NODE(&vo->vo_sysctl_ctx, 437132526Snjl SYSCTL_CHILDREN(acpi_video_sysctl_tree), 438132526Snjl OID_AUTO, name, CTLFLAG_RD, 0, desc); 439126430Snjl if (vo->vo_sysctl_tree != NULL) { 440126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 441132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 442132526Snjl OID_AUTO, "active", 443132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 0, 444132526Snjl acpi_video_vo_active_sysctl, "I", 445132526Snjl "current activity of this device"); 446126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 447132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 448132526Snjl OID_AUTO, "brightness", 449132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 0, 450132526Snjl acpi_video_vo_bright_sysctl, "I", 451132526Snjl "current brightness level"); 452126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 453132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 454132526Snjl OID_AUTO, "fullpower", 455132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 456132526Snjl POWER_PROFILE_PERFORMANCE, 457132526Snjl acpi_video_vo_presets_sysctl, "I", 458132526Snjl "preset level for full power mode"); 459126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 460132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 461132526Snjl OID_AUTO, "economy", 462132526Snjl CTLTYPE_INT|CTLFLAG_RW, vo, 463132526Snjl POWER_PROFILE_ECONOMY, 464132526Snjl acpi_video_vo_presets_sysctl, "I", 465132526Snjl "preset level for economy mode"); 466126430Snjl SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, 467132526Snjl SYSCTL_CHILDREN(vo->vo_sysctl_tree), 468132526Snjl OID_AUTO, "levels", 469132526Snjl CTLTYPE_OPAQUE|CTLFLAG_RD, vo, 0, 470132526Snjl acpi_video_vo_levels_sysctl, "I", 471132526Snjl "supported brightness levels"); 472126430Snjl } else 473126430Snjl printf("%s: sysctl node creation failed\n", type); 474126430Snjl } else 475126430Snjl printf("%s: softc allocation failed\n", type); 476126430Snjl 477126430Snjl /* XXX unlock here - needs locking for protecting STAILQ xxx_units. */ 478126430Snjl 479126430Snjl if (bootverbose) { 480132526Snjl printf("found %s(%x)", desc, adr & DOD_DEVID_MASK); 481126430Snjl if (adr & DOD_BIOS) 482126430Snjl printf(", detectable by BIOS"); 483126430Snjl if (adr & DOD_NONVGA) 484126430Snjl printf(" (not a VGA output)"); 485126430Snjl printf(", head #%d\n", 486132526Snjl (adr & DOD_HEAD_ID_MASK) >> DOD_HEAD_ID_SHIFT); 487126430Snjl } 488132526Snjl return (vo); 489126430Snjl} 490126430Snjl 491126430Snjlstatic void 492126430Snjlacpi_video_vo_bind(struct acpi_video_output *vo, ACPI_HANDLE handle) 493126430Snjl{ 494126430Snjl 495126430Snjl if (vo->vo_levels != NULL) 496126430Snjl AcpiOsFree(vo->vo_levels); 497126430Snjl vo->handle = handle; 498132526Snjl vo->vo_numlevels = vo_query_brightness_levels(handle, &vo->vo_levels); 499126430Snjl if (vo->vo_numlevels >= 2) { 500126430Snjl if (vo->vo_fullpower == -1 501126430Snjl || acpi_video_vo_check_level(vo, vo->vo_fullpower) != 0) 502126430Snjl /* XXX - can't deal with rebinding... */ 503126430Snjl vo->vo_fullpower = vo->vo_levels[BCL_FULLPOWER]; 504126430Snjl if (vo->vo_economy == -1 505126430Snjl || acpi_video_vo_check_level(vo, vo->vo_economy) != 0) 506126430Snjl /* XXX - see above. */ 507126430Snjl vo->vo_economy = vo->vo_levels[BCL_ECONOMY]; 508126430Snjl } 509126430Snjl} 510126430Snjl 511126430Snjlstatic void 512126430Snjlacpi_video_vo_destroy(struct acpi_video_output *vo) 513126430Snjl{ 514126430Snjl struct acpi_video_output_queue *voqh; 515126430Snjl 516126430Snjl 517126430Snjl if (vo->vo_sysctl_tree != NULL) { 518126430Snjl vo->vo_sysctl_tree = NULL; 519126430Snjl sysctl_ctx_free(&vo->vo_sysctl_ctx); 520126430Snjl } 521126430Snjl if (vo->vo_levels != NULL) 522126430Snjl AcpiOsFree(vo->vo_levels); 523126430Snjl 524126430Snjl switch (vo->adr & DOD_DEVID_MASK) { 525126430Snjl case DOD_DEVID_MONITOR: 526126430Snjl voqh = &crt_units; 527126430Snjl break; 528126430Snjl case DOD_DEVID_PANEL: 529126430Snjl voqh = &lcd_units; 530126430Snjl break; 531126430Snjl case DOD_DEVID_TV: 532126430Snjl voqh = &tv_units; 533126430Snjl break; 534126430Snjl default: 535126430Snjl voqh = &other_units; 536126430Snjl } 537126430Snjl /* XXX - needs locking for protecting STAILQ xxx_units. */ 538126430Snjl STAILQ_REMOVE(voqh, vo, acpi_video_output, vo_unit.next); 539126430Snjl free(vo, M_ACPIVIDEO); 540126430Snjl} 541126430Snjl 542126430Snjlstatic int 543126430Snjlacpi_video_vo_check_level(struct acpi_video_output *vo, int level) 544126430Snjl{ 545126430Snjl int i; 546126430Snjl 547126430Snjl if (vo->vo_levels == NULL) 548126430Snjl return (ENODEV); 549126430Snjl for (i = 0; i < vo->vo_numlevels; i++) 550126430Snjl if (vo->vo_levels[i] == level) 551126430Snjl return (0); 552126430Snjl return (EINVAL); 553126430Snjl} 554126430Snjl 555126430Snjl/* ARGSUSED */ 556126430Snjlstatic int 557126430Snjlacpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS) 558126430Snjl{ 559126430Snjl struct acpi_video_output *vo; 560126430Snjl int state, err; 561126430Snjl 562126430Snjl vo = (struct acpi_video_output *)arg1; 563126430Snjl if (vo->handle == NULL) { 564126430Snjl err = ENXIO; 565126430Snjl goto out; 566126430Snjl } 567126430Snjl state = vo_get_device_status(vo->handle) & DCS_ACTIVE? 1 : 0; 568126430Snjl err = sysctl_handle_int(oidp, &state, 0, req); 569126430Snjl if (err != 0 || req->newptr == NULL) 570126430Snjl goto out; 571126430Snjl vo_set_device_state(vo->handle, 572126430Snjl DSS_COMMIT | (state? DSS_ACTIVE : DSS_INACTIVE)); 573126430Snjlout: 574126430Snjl return (err); 575126430Snjl} 576126430Snjl 577126430Snjl/* ARGSUSED */ 578126430Snjlstatic int 579126430Snjlacpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS) 580126430Snjl{ 581126430Snjl struct acpi_video_output *vo; 582126430Snjl int level, preset, err; 583126430Snjl 584126430Snjl vo = (struct acpi_video_output *)arg1; 585126430Snjl if (vo->handle == NULL) { 586126430Snjl err = ENXIO; 587126430Snjl goto out; 588126430Snjl } 589126430Snjl if (vo->vo_levels == NULL) { 590126430Snjl err = ENODEV; 591126430Snjl goto out; 592126430Snjl } 593126430Snjl 594132526Snjl preset = (power_profile_get_state() == POWER_PROFILE_ECONOMY) ? 595132526Snjl vo->vo_economy : vo->vo_fullpower; 596126430Snjl level = vo->vo_brightness; 597126430Snjl if (level == -1) 598126430Snjl level = preset; 599126430Snjl 600126430Snjl err = sysctl_handle_int(oidp, &level, 0, req); 601126430Snjl if (err != 0 || req->newptr == NULL) 602126430Snjl goto out; 603126430Snjl if (level < -1 || level > 100) { 604126430Snjl err = EINVAL; 605126430Snjl goto out; 606126430Snjl } 607126430Snjl 608126430Snjl if (level != -1 && (err = acpi_video_vo_check_level(vo, level))) 609126430Snjl goto out; 610126430Snjl vo->vo_brightness = level; 611126430Snjl vo_set_brightness(vo->handle, level == -1? preset : level); 612126430Snjlout: 613126430Snjl return (err); 614126430Snjl} 615126430Snjl 616126430Snjlstatic int 617126430Snjlacpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS) 618126430Snjl{ 619126430Snjl struct acpi_video_output *vo; 620126430Snjl int level, *preset, err = 0; 621126430Snjl 622126430Snjl vo = (struct acpi_video_output *)arg1; 623126430Snjl if (vo->handle == NULL) { 624126430Snjl err = ENXIO; 625126430Snjl goto out; 626126430Snjl } 627126430Snjl if (vo->vo_levels == NULL) { 628126430Snjl err = ENODEV; 629126430Snjl goto out; 630126430Snjl } 631132526Snjl preset = (arg2 == POWER_PROFILE_ECONOMY) ? 632132526Snjl &vo->vo_economy : &vo->vo_fullpower; 633126430Snjl level = *preset; 634126430Snjl err = sysctl_handle_int(oidp, &level, 0, req); 635126430Snjl if (err != 0 || req->newptr == NULL) 636126430Snjl goto out; 637126430Snjl if (level < -1 || level > 100) { 638126430Snjl err = EINVAL; 639126430Snjl goto out; 640126430Snjl } 641126430Snjl if (level == -1) 642126430Snjl level = vo->vo_levels 643132526Snjl [(arg2 == POWER_PROFILE_ECONOMY) ? 644132526Snjl BCL_ECONOMY : BCL_FULLPOWER]; 645126430Snjl else if ((err = acpi_video_vo_check_level(vo, level)) != 0) 646126430Snjl goto out; 647126430Snjl 648126430Snjl if (vo->vo_brightness == -1 && (power_profile_get_state() == arg2)) 649126430Snjl vo_set_brightness(vo->handle, level); 650126430Snjl *preset = level; 651126430Snjlout: 652126430Snjl return (err); 653126430Snjl} 654126430Snjl 655126430Snjl/* ARGSUSED */ 656126430Snjlstatic int 657126430Snjlacpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS) 658126430Snjl{ 659126430Snjl struct acpi_video_output *vo; 660126430Snjl int err; 661126430Snjl 662126430Snjl vo = (struct acpi_video_output *)arg1; 663126430Snjl if (vo->vo_levels == NULL) { 664126430Snjl err = ENODEV; 665126430Snjl goto out; 666126430Snjl } 667126430Snjl if (req->newptr != NULL) { 668126430Snjl err = EPERM; 669126430Snjl goto out; 670126430Snjl } 671126430Snjl err = sysctl_handle_opaque(oidp, vo->vo_levels, 672132526Snjl vo->vo_numlevels * sizeof *vo->vo_levels, req); 673126430Snjlout: 674126430Snjl return (err); 675126430Snjl} 676126430Snjl 677126430Snjlstatic void 678126430Snjlvid_set_switch_policy(ACPI_HANDLE handle, UINT32 policy) 679126430Snjl{ 680126430Snjl ACPI_STATUS status; 681126430Snjl 682126560Snjl status = acpi_SetInteger(handle, "_DOS", policy); 683126430Snjl if (ACPI_FAILURE(status)) 684126430Snjl printf("can't evaluate %s._DOS - %s\n", 685126430Snjl acpi_name(handle), AcpiFormatException(status)); 686126430Snjl} 687126430Snjl 688126430Snjlstruct enum_callback_arg { 689126430Snjl void (*callback)(ACPI_HANDLE, UINT32, void *); 690126430Snjl void *context; 691126430Snjl ACPI_OBJECT *dod_pkg; 692126430Snjl}; 693126430Snjl 694126430Snjlstatic ACPI_STATUS 695126430Snjlvid_enum_outputs_subr(ACPI_HANDLE handle, UINT32 level __unused, 696126430Snjl void *context, void **retp) 697126430Snjl{ 698126430Snjl ACPI_STATUS status; 699126430Snjl ACPI_OBJECT *tmp; 700126430Snjl UINT32 adr; 701126430Snjl struct enum_callback_arg *argset; 702126430Snjl size_t i; 703126430Snjl 704126430Snjl argset = context; 705126560Snjl status = acpi_GetInteger(handle, "_ADR", &adr); 706126430Snjl if (ACPI_SUCCESS(status)) { 707126430Snjl for (i = 0; i < argset->dod_pkg->Package.Count; i++) { 708126430Snjl tmp = &argset->dod_pkg->Package.Elements[i]; 709126430Snjl if (tmp != NULL && tmp->Type == ACPI_TYPE_INTEGER && 710126430Snjl (tmp->Integer.Value & DOD_DEVID_MASK) == adr) { 711126430Snjl argset->callback(handle, tmp->Integer.Value, 712126430Snjl argset->context); 713126430Snjl (**(int**)retp)++; 714126430Snjl } 715126430Snjl } 716126430Snjl } 717126430Snjl 718126430Snjl return (AE_OK); 719126430Snjl} 720126430Snjl 721126430Snjlstatic int 722126430Snjlvid_enum_outputs(ACPI_HANDLE handle, 723126430Snjl void (*callback)(ACPI_HANDLE, UINT32, void *), void *context) 724126430Snjl{ 725126430Snjl ACPI_STATUS status; 726126430Snjl ACPI_BUFFER dod_buf; 727126430Snjl ACPI_OBJECT *res; 728126430Snjl int num = 0; 729126430Snjl void *pnum; 730126430Snjl struct enum_callback_arg argset; 731126430Snjl 732126430Snjl dod_buf.Length = ACPI_ALLOCATE_BUFFER; 733126430Snjl dod_buf.Pointer = NULL; 734126430Snjl status = AcpiEvaluateObject(handle, "_DOD", NULL, &dod_buf); 735126430Snjl if (ACPI_FAILURE(status)) { 736126430Snjl if (status != AE_NOT_FOUND) 737126430Snjl printf("can't evaluate %s._DOD - %s\n", 738126430Snjl acpi_name(handle), AcpiFormatException(status)); 739126430Snjl num = -1; 740126430Snjl goto out; 741126430Snjl } 742126430Snjl res = (ACPI_OBJECT *)dod_buf.Pointer; 743126430Snjl if (res == NULL || res->Type != ACPI_TYPE_PACKAGE) { 744126430Snjl printf("evaluation of %s._DOD makes no sense\n", 745126430Snjl acpi_name(handle)); 746126430Snjl num = -1; 747126430Snjl goto out; 748126430Snjl } 749126430Snjl if (callback == NULL) { 750126430Snjl num = res->Package.Count; 751126430Snjl goto out; 752126430Snjl } 753126430Snjl argset.callback = callback; 754126430Snjl argset.context = context; 755126430Snjl argset.dod_pkg = res; 756126430Snjl pnum = # 757126430Snjl status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1, 758126430Snjl vid_enum_outputs_subr, &argset, 759126430Snjl &pnum); 760126430Snjl if (ACPI_FAILURE(status)) 761126430Snjl printf("failed walking down %s - %s\n", 762126430Snjl acpi_name(handle), AcpiFormatException(status)); 763126430Snjlout: 764126430Snjl if (dod_buf.Pointer != NULL) 765126430Snjl AcpiOsFree(dod_buf.Pointer); 766126430Snjl return (num); 767126430Snjl} 768126430Snjl 769126430Snjlstatic int 770126430Snjlvo_query_brightness_levels(ACPI_HANDLE handle, int **levelp) 771126430Snjl{ 772126430Snjl ACPI_STATUS status; 773126430Snjl ACPI_BUFFER bcl_buf; 774126430Snjl ACPI_OBJECT *res, *tmp; 775126430Snjl int num = 0, i, n, *levels; 776126430Snjl 777126430Snjl bcl_buf.Length = ACPI_ALLOCATE_BUFFER; 778126430Snjl bcl_buf.Pointer = NULL; 779126430Snjl status = AcpiEvaluateObject(handle, "_BCL", NULL, &bcl_buf); 780126430Snjl if (ACPI_FAILURE(status)) { 781126430Snjl if (status != AE_NOT_FOUND) 782126430Snjl printf("can't evaluate %s._BCL - %s\n", 783126430Snjl acpi_name(handle), AcpiFormatException(status)); 784126430Snjl num = -1; 785126430Snjl goto out; 786126430Snjl } 787126430Snjl res = (ACPI_OBJECT *)bcl_buf.Pointer; 788126430Snjl if (res == NULL || res->Type != ACPI_TYPE_PACKAGE || 789126430Snjl res->Package.Count < 2) { 790126430Snjl printf("evaluation of %s._BCL makes no sense\n", 791126430Snjl acpi_name(handle)); 792126430Snjl num = -1; 793126430Snjl goto out; 794126430Snjl } 795126430Snjl num = res->Package.Count; 796126430Snjl if (levelp == NULL) 797126430Snjl goto out; 798126430Snjl levels = AcpiOsAllocate(num * sizeof *levels); 799126430Snjl if (levels == NULL) { 800126430Snjl num = -1; 801126430Snjl goto out; 802126430Snjl } 803126430Snjl for (i = 0, n = 0; i < num; i++) { 804126430Snjl tmp = &res->Package.Elements[i]; 805126430Snjl if (tmp != NULL && tmp->Type == ACPI_TYPE_INTEGER) 806126430Snjl levels[n++] = tmp->Integer.Value; 807126430Snjl } 808126430Snjl if (n < 2) { 809126430Snjl num = -1; 810126430Snjl AcpiOsFree(levels); 811126430Snjl } else { 812126430Snjl num = n; 813126430Snjl *levelp = levels; 814126430Snjl } 815126430Snjlout: 816126430Snjl if (bcl_buf.Pointer != NULL) 817126430Snjl AcpiOsFree(bcl_buf.Pointer); 818126430Snjl 819126430Snjl return (num); 820126430Snjl} 821126430Snjl 822126430Snjlstatic void 823126430Snjlvo_set_brightness(ACPI_HANDLE handle, int level) 824126430Snjl{ 825126430Snjl ACPI_STATUS status; 826126430Snjl 827126560Snjl status = acpi_SetInteger(handle, "_BCM", level); 828126430Snjl if (ACPI_FAILURE(status)) 829126430Snjl printf("can't evaluate %s._BCM - %s\n", 830126430Snjl acpi_name(handle), AcpiFormatException(status)); 831126430Snjl} 832126430Snjl 833126430Snjlstatic UINT32 834126430Snjlvo_get_device_status(ACPI_HANDLE handle) 835126430Snjl{ 836126430Snjl UINT32 dcs = 0; 837126430Snjl ACPI_STATUS status; 838126430Snjl 839126560Snjl status = acpi_GetInteger(handle, "_DCS", &dcs); 840126430Snjl if (ACPI_FAILURE(status)) 841126430Snjl printf("can't evaluate %s._DCS - %s\n", 842126430Snjl acpi_name(handle), AcpiFormatException(status)); 843126430Snjl 844126430Snjl return (dcs); 845126430Snjl} 846126430Snjl 847126430Snjlstatic UINT32 848126430Snjlvo_query_graphics_state(ACPI_HANDLE handle) 849126430Snjl{ 850126430Snjl UINT32 dgs = 0; 851126430Snjl ACPI_STATUS status; 852126430Snjl 853126560Snjl status = acpi_GetInteger(handle, "_DGS", &dgs); 854126430Snjl if (ACPI_FAILURE(status)) 855126430Snjl printf("can't evaluate %s._DGS - %s\n", 856126430Snjl acpi_name(handle), AcpiFormatException(status)); 857126430Snjl 858126430Snjl return (dgs); 859126430Snjl} 860126430Snjl 861126430Snjlstatic void 862126430Snjlvo_set_device_state(ACPI_HANDLE handle, UINT32 state) 863126430Snjl{ 864126430Snjl ACPI_STATUS status; 865126430Snjl 866126560Snjl status = acpi_SetInteger(handle, "_DSS", state); 867126430Snjl if (ACPI_FAILURE(status)) 868126430Snjl printf("can't evaluate %s._DSS - %s\n", 869126430Snjl acpi_name(handle), AcpiFormatException(status)); 870126430Snjl} 871