acpi_video.c revision 203936
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 */
28126430Snjl
29143002Sobrien#include <sys/cdefs.h>
30143002Sobrien__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_video.c 203936 2010-02-15 20:46:01Z jkim $");
31143002Sobrien
32126430Snjl#include <sys/param.h>
33126430Snjl#include <sys/kernel.h>
34126430Snjl#include <sys/malloc.h>
35129879Sphk#include <sys/module.h>
36126430Snjl#include <sys/bus.h>
37126430Snjl#include <sys/power.h>
38126430Snjl#include <sys/queue.h>
39126430Snjl#include <sys/sysctl.h>
40126430Snjl
41193530Sjkim#include <contrib/dev/acpica/include/acpi.h>
42193530Sjkim
43126430Snjl#include <dev/acpica/acpivar.h>
44126430Snjl
45126430Snjl/* ACPI video extension driver. */
46126430Snjlstruct acpi_video_output {
47126430Snjl	ACPI_HANDLE	handle;
48126430Snjl	UINT32		adr;
49126430Snjl	STAILQ_ENTRY(acpi_video_output) vo_next;
50126430Snjl	struct {
51126430Snjl		int	num;
52126430Snjl		STAILQ_ENTRY(acpi_video_output) next;
53126430Snjl	} vo_unit;
54126430Snjl	int		vo_brightness;
55126430Snjl	int		vo_fullpower;
56126430Snjl	int		vo_economy;
57126430Snjl	int		vo_numlevels;
58126430Snjl	int		*vo_levels;
59126430Snjl	struct sysctl_ctx_list vo_sysctl_ctx;
60126430Snjl	struct sysctl_oid *vo_sysctl_tree;
61126430Snjl};
62126430Snjl
63126430SnjlSTAILQ_HEAD(acpi_video_output_queue, acpi_video_output);
64126430Snjl
65126430Snjlstruct acpi_video_softc {
66126430Snjl	device_t		device;
67126430Snjl	ACPI_HANDLE		handle;
68132526Snjl	struct acpi_video_output_queue vid_outputs;
69126430Snjl	eventhandler_tag	vid_pwr_evh;
70126430Snjl};
71126430Snjl
72126430Snjl/* interfaces */
73126430Snjlstatic int	acpi_video_modevent(struct module*, int, void *);
74153578Sjhbstatic void	acpi_video_identify(driver_t *driver, device_t parent);
75126430Snjlstatic int	acpi_video_probe(device_t);
76126430Snjlstatic int	acpi_video_attach(device_t);
77126430Snjlstatic int	acpi_video_detach(device_t);
78126430Snjlstatic int	acpi_video_shutdown(device_t);
79126430Snjlstatic void	acpi_video_notify_handler(ACPI_HANDLE, UINT32, void *);
80126430Snjlstatic void	acpi_video_power_profile(void *);
81126430Snjlstatic void	acpi_video_bind_outputs(struct acpi_video_softc *);
82132526Snjlstatic struct acpi_video_output *acpi_video_vo_init(UINT32);
83126430Snjlstatic void	acpi_video_vo_bind(struct acpi_video_output *, ACPI_HANDLE);
84126430Snjlstatic void	acpi_video_vo_destroy(struct acpi_video_output *);
85126430Snjlstatic int	acpi_video_vo_check_level(struct acpi_video_output *, int);
86203810Sjkimstatic void	acpi_video_vo_notify_handler(ACPI_HANDLE, UINT32, void *);
87126430Snjlstatic int	acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS);
88126430Snjlstatic int	acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS);
89126430Snjlstatic int	acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS);
90126430Snjlstatic int	acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS);
91126430Snjl
92126430Snjl/* operations */
93126430Snjlstatic void	vid_set_switch_policy(ACPI_HANDLE, UINT32);
94126430Snjlstatic int	vid_enum_outputs(ACPI_HANDLE,
95132526Snjl		    void(*)(ACPI_HANDLE, UINT32, void *), void *);
96133625Snjlstatic int	vo_get_brightness_levels(ACPI_HANDLE, int **);
97203810Sjkimstatic int	vo_get_brightness(ACPI_HANDLE);
98126430Snjlstatic void	vo_set_brightness(ACPI_HANDLE, int);
99126430Snjlstatic UINT32	vo_get_device_status(ACPI_HANDLE);
100133625Snjlstatic UINT32	vo_get_graphics_state(ACPI_HANDLE);
101126430Snjlstatic void	vo_set_device_state(ACPI_HANDLE, UINT32);
102126430Snjl
103126430Snjl/* events */
104203936Sjkim#define	VID_NOTIFY_SWITCHED	0x80
105203936Sjkim#define	VID_NOTIFY_REPROBE	0x81
106203936Sjkim#define	VID_NOTIFY_CYCLE_BRN	0x85
107203810Sjkim#define	VID_NOTIFY_INC_BRN	0x86
108203810Sjkim#define	VID_NOTIFY_DEC_BRN	0x87
109203936Sjkim#define	VID_NOTIFY_ZERO_BRN	0x88
110126430Snjl
111126430Snjl/* _DOS (Enable/Disable Output Switching) argument bits */
112203936Sjkim#define	DOS_SWITCH_MASK		3
113203936Sjkim#define	DOS_SWITCH_BY_OSPM	0
114203936Sjkim#define	DOS_SWITCH_BY_BIOS	1
115203936Sjkim#define	DOS_SWITCH_LOCKED	2
116203936Sjkim#define	DOS_BRIGHTNESS_BY_OSPM	(1 << 2)
117126430Snjl
118126430Snjl/* _DOD and subdev's _ADR */
119203936Sjkim#define	DOD_DEVID_MASK		0x0f00
120203936Sjkim#define	DOD_DEVID_MASK_FULL	0xffff
121203936Sjkim#define	DOD_DEVID_MASK_DISPIDX	0x000f
122203936Sjkim#define	DOD_DEVID_MASK_DISPPORT	0x00f0
123203936Sjkim#define	DOD_DEVID_MONITOR	0x0100
124203936Sjkim#define	DOD_DEVID_LCD		0x0110
125203936Sjkim#define	DOD_DEVID_TV		0x0200
126203936Sjkim#define	DOD_DEVID_EXT		0x0300
127203936Sjkim#define	DOD_DEVID_INTDFP	0x0400
128203936Sjkim#define	DOD_BIOS		(1 << 16)
129203936Sjkim#define	DOD_NONVGA		(1 << 17)
130203936Sjkim#define	DOD_HEAD_ID_SHIFT	18
131203936Sjkim#define	DOD_HEAD_ID_BITS	3
132203936Sjkim#define	DOD_HEAD_ID_MASK \
133132526Snjl		(((1 << DOD_HEAD_ID_BITS) - 1) << DOD_HEAD_ID_SHIFT)
134203936Sjkim#define	DOD_DEVID_SCHEME_STD	(1 << 31)
135126430Snjl
136126430Snjl/* _BCL related constants */
137203936Sjkim#define	BCL_FULLPOWER		0
138203936Sjkim#define	BCL_ECONOMY		1
139126430Snjl
140126430Snjl/* _DCS (Device Currrent Status) value bits and masks. */
141203936Sjkim#define	DCS_EXISTS		(1 << 0)
142203936Sjkim#define	DCS_ACTIVE		(1 << 1)
143203936Sjkim#define	DCS_READY		(1 << 2)
144203936Sjkim#define	DCS_FUNCTIONAL		(1 << 3)
145203936Sjkim#define	DCS_ATTACHED		(1 << 4)
146126430Snjl
147126430Snjl/* _DSS (Device Set Status) argument bits and masks. */
148203936Sjkim#define	DSS_INACTIVE		0
149203936Sjkim#define	DSS_ACTIVE		(1 << 0)
150203936Sjkim#define	DSS_SETNEXT		(1 << 30)
151203936Sjkim#define	DSS_COMMIT		(1 << 31)
152126430Snjl
153126430Snjlstatic device_method_t acpi_video_methods[] = {
154153578Sjhb	DEVMETHOD(device_identify, acpi_video_identify),
155126430Snjl	DEVMETHOD(device_probe, acpi_video_probe),
156126430Snjl	DEVMETHOD(device_attach, acpi_video_attach),
157126430Snjl	DEVMETHOD(device_detach, acpi_video_detach),
158126430Snjl	DEVMETHOD(device_shutdown, acpi_video_shutdown),
159126430Snjl	{ 0, 0 }
160126430Snjl};
161126430Snjl
162126430Snjlstatic driver_t acpi_video_driver = {
163126430Snjl	"acpi_video",
164126430Snjl	acpi_video_methods,
165126430Snjl	sizeof(struct acpi_video_softc),
166126430Snjl};
167126430Snjl
168126430Snjlstatic devclass_t acpi_video_devclass;
169126430Snjl
170153578SjhbDRIVER_MODULE(acpi_video, vgapci, acpi_video_driver, acpi_video_devclass,
171126430Snjl	      acpi_video_modevent, NULL);
172128036SnjlMODULE_DEPEND(acpi_video, acpi, 1, 1, 1);
173126430Snjl
174132526Snjlstatic struct sysctl_ctx_list	acpi_video_sysctl_ctx;
175132526Snjlstatic struct sysctl_oid	*acpi_video_sysctl_tree;
176161184Sbrunostatic struct acpi_video_output_queue crt_units, tv_units,
177161184Sbruno    ext_units, lcd_units, other_units;
178126430Snjl
179197648Sjhb/*
180197648Sjhb * The 'video' lock protects the hierarchy of video output devices
181197648Sjhb * (the video "bus").  The 'video_output' lock protects per-output
182197648Sjhb * data is equivalent to a softc lock for each video output.
183197648Sjhb */
184133625SnjlACPI_SERIAL_DECL(video, "ACPI video");
185197648SjhbACPI_SERIAL_DECL(video_output, "ACPI video output");
186126430SnjlMALLOC_DEFINE(M_ACPIVIDEO, "acpivideo", "ACPI video extension");
187126430Snjl
188126430Snjlstatic int
189126430Snjlacpi_video_modevent(struct module *mod __unused, int evt, void *cookie __unused)
190126430Snjl{
191137438Snjl	int err;
192126430Snjl
193137438Snjl	err = 0;
194126430Snjl	switch (evt) {
195126430Snjl	case MOD_LOAD:
196126430Snjl		sysctl_ctx_init(&acpi_video_sysctl_ctx);
197126430Snjl		STAILQ_INIT(&crt_units);
198126430Snjl		STAILQ_INIT(&tv_units);
199161184Sbruno		STAILQ_INIT(&ext_units);
200161184Sbruno		STAILQ_INIT(&lcd_units);
201126430Snjl		STAILQ_INIT(&other_units);
202126430Snjl		break;
203126430Snjl	case MOD_UNLOAD:
204132256Snjl		sysctl_ctx_free(&acpi_video_sysctl_ctx);
205126430Snjl		acpi_video_sysctl_tree = NULL;
206126430Snjl		break;
207126430Snjl	default:
208126430Snjl		err = EINVAL;
209126430Snjl	}
210126430Snjl
211126430Snjl	return (err);
212126430Snjl}
213126430Snjl
214153578Sjhbstatic void
215153578Sjhbacpi_video_identify(driver_t *driver, device_t parent)
216153578Sjhb{
217153578Sjhb
218153578Sjhb	if (device_find_child(parent, "acpi_video", -1) == NULL)
219153578Sjhb		device_add_child(parent, "acpi_video", -1);
220153578Sjhb}
221153578Sjhb
222126430Snjlstatic int
223126430Snjlacpi_video_probe(device_t dev)
224126430Snjl{
225132526Snjl	ACPI_HANDLE devh, h;
226132526Snjl	ACPI_OBJECT_TYPE t_dos;
227126430Snjl
228132526Snjl	devh = acpi_get_handle(dev);
229132526Snjl	if (acpi_disabled("video") ||
230132526Snjl	    ACPI_FAILURE(AcpiGetHandle(devh, "_DOD", &h)) ||
231132526Snjl	    ACPI_FAILURE(AcpiGetHandle(devh, "_DOS", &h)) ||
232132526Snjl	    ACPI_FAILURE(AcpiGetType(h, &t_dos)) ||
233132526Snjl	    t_dos != ACPI_TYPE_METHOD)
234132526Snjl		return (ENXIO);
235126430Snjl
236132526Snjl	device_set_desc(dev, "ACPI video extension");
237132526Snjl	return (0);
238126430Snjl}
239126430Snjl
240126430Snjlstatic int
241126430Snjlacpi_video_attach(device_t dev)
242126430Snjl{
243126430Snjl	struct acpi_softc *acpi_sc;
244126430Snjl	struct acpi_video_softc *sc;
245126430Snjl
246126430Snjl	sc = device_get_softc(dev);
247126430Snjl
248132256Snjl	acpi_sc = devclass_get_softc(devclass_find("acpi"), 0);
249132605Snjl	if (acpi_sc == NULL)
250132605Snjl		return (ENXIO);
251197648Sjhb	ACPI_SERIAL_BEGIN(video);
252132605Snjl	if (acpi_video_sysctl_tree == NULL) {
253126430Snjl		acpi_video_sysctl_tree = SYSCTL_ADD_NODE(&acpi_video_sysctl_ctx,
254126430Snjl				    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
255126430Snjl				    OID_AUTO, "video", CTLFLAG_RD, 0,
256126430Snjl				    "video extension control");
257126430Snjl	}
258197648Sjhb	ACPI_SERIAL_END(video);
259126430Snjl
260126430Snjl	sc->device = dev;
261126430Snjl	sc->handle = acpi_get_handle(dev);
262126430Snjl	STAILQ_INIT(&sc->vid_outputs);
263126430Snjl
264126430Snjl	AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
265126430Snjl				 acpi_video_notify_handler, sc);
266126430Snjl	sc->vid_pwr_evh = EVENTHANDLER_REGISTER(power_profile_change,
267126430Snjl				 acpi_video_power_profile, sc, 0);
268126430Snjl
269133625Snjl	ACPI_SERIAL_BEGIN(video);
270126430Snjl	acpi_video_bind_outputs(sc);
271133625Snjl	ACPI_SERIAL_END(video);
272126430Snjl
273137438Snjl	/*
274137438Snjl	 * Notify the BIOS that we want to switch both active outputs and
275137438Snjl	 * brightness levels.
276137438Snjl	 */
277137438Snjl	vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_OSPM |
278203936Sjkim	    DOS_BRIGHTNESS_BY_OSPM);
279137438Snjl
280126430Snjl	acpi_video_power_profile(sc);
281126430Snjl
282126430Snjl	return (0);
283126430Snjl}
284126430Snjl
285126430Snjlstatic int
286126430Snjlacpi_video_detach(device_t dev)
287126430Snjl{
288126430Snjl	struct acpi_video_softc *sc;
289126430Snjl	struct acpi_video_output *vo, *vn;
290126430Snjl
291126430Snjl	sc = device_get_softc(dev);
292126430Snjl
293126430Snjl	vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS);
294126430Snjl	EVENTHANDLER_DEREGISTER(power_profile_change, sc->vid_pwr_evh);
295126430Snjl	AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
296126430Snjl				acpi_video_notify_handler);
297126430Snjl
298133625Snjl	ACPI_SERIAL_BEGIN(video);
299197438Sjhb	STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vn) {
300126430Snjl		acpi_video_vo_destroy(vo);
301126430Snjl	}
302133625Snjl	ACPI_SERIAL_END(video);
303126430Snjl
304126430Snjl	return (0);
305126430Snjl}
306126430Snjl
307126430Snjlstatic int
308126430Snjlacpi_video_shutdown(device_t dev)
309126430Snjl{
310126430Snjl	struct acpi_video_softc *sc;
311126430Snjl
312126430Snjl	sc = device_get_softc(dev);
313126430Snjl	vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS);
314126430Snjl
315126430Snjl	return (0);
316126430Snjl}
317126430Snjl
318126430Snjlstatic void
319137438Snjlacpi_video_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context)
320126430Snjl{
321126430Snjl	struct acpi_video_softc *sc;
322132605Snjl	struct acpi_video_output *vo, *vo_tmp;
323137438Snjl	ACPI_HANDLE lasthand;
324137438Snjl	UINT32 dcs, dss, dss_p;
325126430Snjl
326137438Snjl	sc = (struct acpi_video_softc *)context;
327126430Snjl
328126430Snjl	switch (notify) {
329126430Snjl	case VID_NOTIFY_SWITCHED:
330137438Snjl		dss_p = 0;
331137438Snjl		lasthand = NULL;
332133625Snjl		ACPI_SERIAL_BEGIN(video);
333197648Sjhb		ACPI_SERIAL_BEGIN(video_output);
334126430Snjl		STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) {
335133625Snjl			dss = vo_get_graphics_state(vo->handle);
336126430Snjl			dcs = vo_get_device_status(vo->handle);
337126430Snjl			if (!(dcs & DCS_READY))
338126430Snjl				dss = DSS_INACTIVE;
339126430Snjl			if (((dcs & DCS_ACTIVE) && dss == DSS_INACTIVE) ||
340126430Snjl			    (!(dcs & DCS_ACTIVE) && dss == DSS_ACTIVE)) {
341126430Snjl				if (lasthand != NULL)
342126430Snjl					vo_set_device_state(lasthand, dss_p);
343126430Snjl				dss_p = dss;
344126430Snjl				lasthand = vo->handle;
345126430Snjl			}
346126430Snjl		}
347126430Snjl		if (lasthand != NULL)
348126430Snjl			vo_set_device_state(lasthand, dss_p|DSS_COMMIT);
349197648Sjhb		ACPI_SERIAL_END(video_output);
350133625Snjl		ACPI_SERIAL_END(video);
351126430Snjl		break;
352126430Snjl	case VID_NOTIFY_REPROBE:
353133625Snjl		ACPI_SERIAL_BEGIN(video);
354126430Snjl		STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next)
355126430Snjl			vo->handle = NULL;
356126430Snjl		acpi_video_bind_outputs(sc);
357132605Snjl		STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vo_tmp) {
358126430Snjl			if (vo->handle == NULL) {
359126430Snjl				STAILQ_REMOVE(&sc->vid_outputs, vo,
360137438Snjl				    acpi_video_output, vo_next);
361126430Snjl				acpi_video_vo_destroy(vo);
362126430Snjl			}
363126430Snjl		}
364133625Snjl		ACPI_SERIAL_END(video);
365126430Snjl		break;
366126430Snjl	default:
367137438Snjl		device_printf(sc->device, "unknown notify event 0x%x\n",
368137438Snjl		    notify);
369126430Snjl	}
370126430Snjl}
371126430Snjl
372126430Snjlstatic void
373126430Snjlacpi_video_power_profile(void *context)
374126430Snjl{
375126430Snjl	int state;
376126430Snjl	struct acpi_video_softc *sc;
377126430Snjl	struct acpi_video_output *vo;
378126430Snjl
379126430Snjl	sc = context;
380126430Snjl	state = power_profile_get_state();
381126430Snjl	if (state != POWER_PROFILE_PERFORMANCE &&
382126430Snjl	    state != POWER_PROFILE_ECONOMY)
383126430Snjl		return;
384126430Snjl
385133625Snjl	ACPI_SERIAL_BEGIN(video);
386197648Sjhb	ACPI_SERIAL_BEGIN(video_output);
387126430Snjl	STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) {
388126430Snjl		if (vo->vo_levels != NULL && vo->vo_brightness == -1)
389126430Snjl			vo_set_brightness(vo->handle,
390137438Snjl			    state == POWER_PROFILE_ECONOMY ?
391137438Snjl			    vo->vo_economy : vo->vo_fullpower);
392126430Snjl	}
393197648Sjhb	ACPI_SERIAL_END(video_output);
394133625Snjl	ACPI_SERIAL_END(video);
395126430Snjl}
396126430Snjl
397126430Snjlstatic void
398126430Snjlacpi_video_bind_outputs_subr(ACPI_HANDLE handle, UINT32 adr, void *context)
399126430Snjl{
400126430Snjl	struct acpi_video_softc *sc;
401126430Snjl	struct acpi_video_output *vo;
402126430Snjl
403133625Snjl	ACPI_SERIAL_ASSERT(video);
404126430Snjl	sc = context;
405126430Snjl
406126430Snjl	STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) {
407126430Snjl		if (vo->adr == adr) {
408126430Snjl			acpi_video_vo_bind(vo, handle);
409126430Snjl			return;
410126430Snjl		}
411126430Snjl	}
412126430Snjl	vo = acpi_video_vo_init(adr);
413126430Snjl	if (vo != NULL) {
414126430Snjl		acpi_video_vo_bind(vo, handle);
415126430Snjl		STAILQ_INSERT_TAIL(&sc->vid_outputs, vo, vo_next);
416126430Snjl	}
417126430Snjl}
418126430Snjl
419126430Snjlstatic void
420126430Snjlacpi_video_bind_outputs(struct acpi_video_softc *sc)
421126430Snjl{
422126430Snjl
423133625Snjl	ACPI_SERIAL_ASSERT(video);
424126430Snjl	vid_enum_outputs(sc->handle, acpi_video_bind_outputs_subr, sc);
425126430Snjl}
426126430Snjl
427126430Snjlstatic struct acpi_video_output *
428126430Snjlacpi_video_vo_init(UINT32 adr)
429126430Snjl{
430126430Snjl	struct acpi_video_output *vn, *vo, *vp;
431126430Snjl	int n, x;
432161184Sbruno	int display_index;
433161184Sbruno	int display_port;
434137438Snjl	char name[8], env[32];
435126430Snjl	const char *type, *desc;
436126430Snjl	struct acpi_video_output_queue *voqh;
437126430Snjl
438133625Snjl	ACPI_SERIAL_ASSERT(video);
439161184Sbruno	display_index = adr & DOD_DEVID_MASK_DISPIDX;
440161184Sbruno	display_port = (adr & DOD_DEVID_MASK_DISPPORT) >> 4;
441161184Sbruno
442126430Snjl	switch (adr & DOD_DEVID_MASK) {
443126430Snjl	case DOD_DEVID_MONITOR:
444161184Sbruno		if ((adr & DOD_DEVID_MASK_FULL) == DOD_DEVID_LCD) {
445161184Sbruno			/* DOD_DEVID_LCD is a common, backward compatible ID */
446161184Sbruno			desc = "Internal/Integrated Digital Flat Panel";
447161184Sbruno			type = "lcd";
448161184Sbruno			voqh = &lcd_units;
449161184Sbruno		} else {
450161184Sbruno			desc = "VGA CRT or VESA Compatible Analog Monitor";
451161184Sbruno			type = "crt";
452161184Sbruno			voqh = &crt_units;
453161184Sbruno		}
454126430Snjl		break;
455126430Snjl	case DOD_DEVID_TV:
456161184Sbruno		desc = "TV/HDTV or Analog-Video Monitor";
457126430Snjl		type = "tv";
458126430Snjl		voqh = &tv_units;
459126430Snjl		break;
460161184Sbruno	case DOD_DEVID_EXT:
461161184Sbruno		desc = "External Digital Monitor";
462161184Sbruno		type = "ext";
463161184Sbruno		voqh = &ext_units;
464161184Sbruno		break;
465161184Sbruno	case DOD_DEVID_INTDFP:
466161184Sbruno		desc = "Internal/Integrated Digital Flat Panel";
467161184Sbruno		type = "lcd";
468161184Sbruno		voqh = &lcd_units;
469161184Sbruno		break;
470126430Snjl	default:
471126430Snjl		desc = "unknown output";
472126430Snjl		type = "out";
473126430Snjl		voqh = &other_units;
474126430Snjl	}
475126430Snjl
476126430Snjl	n = 0;
477126430Snjl	vn = vp = NULL;
478126430Snjl	STAILQ_FOREACH(vn, voqh, vo_unit.next) {
479126430Snjl		if (vn->vo_unit.num != n)
480126430Snjl			break;
481126430Snjl		vp = vn;
482126430Snjl		n++;
483126430Snjl	}
484126430Snjl
485132526Snjl	snprintf(name, sizeof(name), "%s%d", type, n);
486126430Snjl
487126430Snjl	vo = malloc(sizeof(*vo), M_ACPIVIDEO, M_NOWAIT);
488126430Snjl	if (vo != NULL) {
489126430Snjl		vo->handle = NULL;
490126430Snjl		vo->adr = adr;
491126430Snjl		vo->vo_unit.num = n;
492126430Snjl		vo->vo_brightness = -1;
493126430Snjl		vo->vo_fullpower = -1;	/* TODO: override with tunables */
494126430Snjl		vo->vo_economy = -1;
495126430Snjl		vo->vo_numlevels = 0;
496126430Snjl		vo->vo_levels = NULL;
497137438Snjl		snprintf(env, sizeof(env), "hw.acpi.video.%s.fullpower", name);
498126430Snjl		if (getenv_int(env, &x))
499126430Snjl			vo->vo_fullpower = x;
500137438Snjl		snprintf(env, sizeof(env), "hw.acpi.video.%s.economy", name);
501126430Snjl		if (getenv_int(env, &x))
502126430Snjl			vo->vo_economy = x;
503126430Snjl
504126430Snjl		sysctl_ctx_init(&vo->vo_sysctl_ctx);
505126430Snjl		if (vp != NULL)
506126430Snjl			STAILQ_INSERT_AFTER(voqh, vp, vo, vo_unit.next);
507126430Snjl		else
508126430Snjl			STAILQ_INSERT_TAIL(voqh, vo, vo_unit.next);
509126430Snjl		if (acpi_video_sysctl_tree != NULL)
510126430Snjl			vo->vo_sysctl_tree =
511126430Snjl			    SYSCTL_ADD_NODE(&vo->vo_sysctl_ctx,
512132526Snjl				SYSCTL_CHILDREN(acpi_video_sysctl_tree),
513132526Snjl				OID_AUTO, name, CTLFLAG_RD, 0, desc);
514126430Snjl		if (vo->vo_sysctl_tree != NULL) {
515126430Snjl			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
516132526Snjl			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
517132526Snjl			    OID_AUTO, "active",
518132526Snjl			    CTLTYPE_INT|CTLFLAG_RW, vo, 0,
519132526Snjl			    acpi_video_vo_active_sysctl, "I",
520132526Snjl			    "current activity of this device");
521126430Snjl			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
522132526Snjl			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
523132526Snjl			    OID_AUTO, "brightness",
524132526Snjl			    CTLTYPE_INT|CTLFLAG_RW, vo, 0,
525132526Snjl			    acpi_video_vo_bright_sysctl, "I",
526132526Snjl			    "current brightness level");
527126430Snjl			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
528132526Snjl			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
529132526Snjl			    OID_AUTO, "fullpower",
530132526Snjl			    CTLTYPE_INT|CTLFLAG_RW, vo,
531132526Snjl			    POWER_PROFILE_PERFORMANCE,
532132526Snjl			    acpi_video_vo_presets_sysctl, "I",
533132526Snjl			    "preset level for full power mode");
534126430Snjl			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
535132526Snjl			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
536132526Snjl			    OID_AUTO, "economy",
537132526Snjl			    CTLTYPE_INT|CTLFLAG_RW, vo,
538132526Snjl			    POWER_PROFILE_ECONOMY,
539132526Snjl			    acpi_video_vo_presets_sysctl, "I",
540132526Snjl			    "preset level for economy mode");
541126430Snjl			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
542132526Snjl			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
543132526Snjl			    OID_AUTO, "levels",
544132526Snjl			    CTLTYPE_OPAQUE|CTLFLAG_RD, vo, 0,
545132526Snjl			    acpi_video_vo_levels_sysctl, "I",
546132526Snjl			    "supported brightness levels");
547126430Snjl		} else
548126430Snjl			printf("%s: sysctl node creation failed\n", type);
549126430Snjl	} else
550126430Snjl		printf("%s: softc allocation failed\n", type);
551126430Snjl
552126430Snjl	if (bootverbose) {
553161184Sbruno		printf("found %s(%x)", desc, adr & DOD_DEVID_MASK_FULL);
554161184Sbruno		printf(", idx#%x", adr & DOD_DEVID_MASK_DISPIDX);
555161184Sbruno		printf(", port#%x", (adr & DOD_DEVID_MASK_DISPPORT) >> 4);
556126430Snjl		if (adr & DOD_BIOS)
557126430Snjl			printf(", detectable by BIOS");
558126430Snjl		if (adr & DOD_NONVGA)
559161184Sbruno			printf(" (Non-VGA output device whose power "
560161184Sbruno			    "is related to the VGA device)");
561126430Snjl		printf(", head #%d\n",
562161184Sbruno			(adr & DOD_HEAD_ID_MASK) >> DOD_HEAD_ID_SHIFT);
563126430Snjl	}
564132526Snjl	return (vo);
565126430Snjl}
566126430Snjl
567126430Snjlstatic void
568126430Snjlacpi_video_vo_bind(struct acpi_video_output *vo, ACPI_HANDLE handle)
569126430Snjl{
570126430Snjl
571197648Sjhb	ACPI_SERIAL_BEGIN(video_output);
572126430Snjl	if (vo->vo_levels != NULL)
573126430Snjl		AcpiOsFree(vo->vo_levels);
574126430Snjl	vo->handle = handle;
575133625Snjl	vo->vo_numlevels = vo_get_brightness_levels(handle, &vo->vo_levels);
576126430Snjl	if (vo->vo_numlevels >= 2) {
577126430Snjl		if (vo->vo_fullpower == -1
578126430Snjl		    || acpi_video_vo_check_level(vo, vo->vo_fullpower) != 0)
579126430Snjl			/* XXX - can't deal with rebinding... */
580126430Snjl			vo->vo_fullpower = vo->vo_levels[BCL_FULLPOWER];
581126430Snjl		if (vo->vo_economy == -1
582126430Snjl		    || acpi_video_vo_check_level(vo, vo->vo_economy) != 0)
583126430Snjl			/* XXX - see above. */
584126430Snjl			vo->vo_economy = vo->vo_levels[BCL_ECONOMY];
585126430Snjl	}
586203810Sjkim	if (vo->vo_levels != NULL)
587203810Sjkim	    AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY,
588203810Sjkim		acpi_video_vo_notify_handler, vo);
589197648Sjhb	ACPI_SERIAL_END(video_output);
590126430Snjl}
591126430Snjl
592126430Snjlstatic void
593126430Snjlacpi_video_vo_destroy(struct acpi_video_output *vo)
594126430Snjl{
595126430Snjl	struct acpi_video_output_queue *voqh;
596126430Snjl
597133625Snjl	ACPI_SERIAL_ASSERT(video);
598126430Snjl	if (vo->vo_sysctl_tree != NULL) {
599126430Snjl		vo->vo_sysctl_tree = NULL;
600126430Snjl		sysctl_ctx_free(&vo->vo_sysctl_ctx);
601126430Snjl	}
602203810Sjkim	if (vo->vo_levels != NULL) {
603203810Sjkim		AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY,
604203810Sjkim		    acpi_video_vo_notify_handler);
605126430Snjl		AcpiOsFree(vo->vo_levels);
606203810Sjkim	}
607126430Snjl
608126430Snjl	switch (vo->adr & DOD_DEVID_MASK) {
609126430Snjl	case DOD_DEVID_MONITOR:
610126430Snjl		voqh = &crt_units;
611126430Snjl		break;
612126430Snjl	case DOD_DEVID_TV:
613126430Snjl		voqh = &tv_units;
614126430Snjl		break;
615161184Sbruno	case DOD_DEVID_EXT:
616161184Sbruno		voqh = &ext_units;
617161184Sbruno		break;
618161184Sbruno	case DOD_DEVID_INTDFP:
619161184Sbruno		voqh = &lcd_units;
620161184Sbruno		break;
621126430Snjl	default:
622126430Snjl		voqh = &other_units;
623126430Snjl	}
624126430Snjl	STAILQ_REMOVE(voqh, vo, acpi_video_output, vo_unit.next);
625126430Snjl	free(vo, M_ACPIVIDEO);
626126430Snjl}
627126430Snjl
628126430Snjlstatic int
629126430Snjlacpi_video_vo_check_level(struct acpi_video_output *vo, int level)
630126430Snjl{
631126430Snjl	int i;
632126430Snjl
633197648Sjhb	ACPI_SERIAL_ASSERT(video_output);
634126430Snjl	if (vo->vo_levels == NULL)
635126430Snjl		return (ENODEV);
636126430Snjl	for (i = 0; i < vo->vo_numlevels; i++)
637126430Snjl		if (vo->vo_levels[i] == level)
638126430Snjl			return (0);
639126430Snjl	return (EINVAL);
640126430Snjl}
641126430Snjl
642203810Sjkimstatic void
643203810Sjkimacpi_video_vo_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context)
644203810Sjkim{
645203810Sjkim	struct acpi_video_output *vo;
646203810Sjkim	int i, j, level, new_level;
647203810Sjkim
648203810Sjkim	vo = context;
649203810Sjkim	ACPI_SERIAL_BEGIN(video_output);
650203935Sjkim	if (vo->handle != handle)
651203935Sjkim		goto out;
652203810Sjkim
653203810Sjkim	switch (notify) {
654203936Sjkim	case VID_NOTIFY_CYCLE_BRN:
655203935Sjkim		if (vo->vo_numlevels <= 3)
656203935Sjkim			goto out;
657203935Sjkim		/* FALLTHROUGH */
658203810Sjkim	case VID_NOTIFY_INC_BRN:
659203810Sjkim	case VID_NOTIFY_DEC_BRN:
660203936Sjkim	case VID_NOTIFY_ZERO_BRN:
661203810Sjkim		if (vo->vo_levels == NULL)
662203935Sjkim			goto out;
663203813Sjkim		level = vo_get_brightness(handle);
664203810Sjkim		if (level < 0)
665203935Sjkim			goto out;
666203935Sjkim		break;
667203935Sjkim	default:
668203935Sjkim		printf("unknown notify event 0x%x from %s\n",
669203935Sjkim		    notify, acpi_name(handle));
670203935Sjkim		goto out;
671203935Sjkim	}
672203935Sjkim
673203935Sjkim	new_level = level;
674203935Sjkim	switch (notify) {
675203936Sjkim	case VID_NOTIFY_CYCLE_BRN:
676203935Sjkim		for (i = 2; i < vo->vo_numlevels; i++)
677203935Sjkim			if (vo->vo_levels[i] == level) {
678203935Sjkim				new_level = vo->vo_numlevels > i + 1 ?
679203935Sjkim				     vo->vo_levels[i + 1] : vo->vo_levels[2];
680203935Sjkim				break;
681203935Sjkim			}
682203935Sjkim		break;
683203935Sjkim	case VID_NOTIFY_INC_BRN:
684203935Sjkim	case VID_NOTIFY_DEC_BRN:
685203810Sjkim		for (i = 0; i < vo->vo_numlevels; i++) {
686203810Sjkim			j = vo->vo_levels[i];
687203810Sjkim			if (notify == VID_NOTIFY_INC_BRN) {
688203810Sjkim				if (j > level &&
689203810Sjkim				    (j < new_level || level == new_level))
690203810Sjkim					new_level = j;
691203810Sjkim			} else {
692203810Sjkim				if (j < level &&
693203810Sjkim				    (j > new_level || level == new_level))
694203810Sjkim					new_level = j;
695203810Sjkim			}
696203810Sjkim		}
697203810Sjkim		break;
698203936Sjkim	case VID_NOTIFY_ZERO_BRN:
699203935Sjkim		for (i = 0; i < vo->vo_numlevels; i++)
700203935Sjkim			if (vo->vo_levels[i] == 0) {
701203935Sjkim				new_level = 0;
702203935Sjkim				break;
703203935Sjkim			}
704203935Sjkim		break;
705203810Sjkim	}
706203935Sjkim	if (new_level != level) {
707203935Sjkim		vo_set_brightness(handle, new_level);
708203935Sjkim		vo->vo_brightness = new_level;
709203935Sjkim	}
710203935Sjkim
711203935Sjkimout:
712203810Sjkim	ACPI_SERIAL_END(video_output);
713203810Sjkim}
714203810Sjkim
715126430Snjl/* ARGSUSED */
716126430Snjlstatic int
717126430Snjlacpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS)
718126430Snjl{
719126430Snjl	struct acpi_video_output *vo;
720126430Snjl	int state, err;
721126430Snjl
722126430Snjl	vo = (struct acpi_video_output *)arg1;
723133625Snjl	if (vo->handle == NULL)
724133625Snjl		return (ENXIO);
725197648Sjhb	ACPI_SERIAL_BEGIN(video_output);
726133625Snjl	state = (vo_get_device_status(vo->handle) & DCS_ACTIVE) ? 1 : 0;
727126430Snjl	err = sysctl_handle_int(oidp, &state, 0, req);
728126430Snjl	if (err != 0 || req->newptr == NULL)
729126430Snjl		goto out;
730126430Snjl	vo_set_device_state(vo->handle,
731137438Snjl	    DSS_COMMIT | (state ? DSS_ACTIVE : DSS_INACTIVE));
732126430Snjlout:
733197648Sjhb	ACPI_SERIAL_END(video_output);
734126430Snjl	return (err);
735126430Snjl}
736126430Snjl
737126430Snjl/* ARGSUSED */
738126430Snjlstatic int
739126430Snjlacpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS)
740126430Snjl{
741126430Snjl	struct acpi_video_output *vo;
742126430Snjl	int level, preset, err;
743126430Snjl
744126430Snjl	vo = (struct acpi_video_output *)arg1;
745197648Sjhb	ACPI_SERIAL_BEGIN(video_output);
746126430Snjl	if (vo->handle == NULL) {
747126430Snjl		err = ENXIO;
748126430Snjl		goto out;
749126430Snjl	}
750126430Snjl	if (vo->vo_levels == NULL) {
751126430Snjl		err = ENODEV;
752126430Snjl		goto out;
753126430Snjl	}
754126430Snjl
755132526Snjl	preset = (power_profile_get_state() == POWER_PROFILE_ECONOMY) ?
756132526Snjl		  vo->vo_economy : vo->vo_fullpower;
757126430Snjl	level = vo->vo_brightness;
758126430Snjl	if (level == -1)
759126430Snjl		level = preset;
760126430Snjl
761126430Snjl	err = sysctl_handle_int(oidp, &level, 0, req);
762126430Snjl	if (err != 0 || req->newptr == NULL)
763126430Snjl		goto out;
764126430Snjl	if (level < -1 || level > 100) {
765126430Snjl		err = EINVAL;
766126430Snjl		goto out;
767126430Snjl	}
768126430Snjl
769126430Snjl	if (level != -1 && (err = acpi_video_vo_check_level(vo, level)))
770126430Snjl		goto out;
771126430Snjl	vo->vo_brightness = level;
772133625Snjl	vo_set_brightness(vo->handle, (level == -1) ? preset : level);
773133625Snjl
774126430Snjlout:
775197648Sjhb	ACPI_SERIAL_END(video_output);
776126430Snjl	return (err);
777126430Snjl}
778126430Snjl
779126430Snjlstatic int
780126430Snjlacpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS)
781126430Snjl{
782126430Snjl	struct acpi_video_output *vo;
783137438Snjl	int i, level, *preset, err;
784126430Snjl
785137438Snjl	err = 0;
786126430Snjl	vo = (struct acpi_video_output *)arg1;
787197648Sjhb	ACPI_SERIAL_BEGIN(video_output);
788126430Snjl	if (vo->handle == NULL) {
789126430Snjl		err = ENXIO;
790126430Snjl		goto out;
791126430Snjl	}
792126430Snjl	if (vo->vo_levels == NULL) {
793126430Snjl		err = ENODEV;
794126430Snjl		goto out;
795126430Snjl	}
796132526Snjl	preset = (arg2 == POWER_PROFILE_ECONOMY) ?
797132526Snjl		  &vo->vo_economy : &vo->vo_fullpower;
798126430Snjl	level = *preset;
799126430Snjl	err = sysctl_handle_int(oidp, &level, 0, req);
800126430Snjl	if (err != 0 || req->newptr == NULL)
801126430Snjl		goto out;
802126430Snjl	if (level < -1 || level > 100) {
803126430Snjl		err = EINVAL;
804126430Snjl		goto out;
805126430Snjl	}
806133625Snjl	if (level == -1) {
807133625Snjl		i = (arg2 == POWER_PROFILE_ECONOMY) ?
808133625Snjl		    BCL_ECONOMY : BCL_FULLPOWER;
809133625Snjl		level = vo->vo_levels[i];
810137438Snjl	} else if ((err = acpi_video_vo_check_level(vo, level)) != 0)
811126430Snjl		goto out;
812126430Snjl
813126430Snjl	if (vo->vo_brightness == -1 && (power_profile_get_state() == arg2))
814126430Snjl		vo_set_brightness(vo->handle, level);
815126430Snjl	*preset = level;
816133625Snjl
817126430Snjlout:
818197648Sjhb	ACPI_SERIAL_END(video_output);
819126430Snjl	return (err);
820126430Snjl}
821126430Snjl
822126430Snjl/* ARGSUSED */
823126430Snjlstatic int
824126430Snjlacpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS)
825126430Snjl{
826126430Snjl	struct acpi_video_output *vo;
827126430Snjl	int err;
828126430Snjl
829126430Snjl	vo = (struct acpi_video_output *)arg1;
830197648Sjhb	ACPI_SERIAL_BEGIN(video_output);
831126430Snjl	if (vo->vo_levels == NULL) {
832126430Snjl		err = ENODEV;
833126430Snjl		goto out;
834126430Snjl	}
835126430Snjl	if (req->newptr != NULL) {
836126430Snjl		err = EPERM;
837126430Snjl		goto out;
838126430Snjl	}
839126430Snjl	err = sysctl_handle_opaque(oidp, vo->vo_levels,
840133625Snjl	    vo->vo_numlevels * sizeof(*vo->vo_levels), req);
841133625Snjl
842126430Snjlout:
843197648Sjhb	ACPI_SERIAL_END(video_output);
844126430Snjl	return (err);
845126430Snjl}
846126430Snjl
847126430Snjlstatic void
848126430Snjlvid_set_switch_policy(ACPI_HANDLE handle, UINT32 policy)
849126430Snjl{
850126430Snjl	ACPI_STATUS status;
851126430Snjl
852126560Snjl	status = acpi_SetInteger(handle, "_DOS", policy);
853126430Snjl	if (ACPI_FAILURE(status))
854126430Snjl		printf("can't evaluate %s._DOS - %s\n",
855126430Snjl		       acpi_name(handle), AcpiFormatException(status));
856126430Snjl}
857126430Snjl
858126430Snjlstruct enum_callback_arg {
859126430Snjl	void (*callback)(ACPI_HANDLE, UINT32, void *);
860126430Snjl	void *context;
861126430Snjl	ACPI_OBJECT *dod_pkg;
862132605Snjl	int count;
863126430Snjl};
864126430Snjl
865126430Snjlstatic ACPI_STATUS
866126430Snjlvid_enum_outputs_subr(ACPI_HANDLE handle, UINT32 level __unused,
867132605Snjl		      void *context, void **retp __unused)
868126430Snjl{
869126430Snjl	ACPI_STATUS status;
870132605Snjl	UINT32 adr, val;
871126430Snjl	struct enum_callback_arg *argset;
872126430Snjl	size_t i;
873126430Snjl
874133625Snjl	ACPI_SERIAL_ASSERT(video);
875126430Snjl	argset = context;
876126560Snjl	status = acpi_GetInteger(handle, "_ADR", &adr);
877132605Snjl	if (ACPI_FAILURE(status))
878132605Snjl		return (AE_OK);
879132605Snjl
880132605Snjl	for (i = 0; i < argset->dod_pkg->Package.Count; i++) {
881132605Snjl		if (acpi_PkgInt32(argset->dod_pkg, i, &val) == 0 &&
882161184Sbruno		    (val & DOD_DEVID_MASK_FULL) == adr) {
883132605Snjl			argset->callback(handle, val, argset->context);
884132605Snjl			argset->count++;
885126430Snjl		}
886126430Snjl	}
887126430Snjl
888126430Snjl	return (AE_OK);
889126430Snjl}
890126430Snjl
891126430Snjlstatic int
892126430Snjlvid_enum_outputs(ACPI_HANDLE handle,
893126430Snjl		 void (*callback)(ACPI_HANDLE, UINT32, void *), void *context)
894126430Snjl{
895126430Snjl	ACPI_STATUS status;
896126430Snjl	ACPI_BUFFER dod_buf;
897126430Snjl	ACPI_OBJECT *res;
898126430Snjl	struct enum_callback_arg argset;
899126430Snjl
900133625Snjl	ACPI_SERIAL_ASSERT(video);
901126430Snjl	dod_buf.Length = ACPI_ALLOCATE_BUFFER;
902126430Snjl	dod_buf.Pointer = NULL;
903126430Snjl	status = AcpiEvaluateObject(handle, "_DOD", NULL, &dod_buf);
904126430Snjl	if (ACPI_FAILURE(status)) {
905126430Snjl		if (status != AE_NOT_FOUND)
906126430Snjl			printf("can't evaluate %s._DOD - %s\n",
907126430Snjl			       acpi_name(handle), AcpiFormatException(status));
908132605Snjl		argset.count = -1;
909126430Snjl		goto out;
910126430Snjl	}
911126430Snjl	res = (ACPI_OBJECT *)dod_buf.Pointer;
912132605Snjl	if (!ACPI_PKG_VALID(res, 1)) {
913126430Snjl		printf("evaluation of %s._DOD makes no sense\n",
914126430Snjl		       acpi_name(handle));
915132605Snjl		argset.count = -1;
916126430Snjl		goto out;
917126430Snjl	}
918126430Snjl	if (callback == NULL) {
919132605Snjl		argset.count = res->Package.Count;
920126430Snjl		goto out;
921126430Snjl	}
922126430Snjl	argset.callback = callback;
923126430Snjl	argset.context  = context;
924126430Snjl	argset.dod_pkg  = res;
925132605Snjl	argset.count    = 0;
926126430Snjl	status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1,
927199337Sjkim	    vid_enum_outputs_subr, NULL, &argset, NULL);
928126430Snjl	if (ACPI_FAILURE(status))
929126430Snjl		printf("failed walking down %s - %s\n",
930126430Snjl		       acpi_name(handle), AcpiFormatException(status));
931126430Snjlout:
932126430Snjl	if (dod_buf.Pointer != NULL)
933126430Snjl		AcpiOsFree(dod_buf.Pointer);
934132605Snjl	return (argset.count);
935126430Snjl}
936126430Snjl
937126430Snjlstatic int
938133625Snjlvo_get_brightness_levels(ACPI_HANDLE handle, int **levelp)
939126430Snjl{
940126430Snjl	ACPI_STATUS status;
941126430Snjl	ACPI_BUFFER bcl_buf;
942132605Snjl	ACPI_OBJECT *res;
943137438Snjl	int num, i, n, *levels;
944126430Snjl
945137438Snjl	num = 0;
946126430Snjl	bcl_buf.Length = ACPI_ALLOCATE_BUFFER;
947126430Snjl	bcl_buf.Pointer = NULL;
948126430Snjl	status = AcpiEvaluateObject(handle, "_BCL", NULL, &bcl_buf);
949126430Snjl	if (ACPI_FAILURE(status)) {
950126430Snjl		if (status != AE_NOT_FOUND)
951126430Snjl			printf("can't evaluate %s._BCL - %s\n",
952126430Snjl			       acpi_name(handle), AcpiFormatException(status));
953126430Snjl		num = -1;
954126430Snjl		goto out;
955126430Snjl	}
956126430Snjl	res = (ACPI_OBJECT *)bcl_buf.Pointer;
957132605Snjl	if (!ACPI_PKG_VALID(res, 2)) {
958126430Snjl		printf("evaluation of %s._BCL makes no sense\n",
959126430Snjl		       acpi_name(handle));
960126430Snjl		num = -1;
961126430Snjl		goto out;
962126430Snjl	}
963126430Snjl	num = res->Package.Count;
964126430Snjl	if (levelp == NULL)
965126430Snjl		goto out;
966132605Snjl	levels = AcpiOsAllocate(num * sizeof(*levels));
967126430Snjl	if (levels == NULL) {
968126430Snjl		num = -1;
969126430Snjl		goto out;
970126430Snjl	}
971132605Snjl	for (i = 0, n = 0; i < num; i++)
972132605Snjl		if (acpi_PkgInt32(res, i, &levels[n]) == 0)
973132605Snjl			n++;
974126430Snjl	if (n < 2) {
975126430Snjl		num = -1;
976126430Snjl		AcpiOsFree(levels);
977126430Snjl	} else {
978126430Snjl		num = n;
979126430Snjl		*levelp = levels;
980126430Snjl	}
981126430Snjlout:
982126430Snjl	if (bcl_buf.Pointer != NULL)
983126430Snjl		AcpiOsFree(bcl_buf.Pointer);
984126430Snjl
985126430Snjl	return (num);
986126430Snjl}
987126430Snjl
988203810Sjkimstatic int
989203810Sjkimvo_get_brightness(ACPI_HANDLE handle)
990203810Sjkim{
991203810Sjkim	UINT32 level;
992203810Sjkim	ACPI_STATUS status;
993203810Sjkim
994203810Sjkim	ACPI_SERIAL_ASSERT(video_output);
995203810Sjkim	status = acpi_GetInteger(handle, "_BQC", &level);
996203810Sjkim	if (ACPI_FAILURE(status)) {
997203810Sjkim		printf("can't evaluate %s._BQC - %s\n", acpi_name(handle),
998203810Sjkim		    AcpiFormatException(status));
999203810Sjkim		return (-1);
1000203810Sjkim	}
1001203810Sjkim	if (level > 100)
1002203810Sjkim		return (-1);
1003203810Sjkim
1004203810Sjkim	return (level);
1005203810Sjkim}
1006203810Sjkim
1007126430Snjlstatic void
1008126430Snjlvo_set_brightness(ACPI_HANDLE handle, int level)
1009126430Snjl{
1010126430Snjl	ACPI_STATUS status;
1011126430Snjl
1012197648Sjhb	ACPI_SERIAL_ASSERT(video_output);
1013126560Snjl	status = acpi_SetInteger(handle, "_BCM", level);
1014126430Snjl	if (ACPI_FAILURE(status))
1015126430Snjl		printf("can't evaluate %s._BCM - %s\n",
1016126430Snjl		       acpi_name(handle), AcpiFormatException(status));
1017126430Snjl}
1018126430Snjl
1019126430Snjlstatic UINT32
1020126430Snjlvo_get_device_status(ACPI_HANDLE handle)
1021126430Snjl{
1022137438Snjl	UINT32 dcs;
1023126430Snjl	ACPI_STATUS status;
1024126430Snjl
1025197648Sjhb	ACPI_SERIAL_ASSERT(video_output);
1026137438Snjl	dcs = 0;
1027126560Snjl	status = acpi_GetInteger(handle, "_DCS", &dcs);
1028126430Snjl	if (ACPI_FAILURE(status))
1029126430Snjl		printf("can't evaluate %s._DCS - %s\n",
1030126430Snjl		       acpi_name(handle), AcpiFormatException(status));
1031126430Snjl
1032126430Snjl	return (dcs);
1033126430Snjl}
1034126430Snjl
1035126430Snjlstatic UINT32
1036133625Snjlvo_get_graphics_state(ACPI_HANDLE handle)
1037126430Snjl{
1038137438Snjl	UINT32 dgs;
1039126430Snjl	ACPI_STATUS status;
1040126430Snjl
1041137438Snjl	dgs = 0;
1042126560Snjl	status = acpi_GetInteger(handle, "_DGS", &dgs);
1043126430Snjl	if (ACPI_FAILURE(status))
1044126430Snjl		printf("can't evaluate %s._DGS - %s\n",
1045126430Snjl		       acpi_name(handle), AcpiFormatException(status));
1046126430Snjl
1047126430Snjl	return (dgs);
1048126430Snjl}
1049126430Snjl
1050126430Snjlstatic void
1051126430Snjlvo_set_device_state(ACPI_HANDLE handle, UINT32 state)
1052126430Snjl{
1053126430Snjl	ACPI_STATUS status;
1054126430Snjl
1055197648Sjhb	ACPI_SERIAL_ASSERT(video_output);
1056126560Snjl	status = acpi_SetInteger(handle, "_DSS", state);
1057126430Snjl	if (ACPI_FAILURE(status))
1058126430Snjl		printf("can't evaluate %s._DSS - %s\n",
1059126430Snjl		       acpi_name(handle), AcpiFormatException(status));
1060126430Snjl}
1061