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$"); 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 92 struct dpms_softc { 93 int dpms_supported_states; 94 int dpms_initial_state; 95 }; 96 97 static struct x86emu vesa_emu; 98 static unsigned char *emumem = NULL; 99 100 static int dpms_attach(device_t); 101 static int dpms_detach(device_t); 102 static int dpms_get_supported_states(int *); 103 static int dpms_get_current_state(int *); 104 static void dpms_identify(driver_t *, device_t); 105 static int dpms_probe(device_t); 106 static int dpms_resume(device_t); 107 static int dpms_set_state(int); 108 static int dpms_suspend(device_t); 109 110 static 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 120 static driver_t dpms_driver = { 121 "dpms", 122 dpms_methods, 123 sizeof(struct dpms_softc), 124 }; 125 126 static devclass_t dpms_devclass; 127 128 DRIVER_MODULE(dpms, vgapci, dpms_driver, dpms_devclass, NULL, NULL); 129 MODULE_DEPEND(dpms, x86emu, 1, 1, 1); 130 131 static uint8_t 132 vm86_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 141 static uint16_t 142 vm86_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 149 static uint32_t 150 vm86_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 157 static void 158 vm86_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 167 static void 168 vm86_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 175 static void 176 vm86_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 183 static void 184 dpms_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 198 static int 199 dpms_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 226 static int 227 dpms_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 240 static int 241 dpms_detach(device_t dev) 242 { 243 if (emumem) 244 pmap_unmapdev((vm_offset_t)emumem, 0xc00000); 245 246 return (0); 247 } 248 249 static int 250 dpms_suspend(device_t dev) 251 { 252 253 dpms_set_state(DPMS_OFF); 254 return (0); 255 } 256 257 static int 258 dpms_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 267 static int 268 dpms_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 285 static int 286 dpms_get_supported_states(int *states) 287 { 288 289 *states = 0; 290 return (dpms_call_bios(VBE_DPMS_GET_SUPPORTED_STATES, states)); 291 } 292 293 static int 294 dpms_get_current_state(int *state) 295 { 296 297 *state = 0; 298 return (dpms_call_bios(VBE_DPMS_GET_STATE, state)); 299 } 300 301 static int 302 dpms_set_state(int state) 303 { 304 305 return (dpms_call_bios(VBE_DPMS_SET_STATE, &state)); 306 } 307