dpms.c revision 197025
1182081Sjhb/*- 2182081Sjhb * Copyright (c) 2008 Yahoo!, Inc. 3182081Sjhb * All rights reserved. 4182081Sjhb * Written by: John Baldwin <jhb@FreeBSD.org> 5182081Sjhb * 6182081Sjhb * Redistribution and use in source and binary forms, with or without 7182081Sjhb * modification, are permitted provided that the following conditions 8182081Sjhb * are met: 9182081Sjhb * 1. Redistributions of source code must retain the above copyright 10182081Sjhb * notice, this list of conditions and the following disclaimer. 11182081Sjhb * 2. Redistributions in binary form must reproduce the above copyright 12182081Sjhb * notice, this list of conditions and the following disclaimer in the 13182081Sjhb * documentation and/or other materials provided with the distribution. 14182081Sjhb * 3. Neither the name of the author nor the names of any co-contributors 15182081Sjhb * may be used to endorse or promote products derived from this software 16182081Sjhb * without specific prior written permission. 17182081Sjhb * 18182081Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19182081Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20182081Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21182081Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22182081Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23182081Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24182081Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25182081Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26182081Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27182081Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28182081Sjhb * SUCH DAMAGE. 29182081Sjhb */ 30182081Sjhb 31182081Sjhb/* 32182081Sjhb * Copyright (c) 2004 Benjamin Close <Benjamin.Close@clearchain.com> 33182081Sjhb * All rights reserved. 34182081Sjhb * 35182081Sjhb * Redistribution and use in source and binary forms, with or without 36182081Sjhb * modification, are permitted provided that the following conditions 37182081Sjhb * are met: 38182081Sjhb * 1. Redistributions of source code must retain the above copyright 39182081Sjhb * notice, this list of conditions and the following disclaimer. 40182081Sjhb * 2. Redistributions in binary form must reproduce the above copyright 41182081Sjhb * notice, this list of conditions and the following disclaimer in the 42182081Sjhb * documentation and/or other materials provided with the distribution. 43182081Sjhb * 44182081Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 45182081Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 46182081Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 47182081Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 48182081Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 49182081Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 50182081Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 51182081Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 52182081Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 53182081Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 54182081Sjhb * SUCH DAMAGE. 55182081Sjhb */ 56182081Sjhb 57182081Sjhb/* 58182081Sjhb * Support for managing the display via DPMS for suspend/resume. 59182081Sjhb */ 60182081Sjhb 61182081Sjhb#include <sys/cdefs.h> 62182081Sjhb__FBSDID("$FreeBSD: head/sys/dev/dpms/dpms.c 197025 2009-09-09 09:50:31Z delphij $"); 63182081Sjhb 64182081Sjhb#include <sys/param.h> 65182081Sjhb#include <sys/bus.h> 66182081Sjhb#include <sys/kernel.h> 67182081Sjhb#include <sys/libkern.h> 68182081Sjhb#include <sys/module.h> 69182081Sjhb 70197025Sdelphij#include <vm/vm.h> 71197025Sdelphij#include <vm/pmap.h> 72182081Sjhb 73197025Sdelphij#include <contrib/x86emu/x86emu.h> 74197025Sdelphij#include <contrib/x86emu/x86emu_regs.h> 75197025Sdelphij 76182081Sjhb/* 77182081Sjhb * VESA DPMS States 78182081Sjhb */ 79182081Sjhb#define DPMS_ON 0x00 80182081Sjhb#define DPMS_STANDBY 0x01 81182081Sjhb#define DPMS_SUSPEND 0x02 82182081Sjhb#define DPMS_OFF 0x04 83182081Sjhb#define DPMS_REDUCEDON 0x08 84182081Sjhb 85182081Sjhb#define VBE_DPMS_FUNCTION 0x4F10 86182081Sjhb#define VBE_DPMS_GET_SUPPORTED_STATES 0x00 87182081Sjhb#define VBE_DPMS_GET_STATE 0x02 88182081Sjhb#define VBE_DPMS_SET_STATE 0x01 89182081Sjhb#define VBE_MAJORVERSION_MASK 0x0F 90182081Sjhb#define VBE_MINORVERSION_MASK 0xF0 91182081Sjhb 92182081Sjhbstruct dpms_softc { 93182081Sjhb int dpms_supported_states; 94182081Sjhb int dpms_initial_state; 95182081Sjhb}; 96182081Sjhb 97197025Sdelphijstatic struct x86emu vesa_emu; 98197025Sdelphijstatic unsigned char *emumem = NULL; 99197025Sdelphij 100182081Sjhbstatic int dpms_attach(device_t); 101182081Sjhbstatic int dpms_detach(device_t); 102182081Sjhbstatic int dpms_get_supported_states(int *); 103182081Sjhbstatic int dpms_get_current_state(int *); 104182081Sjhbstatic void dpms_identify(driver_t *, device_t); 105182081Sjhbstatic int dpms_probe(device_t); 106182081Sjhbstatic int dpms_resume(device_t); 107182081Sjhbstatic int dpms_set_state(int); 108182081Sjhbstatic int dpms_suspend(device_t); 109182081Sjhb 110182081Sjhbstatic device_method_t dpms_methods[] = { 111182081Sjhb DEVMETHOD(device_identify, dpms_identify), 112182081Sjhb DEVMETHOD(device_probe, dpms_probe), 113182081Sjhb DEVMETHOD(device_attach, dpms_attach), 114182081Sjhb DEVMETHOD(device_detach, dpms_detach), 115182081Sjhb DEVMETHOD(device_suspend, dpms_suspend), 116182081Sjhb DEVMETHOD(device_resume, dpms_resume), 117182081Sjhb { 0, 0 } 118182081Sjhb}; 119182081Sjhb 120182081Sjhbstatic driver_t dpms_driver = { 121182081Sjhb "dpms", 122182081Sjhb dpms_methods, 123182081Sjhb sizeof(struct dpms_softc), 124182081Sjhb}; 125182081Sjhb 126182081Sjhbstatic devclass_t dpms_devclass; 127182081Sjhb 128182081SjhbDRIVER_MODULE(dpms, vgapci, dpms_driver, dpms_devclass, NULL, NULL); 129197025SdelphijMODULE_DEPEND(dpms, x86emu, 1, 1, 1); 130182081Sjhb 131197025Sdelphijstatic uint8_t 132197025Sdelphijvm86_emu_inb(struct x86emu *emu, uint16_t port) 133197025Sdelphij{ 134197025Sdelphij if (port == 0xb2) /* APM scratch register */ 135197025Sdelphij return 0; 136197025Sdelphij if (port >= 0x80 && port < 0x88) /* POST status register */ 137197025Sdelphij return 0; 138197025Sdelphij return inb(port); 139197025Sdelphij} 140197025Sdelphij 141197025Sdelphijstatic uint16_t 142197025Sdelphijvm86_emu_inw(struct x86emu *emu, uint16_t port) 143197025Sdelphij{ 144197025Sdelphij if (port >= 0x80 && port < 0x88) /* POST status register */ 145197025Sdelphij return 0; 146197025Sdelphij return inw(port); 147197025Sdelphij} 148197025Sdelphij 149197025Sdelphijstatic uint32_t 150197025Sdelphijvm86_emu_inl(struct x86emu *emu, uint16_t port) 151197025Sdelphij{ 152197025Sdelphij if (port >= 0x80 && port < 0x88) /* POST status register */ 153197025Sdelphij return 0; 154197025Sdelphij return inl(port); 155197025Sdelphij} 156197025Sdelphij 157182081Sjhbstatic void 158197025Sdelphijvm86_emu_outb(struct x86emu *emu, uint16_t port, uint8_t val) 159197025Sdelphij{ 160197025Sdelphij if (port == 0xb2) /* APM scratch register */ 161197025Sdelphij return; 162197025Sdelphij if (port >= 0x80 && port < 0x88) /* POST status register */ 163197025Sdelphij return; 164197025Sdelphij outb(port, val); 165197025Sdelphij} 166197025Sdelphij 167197025Sdelphijstatic void 168197025Sdelphijvm86_emu_outw(struct x86emu *emu, uint16_t port, uint16_t val) 169197025Sdelphij{ 170197025Sdelphij if (port >= 0x80 && port < 0x88) /* POST status register */ 171197025Sdelphij return; 172197025Sdelphij outw(port, val); 173197025Sdelphij} 174197025Sdelphij 175197025Sdelphijstatic void 176197025Sdelphijvm86_emu_outl(struct x86emu *emu, uint16_t port, uint32_t val) 177197025Sdelphij{ 178197025Sdelphij if (port >= 0x80 && port < 0x88) /* POST status register */ 179197025Sdelphij return; 180197025Sdelphij outl(port, val); 181197025Sdelphij} 182197025Sdelphij 183197025Sdelphijstatic void 184182081Sjhbdpms_identify(driver_t *driver, device_t parent) 185182081Sjhb{ 186182081Sjhb 187182081Sjhb /* 188182081Sjhb * XXX: The DPMS VBE only allows for manipulating a single 189182081Sjhb * monitor, but we don't know which one. Just attach to the 190182081Sjhb * first vgapci(4) device we encounter and hope it is the 191182081Sjhb * right one. 192182081Sjhb */ 193182081Sjhb if (devclass_get_device(dpms_devclass, 0) == NULL) 194182081Sjhb device_add_child(parent, "dpms", 0); 195197025Sdelphij 196182081Sjhb} 197182081Sjhb 198182081Sjhbstatic int 199182081Sjhbdpms_probe(device_t dev) 200182081Sjhb{ 201182081Sjhb int error, states; 202182081Sjhb 203197025Sdelphij emumem = pmap_mapbios(0x0, 0xc00000); 204197025Sdelphij 205197025Sdelphij memset(&vesa_emu, 0, sizeof(vesa_emu)); 206197025Sdelphij x86emu_init_default(&vesa_emu); 207197025Sdelphij 208197025Sdelphij vesa_emu.emu_inb = vm86_emu_inb; 209197025Sdelphij vesa_emu.emu_inw = vm86_emu_inw; 210197025Sdelphij vesa_emu.emu_inl = vm86_emu_inl; 211197025Sdelphij vesa_emu.emu_outb = vm86_emu_outb; 212197025Sdelphij vesa_emu.emu_outw = vm86_emu_outw; 213197025Sdelphij vesa_emu.emu_outl = vm86_emu_outl; 214197025Sdelphij 215197025Sdelphij vesa_emu.mem_base = (char *)emumem; 216197025Sdelphij vesa_emu.mem_size = 1024 * 1024; 217197025Sdelphij 218182081Sjhb error = dpms_get_supported_states(&states); 219182081Sjhb if (error) 220182081Sjhb return (error); 221182081Sjhb device_set_desc(dev, "DPMS suspend/resume"); 222182081Sjhb device_quiet(dev); 223182081Sjhb return (BUS_PROBE_DEFAULT); 224182081Sjhb} 225182081Sjhb 226182081Sjhbstatic int 227182081Sjhbdpms_attach(device_t dev) 228182081Sjhb{ 229182081Sjhb struct dpms_softc *sc; 230182081Sjhb int error; 231182081Sjhb 232182081Sjhb sc = device_get_softc(dev); 233182081Sjhb error = dpms_get_supported_states(&sc->dpms_supported_states); 234182081Sjhb if (error) 235182081Sjhb return (error); 236182081Sjhb error = dpms_get_current_state(&sc->dpms_initial_state); 237182081Sjhb return (error); 238182081Sjhb} 239182081Sjhb 240182081Sjhbstatic int 241182081Sjhbdpms_detach(device_t dev) 242182081Sjhb{ 243197025Sdelphij if (emumem) 244197025Sdelphij pmap_unmapdev((vm_offset_t)emumem, 0xc00000); 245182081Sjhb 246182081Sjhb return (0); 247182081Sjhb} 248182081Sjhb 249182081Sjhbstatic int 250182081Sjhbdpms_suspend(device_t dev) 251182081Sjhb{ 252182081Sjhb 253182081Sjhb dpms_set_state(DPMS_OFF); 254182081Sjhb return (0); 255182081Sjhb} 256182081Sjhb 257182081Sjhbstatic int 258182081Sjhbdpms_resume(device_t dev) 259182081Sjhb{ 260182081Sjhb struct dpms_softc *sc; 261182081Sjhb 262182081Sjhb sc = device_get_softc(dev); 263182081Sjhb dpms_set_state(sc->dpms_initial_state); 264182081Sjhb return (0); 265182081Sjhb} 266182081Sjhb 267182081Sjhbstatic int 268182081Sjhbdpms_call_bios(int subfunction, int *bh) 269182081Sjhb{ 270197025Sdelphij vesa_emu.x86.R_AX = VBE_DPMS_FUNCTION; 271197025Sdelphij vesa_emu.x86.R_BL = subfunction; 272197025Sdelphij vesa_emu.x86.R_BH = *bh; 273197025Sdelphij vesa_emu.x86.R_ES = 0; 274197025Sdelphij vesa_emu.x86.R_DI = 0; 275197025Sdelphij x86emu_exec_intr(&vesa_emu, 0x10); 276182081Sjhb 277197025Sdelphij if ((vesa_emu.x86.R_EAX & 0xffff) != 0x004f) 278197025Sdelphij return (ENXIO); 279197025Sdelphij 280197025Sdelphij *bh = vesa_emu.x86.R_BH; 281197025Sdelphij 282197025Sdelphij return (0); 283182081Sjhb} 284182081Sjhb 285182081Sjhbstatic int 286182081Sjhbdpms_get_supported_states(int *states) 287182081Sjhb{ 288182081Sjhb 289182081Sjhb *states = 0; 290182081Sjhb return (dpms_call_bios(VBE_DPMS_GET_SUPPORTED_STATES, states)); 291182081Sjhb} 292182081Sjhb 293182081Sjhbstatic int 294182081Sjhbdpms_get_current_state(int *state) 295182081Sjhb{ 296182081Sjhb 297182081Sjhb *state = 0; 298182081Sjhb return (dpms_call_bios(VBE_DPMS_GET_STATE, state)); 299182081Sjhb} 300182081Sjhb 301182081Sjhbstatic int 302182081Sjhbdpms_set_state(int state) 303182081Sjhb{ 304182081Sjhb 305182081Sjhb return (dpms_call_bios(VBE_DPMS_SET_STATE, &state)); 306182081Sjhb} 307