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