acpi_video.c revision 227293
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 227293 2011-11-07 06:44:47Z ed $");
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");
186227293Sedstatic MALLOC_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;
432137438Snjl	char name[8], env[32];
433126430Snjl	const char *type, *desc;
434126430Snjl	struct acpi_video_output_queue *voqh;
435126430Snjl
436133625Snjl	ACPI_SERIAL_ASSERT(video);
437161184Sbruno
438126430Snjl	switch (adr & DOD_DEVID_MASK) {
439126430Snjl	case DOD_DEVID_MONITOR:
440161184Sbruno		if ((adr & DOD_DEVID_MASK_FULL) == DOD_DEVID_LCD) {
441161184Sbruno			/* DOD_DEVID_LCD is a common, backward compatible ID */
442161184Sbruno			desc = "Internal/Integrated Digital Flat Panel";
443161184Sbruno			type = "lcd";
444161184Sbruno			voqh = &lcd_units;
445161184Sbruno		} else {
446161184Sbruno			desc = "VGA CRT or VESA Compatible Analog Monitor";
447161184Sbruno			type = "crt";
448161184Sbruno			voqh = &crt_units;
449161184Sbruno		}
450126430Snjl		break;
451126430Snjl	case DOD_DEVID_TV:
452161184Sbruno		desc = "TV/HDTV or Analog-Video Monitor";
453126430Snjl		type = "tv";
454126430Snjl		voqh = &tv_units;
455126430Snjl		break;
456161184Sbruno	case DOD_DEVID_EXT:
457161184Sbruno		desc = "External Digital Monitor";
458161184Sbruno		type = "ext";
459161184Sbruno		voqh = &ext_units;
460161184Sbruno		break;
461161184Sbruno	case DOD_DEVID_INTDFP:
462161184Sbruno		desc = "Internal/Integrated Digital Flat Panel";
463161184Sbruno		type = "lcd";
464161184Sbruno		voqh = &lcd_units;
465161184Sbruno		break;
466126430Snjl	default:
467126430Snjl		desc = "unknown output";
468126430Snjl		type = "out";
469126430Snjl		voqh = &other_units;
470126430Snjl	}
471126430Snjl
472126430Snjl	n = 0;
473209064Sjkim	vp = NULL;
474126430Snjl	STAILQ_FOREACH(vn, voqh, vo_unit.next) {
475126430Snjl		if (vn->vo_unit.num != n)
476126430Snjl			break;
477126430Snjl		vp = vn;
478126430Snjl		n++;
479126430Snjl	}
480126430Snjl
481132526Snjl	snprintf(name, sizeof(name), "%s%d", type, n);
482126430Snjl
483126430Snjl	vo = malloc(sizeof(*vo), M_ACPIVIDEO, M_NOWAIT);
484126430Snjl	if (vo != NULL) {
485126430Snjl		vo->handle = NULL;
486126430Snjl		vo->adr = adr;
487126430Snjl		vo->vo_unit.num = n;
488126430Snjl		vo->vo_brightness = -1;
489126430Snjl		vo->vo_fullpower = -1;	/* TODO: override with tunables */
490126430Snjl		vo->vo_economy = -1;
491126430Snjl		vo->vo_numlevels = 0;
492126430Snjl		vo->vo_levels = NULL;
493137438Snjl		snprintf(env, sizeof(env), "hw.acpi.video.%s.fullpower", name);
494126430Snjl		if (getenv_int(env, &x))
495126430Snjl			vo->vo_fullpower = x;
496137438Snjl		snprintf(env, sizeof(env), "hw.acpi.video.%s.economy", name);
497126430Snjl		if (getenv_int(env, &x))
498126430Snjl			vo->vo_economy = x;
499126430Snjl
500126430Snjl		sysctl_ctx_init(&vo->vo_sysctl_ctx);
501126430Snjl		if (vp != NULL)
502126430Snjl			STAILQ_INSERT_AFTER(voqh, vp, vo, vo_unit.next);
503126430Snjl		else
504126430Snjl			STAILQ_INSERT_TAIL(voqh, vo, vo_unit.next);
505126430Snjl		if (acpi_video_sysctl_tree != NULL)
506126430Snjl			vo->vo_sysctl_tree =
507126430Snjl			    SYSCTL_ADD_NODE(&vo->vo_sysctl_ctx,
508132526Snjl				SYSCTL_CHILDREN(acpi_video_sysctl_tree),
509132526Snjl				OID_AUTO, name, CTLFLAG_RD, 0, desc);
510126430Snjl		if (vo->vo_sysctl_tree != NULL) {
511126430Snjl			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
512132526Snjl			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
513132526Snjl			    OID_AUTO, "active",
514132526Snjl			    CTLTYPE_INT|CTLFLAG_RW, vo, 0,
515132526Snjl			    acpi_video_vo_active_sysctl, "I",
516132526Snjl			    "current activity of this device");
517126430Snjl			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
518132526Snjl			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
519132526Snjl			    OID_AUTO, "brightness",
520132526Snjl			    CTLTYPE_INT|CTLFLAG_RW, vo, 0,
521132526Snjl			    acpi_video_vo_bright_sysctl, "I",
522132526Snjl			    "current brightness level");
523126430Snjl			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
524132526Snjl			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
525132526Snjl			    OID_AUTO, "fullpower",
526132526Snjl			    CTLTYPE_INT|CTLFLAG_RW, vo,
527132526Snjl			    POWER_PROFILE_PERFORMANCE,
528132526Snjl			    acpi_video_vo_presets_sysctl, "I",
529132526Snjl			    "preset level for full power mode");
530126430Snjl			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
531132526Snjl			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
532132526Snjl			    OID_AUTO, "economy",
533132526Snjl			    CTLTYPE_INT|CTLFLAG_RW, vo,
534132526Snjl			    POWER_PROFILE_ECONOMY,
535132526Snjl			    acpi_video_vo_presets_sysctl, "I",
536132526Snjl			    "preset level for economy mode");
537126430Snjl			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
538132526Snjl			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
539132526Snjl			    OID_AUTO, "levels",
540217566Smdf			    CTLTYPE_INT | CTLFLAG_RD, vo, 0,
541132526Snjl			    acpi_video_vo_levels_sysctl, "I",
542132526Snjl			    "supported brightness levels");
543126430Snjl		} else
544126430Snjl			printf("%s: sysctl node creation failed\n", type);
545126430Snjl	} else
546126430Snjl		printf("%s: softc allocation failed\n", type);
547126430Snjl
548126430Snjl	if (bootverbose) {
549161184Sbruno		printf("found %s(%x)", desc, adr & DOD_DEVID_MASK_FULL);
550161184Sbruno		printf(", idx#%x", adr & DOD_DEVID_MASK_DISPIDX);
551161184Sbruno		printf(", port#%x", (adr & DOD_DEVID_MASK_DISPPORT) >> 4);
552126430Snjl		if (adr & DOD_BIOS)
553126430Snjl			printf(", detectable by BIOS");
554126430Snjl		if (adr & DOD_NONVGA)
555161184Sbruno			printf(" (Non-VGA output device whose power "
556161184Sbruno			    "is related to the VGA device)");
557126430Snjl		printf(", head #%d\n",
558161184Sbruno			(adr & DOD_HEAD_ID_MASK) >> DOD_HEAD_ID_SHIFT);
559126430Snjl	}
560132526Snjl	return (vo);
561126430Snjl}
562126430Snjl
563126430Snjlstatic void
564126430Snjlacpi_video_vo_bind(struct acpi_video_output *vo, ACPI_HANDLE handle)
565126430Snjl{
566126430Snjl
567197648Sjhb	ACPI_SERIAL_BEGIN(video_output);
568126430Snjl	if (vo->vo_levels != NULL)
569126430Snjl		AcpiOsFree(vo->vo_levels);
570126430Snjl	vo->handle = handle;
571133625Snjl	vo->vo_numlevels = vo_get_brightness_levels(handle, &vo->vo_levels);
572126430Snjl	if (vo->vo_numlevels >= 2) {
573126430Snjl		if (vo->vo_fullpower == -1
574126430Snjl		    || acpi_video_vo_check_level(vo, vo->vo_fullpower) != 0)
575126430Snjl			/* XXX - can't deal with rebinding... */
576126430Snjl			vo->vo_fullpower = vo->vo_levels[BCL_FULLPOWER];
577126430Snjl		if (vo->vo_economy == -1
578126430Snjl		    || acpi_video_vo_check_level(vo, vo->vo_economy) != 0)
579126430Snjl			/* XXX - see above. */
580126430Snjl			vo->vo_economy = vo->vo_levels[BCL_ECONOMY];
581126430Snjl	}
582203810Sjkim	if (vo->vo_levels != NULL)
583204965Sjkim		AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY,
584204965Sjkim		    acpi_video_vo_notify_handler, vo);
585197648Sjhb	ACPI_SERIAL_END(video_output);
586126430Snjl}
587126430Snjl
588126430Snjlstatic void
589126430Snjlacpi_video_vo_destroy(struct acpi_video_output *vo)
590126430Snjl{
591126430Snjl	struct acpi_video_output_queue *voqh;
592126430Snjl
593133625Snjl	ACPI_SERIAL_ASSERT(video);
594126430Snjl	if (vo->vo_sysctl_tree != NULL) {
595126430Snjl		vo->vo_sysctl_tree = NULL;
596126430Snjl		sysctl_ctx_free(&vo->vo_sysctl_ctx);
597126430Snjl	}
598203810Sjkim	if (vo->vo_levels != NULL) {
599203810Sjkim		AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY,
600203810Sjkim		    acpi_video_vo_notify_handler);
601126430Snjl		AcpiOsFree(vo->vo_levels);
602203810Sjkim	}
603126430Snjl
604126430Snjl	switch (vo->adr & DOD_DEVID_MASK) {
605126430Snjl	case DOD_DEVID_MONITOR:
606126430Snjl		voqh = &crt_units;
607126430Snjl		break;
608126430Snjl	case DOD_DEVID_TV:
609126430Snjl		voqh = &tv_units;
610126430Snjl		break;
611161184Sbruno	case DOD_DEVID_EXT:
612161184Sbruno		voqh = &ext_units;
613161184Sbruno		break;
614161184Sbruno	case DOD_DEVID_INTDFP:
615161184Sbruno		voqh = &lcd_units;
616161184Sbruno		break;
617126430Snjl	default:
618126430Snjl		voqh = &other_units;
619126430Snjl	}
620126430Snjl	STAILQ_REMOVE(voqh, vo, acpi_video_output, vo_unit.next);
621126430Snjl	free(vo, M_ACPIVIDEO);
622126430Snjl}
623126430Snjl
624126430Snjlstatic int
625126430Snjlacpi_video_vo_check_level(struct acpi_video_output *vo, int level)
626126430Snjl{
627126430Snjl	int i;
628126430Snjl
629197648Sjhb	ACPI_SERIAL_ASSERT(video_output);
630126430Snjl	if (vo->vo_levels == NULL)
631126430Snjl		return (ENODEV);
632126430Snjl	for (i = 0; i < vo->vo_numlevels; i++)
633126430Snjl		if (vo->vo_levels[i] == level)
634126430Snjl			return (0);
635126430Snjl	return (EINVAL);
636126430Snjl}
637126430Snjl
638203810Sjkimstatic void
639203810Sjkimacpi_video_vo_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context)
640203810Sjkim{
641203810Sjkim	struct acpi_video_output *vo;
642203810Sjkim	int i, j, level, new_level;
643203810Sjkim
644203810Sjkim	vo = context;
645203810Sjkim	ACPI_SERIAL_BEGIN(video_output);
646203935Sjkim	if (vo->handle != handle)
647203935Sjkim		goto out;
648203810Sjkim
649203810Sjkim	switch (notify) {
650203936Sjkim	case VID_NOTIFY_CYCLE_BRN:
651203935Sjkim		if (vo->vo_numlevels <= 3)
652203935Sjkim			goto out;
653203935Sjkim		/* FALLTHROUGH */
654203810Sjkim	case VID_NOTIFY_INC_BRN:
655203810Sjkim	case VID_NOTIFY_DEC_BRN:
656203936Sjkim	case VID_NOTIFY_ZERO_BRN:
657203810Sjkim		if (vo->vo_levels == NULL)
658203935Sjkim			goto out;
659203813Sjkim		level = vo_get_brightness(handle);
660203810Sjkim		if (level < 0)
661203935Sjkim			goto out;
662203935Sjkim		break;
663203935Sjkim	default:
664203935Sjkim		printf("unknown notify event 0x%x from %s\n",
665203935Sjkim		    notify, acpi_name(handle));
666203935Sjkim		goto out;
667203935Sjkim	}
668203935Sjkim
669203935Sjkim	new_level = level;
670203935Sjkim	switch (notify) {
671203936Sjkim	case VID_NOTIFY_CYCLE_BRN:
672203935Sjkim		for (i = 2; i < vo->vo_numlevels; i++)
673203935Sjkim			if (vo->vo_levels[i] == level) {
674203935Sjkim				new_level = vo->vo_numlevels > i + 1 ?
675203935Sjkim				     vo->vo_levels[i + 1] : vo->vo_levels[2];
676203935Sjkim				break;
677203935Sjkim			}
678203935Sjkim		break;
679203935Sjkim	case VID_NOTIFY_INC_BRN:
680203935Sjkim	case VID_NOTIFY_DEC_BRN:
681203810Sjkim		for (i = 0; i < vo->vo_numlevels; i++) {
682203810Sjkim			j = vo->vo_levels[i];
683203810Sjkim			if (notify == VID_NOTIFY_INC_BRN) {
684203810Sjkim				if (j > level &&
685203810Sjkim				    (j < new_level || level == new_level))
686203810Sjkim					new_level = j;
687203810Sjkim			} else {
688203810Sjkim				if (j < level &&
689203810Sjkim				    (j > new_level || level == new_level))
690203810Sjkim					new_level = j;
691203810Sjkim			}
692203810Sjkim		}
693203810Sjkim		break;
694203936Sjkim	case VID_NOTIFY_ZERO_BRN:
695203935Sjkim		for (i = 0; i < vo->vo_numlevels; i++)
696203935Sjkim			if (vo->vo_levels[i] == 0) {
697203935Sjkim				new_level = 0;
698203935Sjkim				break;
699203935Sjkim			}
700203935Sjkim		break;
701203810Sjkim	}
702203935Sjkim	if (new_level != level) {
703203935Sjkim		vo_set_brightness(handle, new_level);
704203935Sjkim		vo->vo_brightness = new_level;
705203935Sjkim	}
706203935Sjkim
707203935Sjkimout:
708203810Sjkim	ACPI_SERIAL_END(video_output);
709203810Sjkim}
710203810Sjkim
711126430Snjl/* ARGSUSED */
712126430Snjlstatic int
713126430Snjlacpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS)
714126430Snjl{
715126430Snjl	struct acpi_video_output *vo;
716126430Snjl	int state, err;
717126430Snjl
718126430Snjl	vo = (struct acpi_video_output *)arg1;
719133625Snjl	if (vo->handle == NULL)
720133625Snjl		return (ENXIO);
721197648Sjhb	ACPI_SERIAL_BEGIN(video_output);
722133625Snjl	state = (vo_get_device_status(vo->handle) & DCS_ACTIVE) ? 1 : 0;
723126430Snjl	err = sysctl_handle_int(oidp, &state, 0, req);
724126430Snjl	if (err != 0 || req->newptr == NULL)
725126430Snjl		goto out;
726126430Snjl	vo_set_device_state(vo->handle,
727137438Snjl	    DSS_COMMIT | (state ? DSS_ACTIVE : DSS_INACTIVE));
728126430Snjlout:
729197648Sjhb	ACPI_SERIAL_END(video_output);
730126430Snjl	return (err);
731126430Snjl}
732126430Snjl
733126430Snjl/* ARGSUSED */
734126430Snjlstatic int
735126430Snjlacpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS)
736126430Snjl{
737126430Snjl	struct acpi_video_output *vo;
738126430Snjl	int level, preset, err;
739126430Snjl
740126430Snjl	vo = (struct acpi_video_output *)arg1;
741197648Sjhb	ACPI_SERIAL_BEGIN(video_output);
742126430Snjl	if (vo->handle == NULL) {
743126430Snjl		err = ENXIO;
744126430Snjl		goto out;
745126430Snjl	}
746126430Snjl	if (vo->vo_levels == NULL) {
747126430Snjl		err = ENODEV;
748126430Snjl		goto out;
749126430Snjl	}
750126430Snjl
751132526Snjl	preset = (power_profile_get_state() == POWER_PROFILE_ECONOMY) ?
752132526Snjl		  vo->vo_economy : vo->vo_fullpower;
753126430Snjl	level = vo->vo_brightness;
754126430Snjl	if (level == -1)
755126430Snjl		level = preset;
756126430Snjl
757126430Snjl	err = sysctl_handle_int(oidp, &level, 0, req);
758126430Snjl	if (err != 0 || req->newptr == NULL)
759126430Snjl		goto out;
760126430Snjl	if (level < -1 || level > 100) {
761126430Snjl		err = EINVAL;
762126430Snjl		goto out;
763126430Snjl	}
764126430Snjl
765126430Snjl	if (level != -1 && (err = acpi_video_vo_check_level(vo, level)))
766126430Snjl		goto out;
767126430Snjl	vo->vo_brightness = level;
768133625Snjl	vo_set_brightness(vo->handle, (level == -1) ? preset : level);
769133625Snjl
770126430Snjlout:
771197648Sjhb	ACPI_SERIAL_END(video_output);
772126430Snjl	return (err);
773126430Snjl}
774126430Snjl
775126430Snjlstatic int
776126430Snjlacpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS)
777126430Snjl{
778126430Snjl	struct acpi_video_output *vo;
779137438Snjl	int i, level, *preset, err;
780126430Snjl
781126430Snjl	vo = (struct acpi_video_output *)arg1;
782197648Sjhb	ACPI_SERIAL_BEGIN(video_output);
783126430Snjl	if (vo->handle == NULL) {
784126430Snjl		err = ENXIO;
785126430Snjl		goto out;
786126430Snjl	}
787126430Snjl	if (vo->vo_levels == NULL) {
788126430Snjl		err = ENODEV;
789126430Snjl		goto out;
790126430Snjl	}
791132526Snjl	preset = (arg2 == POWER_PROFILE_ECONOMY) ?
792132526Snjl		  &vo->vo_economy : &vo->vo_fullpower;
793126430Snjl	level = *preset;
794126430Snjl	err = sysctl_handle_int(oidp, &level, 0, req);
795126430Snjl	if (err != 0 || req->newptr == NULL)
796126430Snjl		goto out;
797126430Snjl	if (level < -1 || level > 100) {
798126430Snjl		err = EINVAL;
799126430Snjl		goto out;
800126430Snjl	}
801133625Snjl	if (level == -1) {
802133625Snjl		i = (arg2 == POWER_PROFILE_ECONOMY) ?
803133625Snjl		    BCL_ECONOMY : BCL_FULLPOWER;
804133625Snjl		level = vo->vo_levels[i];
805137438Snjl	} else if ((err = acpi_video_vo_check_level(vo, level)) != 0)
806126430Snjl		goto out;
807126430Snjl
808126430Snjl	if (vo->vo_brightness == -1 && (power_profile_get_state() == arg2))
809126430Snjl		vo_set_brightness(vo->handle, level);
810126430Snjl	*preset = level;
811133625Snjl
812126430Snjlout:
813197648Sjhb	ACPI_SERIAL_END(video_output);
814126430Snjl	return (err);
815126430Snjl}
816126430Snjl
817126430Snjl/* ARGSUSED */
818126430Snjlstatic int
819126430Snjlacpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS)
820126430Snjl{
821126430Snjl	struct acpi_video_output *vo;
822126430Snjl	int err;
823126430Snjl
824126430Snjl	vo = (struct acpi_video_output *)arg1;
825197648Sjhb	ACPI_SERIAL_BEGIN(video_output);
826126430Snjl	if (vo->vo_levels == NULL) {
827126430Snjl		err = ENODEV;
828126430Snjl		goto out;
829126430Snjl	}
830126430Snjl	if (req->newptr != NULL) {
831126430Snjl		err = EPERM;
832126430Snjl		goto out;
833126430Snjl	}
834126430Snjl	err = sysctl_handle_opaque(oidp, vo->vo_levels,
835133625Snjl	    vo->vo_numlevels * sizeof(*vo->vo_levels), req);
836133625Snjl
837126430Snjlout:
838197648Sjhb	ACPI_SERIAL_END(video_output);
839126430Snjl	return (err);
840126430Snjl}
841126430Snjl
842126430Snjlstatic void
843126430Snjlvid_set_switch_policy(ACPI_HANDLE handle, UINT32 policy)
844126430Snjl{
845126430Snjl	ACPI_STATUS status;
846126430Snjl
847126560Snjl	status = acpi_SetInteger(handle, "_DOS", policy);
848126430Snjl	if (ACPI_FAILURE(status))
849126430Snjl		printf("can't evaluate %s._DOS - %s\n",
850126430Snjl		       acpi_name(handle), AcpiFormatException(status));
851126430Snjl}
852126430Snjl
853126430Snjlstruct enum_callback_arg {
854126430Snjl	void (*callback)(ACPI_HANDLE, UINT32, void *);
855126430Snjl	void *context;
856126430Snjl	ACPI_OBJECT *dod_pkg;
857132605Snjl	int count;
858126430Snjl};
859126430Snjl
860126430Snjlstatic ACPI_STATUS
861126430Snjlvid_enum_outputs_subr(ACPI_HANDLE handle, UINT32 level __unused,
862132605Snjl		      void *context, void **retp __unused)
863126430Snjl{
864126430Snjl	ACPI_STATUS status;
865132605Snjl	UINT32 adr, val;
866126430Snjl	struct enum_callback_arg *argset;
867126430Snjl	size_t i;
868126430Snjl
869133625Snjl	ACPI_SERIAL_ASSERT(video);
870126430Snjl	argset = context;
871126560Snjl	status = acpi_GetInteger(handle, "_ADR", &adr);
872132605Snjl	if (ACPI_FAILURE(status))
873132605Snjl		return (AE_OK);
874132605Snjl
875132605Snjl	for (i = 0; i < argset->dod_pkg->Package.Count; i++) {
876132605Snjl		if (acpi_PkgInt32(argset->dod_pkg, i, &val) == 0 &&
877161184Sbruno		    (val & DOD_DEVID_MASK_FULL) == adr) {
878132605Snjl			argset->callback(handle, val, argset->context);
879132605Snjl			argset->count++;
880126430Snjl		}
881126430Snjl	}
882126430Snjl
883126430Snjl	return (AE_OK);
884126430Snjl}
885126430Snjl
886126430Snjlstatic int
887126430Snjlvid_enum_outputs(ACPI_HANDLE handle,
888126430Snjl		 void (*callback)(ACPI_HANDLE, UINT32, void *), void *context)
889126430Snjl{
890126430Snjl	ACPI_STATUS status;
891126430Snjl	ACPI_BUFFER dod_buf;
892126430Snjl	ACPI_OBJECT *res;
893126430Snjl	struct enum_callback_arg argset;
894126430Snjl
895133625Snjl	ACPI_SERIAL_ASSERT(video);
896126430Snjl	dod_buf.Length = ACPI_ALLOCATE_BUFFER;
897126430Snjl	dod_buf.Pointer = NULL;
898126430Snjl	status = AcpiEvaluateObject(handle, "_DOD", NULL, &dod_buf);
899126430Snjl	if (ACPI_FAILURE(status)) {
900126430Snjl		if (status != AE_NOT_FOUND)
901126430Snjl			printf("can't evaluate %s._DOD - %s\n",
902126430Snjl			       acpi_name(handle), AcpiFormatException(status));
903132605Snjl		argset.count = -1;
904126430Snjl		goto out;
905126430Snjl	}
906126430Snjl	res = (ACPI_OBJECT *)dod_buf.Pointer;
907132605Snjl	if (!ACPI_PKG_VALID(res, 1)) {
908126430Snjl		printf("evaluation of %s._DOD makes no sense\n",
909126430Snjl		       acpi_name(handle));
910132605Snjl		argset.count = -1;
911126430Snjl		goto out;
912126430Snjl	}
913126430Snjl	if (callback == NULL) {
914132605Snjl		argset.count = res->Package.Count;
915126430Snjl		goto out;
916126430Snjl	}
917126430Snjl	argset.callback = callback;
918126430Snjl	argset.context  = context;
919126430Snjl	argset.dod_pkg  = res;
920132605Snjl	argset.count    = 0;
921126430Snjl	status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1,
922199337Sjkim	    vid_enum_outputs_subr, NULL, &argset, NULL);
923126430Snjl	if (ACPI_FAILURE(status))
924126430Snjl		printf("failed walking down %s - %s\n",
925126430Snjl		       acpi_name(handle), AcpiFormatException(status));
926126430Snjlout:
927126430Snjl	if (dod_buf.Pointer != NULL)
928126430Snjl		AcpiOsFree(dod_buf.Pointer);
929132605Snjl	return (argset.count);
930126430Snjl}
931126430Snjl
932126430Snjlstatic int
933133625Snjlvo_get_brightness_levels(ACPI_HANDLE handle, int **levelp)
934126430Snjl{
935126430Snjl	ACPI_STATUS status;
936126430Snjl	ACPI_BUFFER bcl_buf;
937132605Snjl	ACPI_OBJECT *res;
938137438Snjl	int num, i, n, *levels;
939126430Snjl
940126430Snjl	bcl_buf.Length = ACPI_ALLOCATE_BUFFER;
941126430Snjl	bcl_buf.Pointer = NULL;
942126430Snjl	status = AcpiEvaluateObject(handle, "_BCL", NULL, &bcl_buf);
943126430Snjl	if (ACPI_FAILURE(status)) {
944126430Snjl		if (status != AE_NOT_FOUND)
945126430Snjl			printf("can't evaluate %s._BCL - %s\n",
946126430Snjl			       acpi_name(handle), AcpiFormatException(status));
947126430Snjl		goto out;
948126430Snjl	}
949126430Snjl	res = (ACPI_OBJECT *)bcl_buf.Pointer;
950132605Snjl	if (!ACPI_PKG_VALID(res, 2)) {
951126430Snjl		printf("evaluation of %s._BCL makes no sense\n",
952126430Snjl		       acpi_name(handle));
953126430Snjl		goto out;
954126430Snjl	}
955126430Snjl	num = res->Package.Count;
956209065Sjkim	if (num < 2 || levelp == NULL)
957126430Snjl		goto out;
958132605Snjl	levels = AcpiOsAllocate(num * sizeof(*levels));
959209065Sjkim	if (levels == NULL)
960126430Snjl		goto out;
961132605Snjl	for (i = 0, n = 0; i < num; i++)
962132605Snjl		if (acpi_PkgInt32(res, i, &levels[n]) == 0)
963132605Snjl			n++;
964126430Snjl	if (n < 2) {
965126430Snjl		AcpiOsFree(levels);
966209065Sjkim		goto out;
967126430Snjl	}
968209065Sjkim	*levelp = levels;
969209065Sjkim	return (n);
970209065Sjkim
971126430Snjlout:
972126430Snjl	if (bcl_buf.Pointer != NULL)
973126430Snjl		AcpiOsFree(bcl_buf.Pointer);
974209065Sjkim	return (0);
975126430Snjl}
976126430Snjl
977203810Sjkimstatic int
978203810Sjkimvo_get_brightness(ACPI_HANDLE handle)
979203810Sjkim{
980203810Sjkim	UINT32 level;
981203810Sjkim	ACPI_STATUS status;
982203810Sjkim
983203810Sjkim	ACPI_SERIAL_ASSERT(video_output);
984203810Sjkim	status = acpi_GetInteger(handle, "_BQC", &level);
985203810Sjkim	if (ACPI_FAILURE(status)) {
986203810Sjkim		printf("can't evaluate %s._BQC - %s\n", acpi_name(handle),
987203810Sjkim		    AcpiFormatException(status));
988203810Sjkim		return (-1);
989203810Sjkim	}
990203810Sjkim	if (level > 100)
991203810Sjkim		return (-1);
992203810Sjkim
993203810Sjkim	return (level);
994203810Sjkim}
995203810Sjkim
996126430Snjlstatic void
997126430Snjlvo_set_brightness(ACPI_HANDLE handle, int level)
998126430Snjl{
999126430Snjl	ACPI_STATUS status;
1000126430Snjl
1001197648Sjhb	ACPI_SERIAL_ASSERT(video_output);
1002126560Snjl	status = acpi_SetInteger(handle, "_BCM", level);
1003126430Snjl	if (ACPI_FAILURE(status))
1004126430Snjl		printf("can't evaluate %s._BCM - %s\n",
1005126430Snjl		       acpi_name(handle), AcpiFormatException(status));
1006126430Snjl}
1007126430Snjl
1008126430Snjlstatic UINT32
1009126430Snjlvo_get_device_status(ACPI_HANDLE handle)
1010126430Snjl{
1011137438Snjl	UINT32 dcs;
1012126430Snjl	ACPI_STATUS status;
1013126430Snjl
1014197648Sjhb	ACPI_SERIAL_ASSERT(video_output);
1015137438Snjl	dcs = 0;
1016126560Snjl	status = acpi_GetInteger(handle, "_DCS", &dcs);
1017126430Snjl	if (ACPI_FAILURE(status))
1018126430Snjl		printf("can't evaluate %s._DCS - %s\n",
1019126430Snjl		       acpi_name(handle), AcpiFormatException(status));
1020126430Snjl
1021126430Snjl	return (dcs);
1022126430Snjl}
1023126430Snjl
1024126430Snjlstatic UINT32
1025133625Snjlvo_get_graphics_state(ACPI_HANDLE handle)
1026126430Snjl{
1027137438Snjl	UINT32 dgs;
1028126430Snjl	ACPI_STATUS status;
1029126430Snjl
1030137438Snjl	dgs = 0;
1031126560Snjl	status = acpi_GetInteger(handle, "_DGS", &dgs);
1032126430Snjl	if (ACPI_FAILURE(status))
1033126430Snjl		printf("can't evaluate %s._DGS - %s\n",
1034126430Snjl		       acpi_name(handle), AcpiFormatException(status));
1035126430Snjl
1036126430Snjl	return (dgs);
1037126430Snjl}
1038126430Snjl
1039126430Snjlstatic void
1040126430Snjlvo_set_device_state(ACPI_HANDLE handle, UINT32 state)
1041126430Snjl{
1042126430Snjl	ACPI_STATUS status;
1043126430Snjl
1044197648Sjhb	ACPI_SERIAL_ASSERT(video_output);
1045126560Snjl	status = acpi_SetInteger(handle, "_DSS", state);
1046126430Snjl	if (ACPI_FAILURE(status))
1047126430Snjl		printf("can't evaluate %s._DSS - %s\n",
1048126430Snjl		       acpi_name(handle), AcpiFormatException(status));
1049126430Snjl}
1050