dpms.c revision 197025
1/*- 2 * Copyright (c) 2008 Yahoo!, Inc. 3 * All rights reserved. 4 * Written by: John Baldwin <jhb@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the author nor the names of any co-contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31/* 32 * Copyright (c) 2004 Benjamin Close <Benjamin.Close@clearchain.com> 33 * All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 44 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 47 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 54 * SUCH DAMAGE. 55 */ 56 57/* 58 * Support for managing the display via DPMS for suspend/resume. 59 */ 60 61#include <sys/cdefs.h> 62__FBSDID("$FreeBSD: head/sys/dev/dpms/dpms.c 197025 2009-09-09 09:50:31Z delphij $"); 63 64#include <sys/param.h> 65#include <sys/bus.h> 66#include <sys/kernel.h> 67#include <sys/libkern.h> 68#include <sys/module.h> 69 70#include <vm/vm.h> 71#include <vm/pmap.h> 72 73#include <contrib/x86emu/x86emu.h> 74#include <contrib/x86emu/x86emu_regs.h> 75 76/* 77 * VESA DPMS States 78 */ 79#define DPMS_ON 0x00 80#define DPMS_STANDBY 0x01 81#define DPMS_SUSPEND 0x02 82#define DPMS_OFF 0x04 83#define DPMS_REDUCEDON 0x08 84 85#define VBE_DPMS_FUNCTION 0x4F10 86#define VBE_DPMS_GET_SUPPORTED_STATES 0x00 87#define VBE_DPMS_GET_STATE 0x02 88#define VBE_DPMS_SET_STATE 0x01 89#define VBE_MAJORVERSION_MASK 0x0F 90#define VBE_MINORVERSION_MASK 0xF0 91 92struct dpms_softc { 93 int dpms_supported_states; 94 int dpms_initial_state; 95}; 96 97static struct x86emu vesa_emu; 98static unsigned char *emumem = NULL; 99 100static int dpms_attach(device_t); 101static int dpms_detach(device_t); 102static int dpms_get_supported_states(int *); 103static int dpms_get_current_state(int *); 104static void dpms_identify(driver_t *, device_t); 105static int dpms_probe(device_t); 106static int dpms_resume(device_t); 107static int dpms_set_state(int); 108static int dpms_suspend(device_t); 109 110static device_method_t dpms_methods[] = { 111 DEVMETHOD(device_identify, dpms_identify), 112 DEVMETHOD(device_probe, dpms_probe), 113 DEVMETHOD(device_attach, dpms_attach), 114 DEVMETHOD(device_detach, dpms_detach), 115 DEVMETHOD(device_suspend, dpms_suspend), 116 DEVMETHOD(device_resume, dpms_resume), 117 { 0, 0 } 118}; 119 120static driver_t dpms_driver = { 121 "dpms", 122 dpms_methods, 123 sizeof(struct dpms_softc), 124}; 125 126static devclass_t dpms_devclass; 127 128DRIVER_MODULE(dpms, vgapci, dpms_driver, dpms_devclass, NULL, NULL); 129MODULE_DEPEND(dpms, x86emu, 1, 1, 1); 130 131static uint8_t 132vm86_emu_inb(struct x86emu *emu, uint16_t port) 133{ 134 if (port == 0xb2) /* APM scratch register */ 135 return 0; 136 if (port >= 0x80 && port < 0x88) /* POST status register */ 137 return 0; 138 return inb(port); 139} 140 141static uint16_t 142vm86_emu_inw(struct x86emu *emu, uint16_t port) 143{ 144 if (port >= 0x80 && port < 0x88) /* POST status register */ 145 return 0; 146 return inw(port); 147} 148 149static uint32_t 150vm86_emu_inl(struct x86emu *emu, uint16_t port) 151{ 152 if (port >= 0x80 && port < 0x88) /* POST status register */ 153 return 0; 154 return inl(port); 155} 156 157static void 158vm86_emu_outb(struct x86emu *emu, uint16_t port, uint8_t val) 159{ 160 if (port == 0xb2) /* APM scratch register */ 161 return; 162 if (port >= 0x80 && port < 0x88) /* POST status register */ 163 return; 164 outb(port, val); 165} 166 167static void 168vm86_emu_outw(struct x86emu *emu, uint16_t port, uint16_t val) 169{ 170 if (port >= 0x80 && port < 0x88) /* POST status register */ 171 return; 172 outw(port, val); 173} 174 175static void 176vm86_emu_outl(struct x86emu *emu, uint16_t port, uint32_t val) 177{ 178 if (port >= 0x80 && port < 0x88) /* POST status register */ 179 return; 180 outl(port, val); 181} 182 183static void 184dpms_identify(driver_t *driver, device_t parent) 185{ 186 187 /* 188 * XXX: The DPMS VBE only allows for manipulating a single 189 * monitor, but we don't know which one. Just attach to the 190 * first vgapci(4) device we encounter and hope it is the 191 * right one. 192 */ 193 if (devclass_get_device(dpms_devclass, 0) == NULL) 194 device_add_child(parent, "dpms", 0); 195 196} 197 198static int 199dpms_probe(device_t dev) 200{ 201 int error, states; 202 203 emumem = pmap_mapbios(0x0, 0xc00000); 204 205 memset(&vesa_emu, 0, sizeof(vesa_emu)); 206 x86emu_init_default(&vesa_emu); 207 208 vesa_emu.emu_inb = vm86_emu_inb; 209 vesa_emu.emu_inw = vm86_emu_inw; 210 vesa_emu.emu_inl = vm86_emu_inl; 211 vesa_emu.emu_outb = vm86_emu_outb; 212 vesa_emu.emu_outw = vm86_emu_outw; 213 vesa_emu.emu_outl = vm86_emu_outl; 214 215 vesa_emu.mem_base = (char *)emumem; 216 vesa_emu.mem_size = 1024 * 1024; 217 218 error = dpms_get_supported_states(&states); 219 if (error) 220 return (error); 221 device_set_desc(dev, "DPMS suspend/resume"); 222 device_quiet(dev); 223 return (BUS_PROBE_DEFAULT); 224} 225 226static int 227dpms_attach(device_t dev) 228{ 229 struct dpms_softc *sc; 230 int error; 231 232 sc = device_get_softc(dev); 233 error = dpms_get_supported_states(&sc->dpms_supported_states); 234 if (error) 235 return (error); 236 error = dpms_get_current_state(&sc->dpms_initial_state); 237 return (error); 238} 239 240static int 241dpms_detach(device_t dev) 242{ 243 if (emumem) 244 pmap_unmapdev((vm_offset_t)emumem, 0xc00000); 245 246 return (0); 247} 248 249static int 250dpms_suspend(device_t dev) 251{ 252 253 dpms_set_state(DPMS_OFF); 254 return (0); 255} 256 257static int 258dpms_resume(device_t dev) 259{ 260 struct dpms_softc *sc; 261 262 sc = device_get_softc(dev); 263 dpms_set_state(sc->dpms_initial_state); 264 return (0); 265} 266 267static int 268dpms_call_bios(int subfunction, int *bh) 269{ 270 vesa_emu.x86.R_AX = VBE_DPMS_FUNCTION; 271 vesa_emu.x86.R_BL = subfunction; 272 vesa_emu.x86.R_BH = *bh; 273 vesa_emu.x86.R_ES = 0; 274 vesa_emu.x86.R_DI = 0; 275 x86emu_exec_intr(&vesa_emu, 0x10); 276 277 if ((vesa_emu.x86.R_EAX & 0xffff) != 0x004f) 278 return (ENXIO); 279 280 *bh = vesa_emu.x86.R_BH; 281 282 return (0); 283} 284 285static int 286dpms_get_supported_states(int *states) 287{ 288 289 *states = 0; 290 return (dpms_call_bios(VBE_DPMS_GET_SUPPORTED_STATES, states)); 291} 292 293static int 294dpms_get_current_state(int *state) 295{ 296 297 *state = 0; 298 return (dpms_call_bios(VBE_DPMS_GET_STATE, state)); 299} 300 301static int 302dpms_set_state(int state) 303{ 304 305 return (dpms_call_bios(VBE_DPMS_SET_STATE, &state)); 306} 307