xref: /freebsd-13-stable/sys/dev/acpica/acpi_video.c (revision 3bc80996974a61a4223eae4c1ccd47b6ee32a48a)
1 /*-
2  * Copyright (c) 2002-2003 Taku YAMAMOTO <taku@cent.saitama-u.ac.jp>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$Id: acpi_vid.c,v 1.4 2003/10/13 10:07:36 taku Exp $
27  */
28 
29 #include <sys/cdefs.h>
30 #include "opt_evdev.h"
31 
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/eventhandler.h>
35 #include <sys/kernel.h>
36 #include <sys/malloc.h>
37 #include <sys/module.h>
38 #include <sys/power.h>
39 #include <sys/queue.h>
40 #include <sys/sysctl.h>
41 
42 #include <contrib/dev/acpica/include/acpi.h>
43 
44 #include <dev/acpica/acpivar.h>
45 
46 #ifdef EVDEV_SUPPORT
47 #include <dev/evdev/input.h>
48 #include <dev/evdev/evdev.h>
49 #endif
50 
51 /* ACPI video extension driver. */
52 struct acpi_video_output {
53 	ACPI_HANDLE	handle;
54 	UINT32		adr;
55 	STAILQ_ENTRY(acpi_video_output) vo_next;
56 	struct {
57 		int	num;
58 		STAILQ_ENTRY(acpi_video_output) next;
59 	} vo_unit;
60 	int		vo_hasbqc;	/* Query method is present. */
61 	int		vo_level;	/* Cached level when !vo_hasbqc. */
62 	int		vo_brightness;
63 	int		vo_fullpower;
64 	int		vo_economy;
65 	int		vo_numlevels;
66 	int		*vo_levels;
67 	struct sysctl_ctx_list vo_sysctl_ctx;
68 	struct sysctl_oid *vo_sysctl_tree;
69 #ifdef EVDEV_SUPPORT
70 	struct evdev_dev *evdev;
71 #endif
72 };
73 
74 STAILQ_HEAD(acpi_video_output_queue, acpi_video_output);
75 
76 struct acpi_video_softc {
77 	device_t		device;
78 	ACPI_HANDLE		handle;
79 	struct acpi_video_output_queue vid_outputs;
80 	eventhandler_tag	vid_pwr_evh;
81 #ifdef EVDEV_SUPPORT
82 	struct evdev_dev	*evdev;
83 #endif
84 };
85 
86 /* interfaces */
87 static int	acpi_video_modevent(struct module*, int, void *);
88 static void	acpi_video_identify(driver_t *driver, device_t parent);
89 static int	acpi_video_probe(device_t);
90 static int	acpi_video_attach(device_t);
91 static int	acpi_video_detach(device_t);
92 static int	acpi_video_resume(device_t);
93 static int	acpi_video_shutdown(device_t);
94 static void	acpi_video_notify_handler(ACPI_HANDLE, UINT32, void *);
95 static void	acpi_video_power_profile(void *);
96 static void	acpi_video_bind_outputs(struct acpi_video_softc *);
97 static struct acpi_video_output *acpi_video_vo_init(UINT32);
98 static void	acpi_video_vo_bind(struct acpi_video_output *, ACPI_HANDLE);
99 static void	acpi_video_vo_destroy(struct acpi_video_output *);
100 static int	acpi_video_vo_check_level(struct acpi_video_output *, int);
101 static void	acpi_video_vo_notify_handler(ACPI_HANDLE, UINT32, void *);
102 static int	acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS);
103 static int	acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS);
104 static int	acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS);
105 static int	acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS);
106 
107 /* operations */
108 static void	vid_set_switch_policy(ACPI_HANDLE, UINT32);
109 static int	vid_enum_outputs(ACPI_HANDLE,
110 		    void(*)(ACPI_HANDLE, UINT32, void *), void *);
111 static int	vo_get_brightness_levels(ACPI_HANDLE, int **);
112 static int	vo_get_brightness(struct acpi_video_output *);
113 static void	vo_set_brightness(struct acpi_video_output *, int);
114 static UINT32	vo_get_device_status(ACPI_HANDLE);
115 static UINT32	vo_get_graphics_state(ACPI_HANDLE);
116 static void	vo_set_device_state(ACPI_HANDLE, UINT32);
117 
118 /* events */
119 #define	VID_NOTIFY_SWITCHED	0x80
120 #define	VID_NOTIFY_REPROBE	0x81
121 #define	VID_NOTIFY_CYCLE_OUT	0x82
122 #define	VID_NOTIFY_NEXT_OUT	0x83
123 #define	VID_NOTIFY_PREV_OUT	0x84
124 #define	VID_NOTIFY_CYCLE_BRN	0x85
125 #define	VID_NOTIFY_INC_BRN	0x86
126 #define	VID_NOTIFY_DEC_BRN	0x87
127 #define	VID_NOTIFY_ZERO_BRN	0x88
128 #define	VID_NOTIFY_DISP_OFF	0x89
129 
130 /* _DOS (Enable/Disable Output Switching) argument bits */
131 #define	DOS_SWITCH_MASK		3
132 #define	DOS_SWITCH_BY_OSPM	0
133 #define	DOS_SWITCH_BY_BIOS	1
134 #define	DOS_SWITCH_LOCKED	2
135 #define	DOS_BRIGHTNESS_BY_OSPM	(1 << 2)
136 
137 /* _DOD and subdev's _ADR */
138 #define	DOD_DEVID_MASK		0x0f00
139 #define	DOD_DEVID_MASK_FULL	0xffff
140 #define	DOD_DEVID_MASK_DISPIDX	0x000f
141 #define	DOD_DEVID_MASK_DISPPORT	0x00f0
142 #define	DOD_DEVID_MONITOR	0x0100
143 #define	DOD_DEVID_LCD		0x0110
144 #define	DOD_DEVID_TV		0x0200
145 #define	DOD_DEVID_EXT		0x0300
146 #define	DOD_DEVID_INTDFP	0x0400
147 #define	DOD_BIOS		(1 << 16)
148 #define	DOD_NONVGA		(1 << 17)
149 #define	DOD_HEAD_ID_SHIFT	18
150 #define	DOD_HEAD_ID_BITS	3
151 #define	DOD_HEAD_ID_MASK \
152 		(((1 << DOD_HEAD_ID_BITS) - 1) << DOD_HEAD_ID_SHIFT)
153 #define	DOD_DEVID_SCHEME_STD	(1U << 31)
154 
155 /* _BCL related constants */
156 #define	BCL_FULLPOWER		0
157 #define	BCL_ECONOMY		1
158 
159 /* _DCS (Device Currrent Status) value bits and masks. */
160 #define	DCS_EXISTS		(1 << 0)
161 #define	DCS_ACTIVE		(1 << 1)
162 #define	DCS_READY		(1 << 2)
163 #define	DCS_FUNCTIONAL		(1 << 3)
164 #define	DCS_ATTACHED		(1 << 4)
165 
166 /* _DSS (Device Set Status) argument bits and masks. */
167 #define	DSS_INACTIVE		0
168 #define	DSS_ACTIVE		(1 << 0)
169 #define	DSS_SETNEXT		(1 << 30)
170 #define	DSS_COMMIT		(1U << 31)
171 
172 static device_method_t acpi_video_methods[] = {
173 	DEVMETHOD(device_identify, acpi_video_identify),
174 	DEVMETHOD(device_probe, acpi_video_probe),
175 	DEVMETHOD(device_attach, acpi_video_attach),
176 	DEVMETHOD(device_detach, acpi_video_detach),
177 	DEVMETHOD(device_resume, acpi_video_resume),
178 	DEVMETHOD(device_shutdown, acpi_video_shutdown),
179 	{ 0, 0 }
180 };
181 
182 static driver_t acpi_video_driver = {
183 	"acpi_video",
184 	acpi_video_methods,
185 	sizeof(struct acpi_video_softc),
186 };
187 
188 static devclass_t acpi_video_devclass;
189 
190 DRIVER_MODULE(acpi_video, vgapci, acpi_video_driver, acpi_video_devclass,
191 	      acpi_video_modevent, NULL);
192 MODULE_DEPEND(acpi_video, acpi, 1, 1, 1);
193 #ifdef EVDEV_SUPPORT
194 MODULE_DEPEND(acpi_video, evdev, 1, 1, 1);
195 #endif
196 
197 static struct sysctl_ctx_list	acpi_video_sysctl_ctx;
198 static struct sysctl_oid	*acpi_video_sysctl_tree;
199 static struct acpi_video_output_queue crt_units, tv_units,
200     ext_units, lcd_units, other_units;
201 
202 /*
203  * The 'video' lock protects the hierarchy of video output devices
204  * (the video "bus").  The 'video_output' lock protects per-output
205  * data is equivalent to a softc lock for each video output.
206  */
207 ACPI_SERIAL_DECL(video, "ACPI video");
208 ACPI_SERIAL_DECL(video_output, "ACPI video output");
209 static MALLOC_DEFINE(M_ACPIVIDEO, "acpivideo", "ACPI video extension");
210 
211 #ifdef EVDEV_SUPPORT
212 static const struct {
213 	UINT32		notify;
214 	uint16_t	key;
215 } acpi_video_evdev_map[] = {
216 	{ VID_NOTIFY_SWITCHED,	KEY_SWITCHVIDEOMODE },
217 	{ VID_NOTIFY_REPROBE,	KEY_SWITCHVIDEOMODE },
218 	{ VID_NOTIFY_CYCLE_OUT,	KEY_SWITCHVIDEOMODE },
219 	{ VID_NOTIFY_NEXT_OUT,	KEY_VIDEO_NEXT },
220 	{ VID_NOTIFY_PREV_OUT,	KEY_VIDEO_PREV },
221 	{ VID_NOTIFY_CYCLE_BRN,	KEY_BRIGHTNESS_CYCLE },
222 	{ VID_NOTIFY_INC_BRN,	KEY_BRIGHTNESSUP },
223 	{ VID_NOTIFY_DEC_BRN,	KEY_BRIGHTNESSDOWN },
224 	{ VID_NOTIFY_ZERO_BRN,	KEY_BRIGHTNESS_ZERO },
225 	{ VID_NOTIFY_DISP_OFF,	KEY_DISPLAY_OFF },
226 };
227 
228 static void
acpi_video_push_evdev_event(struct evdev_dev * evdev,UINT32 notify)229 acpi_video_push_evdev_event(struct evdev_dev *evdev, UINT32 notify)
230 {
231 	int i;
232 	uint16_t key;
233 
234 	/* Do not allow to execute 2 instances this routine concurrently */
235 	ACPI_SERIAL_ASSERT(video_output);
236 
237 	for (i = 0; i < nitems(acpi_video_evdev_map); i++) {
238 		if (acpi_video_evdev_map[i].notify == notify) {
239 			key = acpi_video_evdev_map[i].key;
240 			evdev_push_key(evdev, key, 1);
241 			evdev_sync(evdev);
242 			evdev_push_key(evdev, key, 0);
243 			evdev_sync(evdev);
244 			break;
245 		}
246 	}
247 }
248 #endif
249 
250 static int
acpi_video_modevent(struct module * mod __unused,int evt,void * cookie __unused)251 acpi_video_modevent(struct module *mod __unused, int evt, void *cookie __unused)
252 {
253 	int err;
254 
255 	err = 0;
256 	switch (evt) {
257 	case MOD_LOAD:
258 		sysctl_ctx_init(&acpi_video_sysctl_ctx);
259 		STAILQ_INIT(&crt_units);
260 		STAILQ_INIT(&tv_units);
261 		STAILQ_INIT(&ext_units);
262 		STAILQ_INIT(&lcd_units);
263 		STAILQ_INIT(&other_units);
264 		break;
265 	case MOD_UNLOAD:
266 		sysctl_ctx_free(&acpi_video_sysctl_ctx);
267 		acpi_video_sysctl_tree = NULL;
268 		break;
269 	default:
270 		err = EINVAL;
271 	}
272 
273 	return (err);
274 }
275 
276 static void
acpi_video_identify(driver_t * driver,device_t parent)277 acpi_video_identify(driver_t *driver, device_t parent)
278 {
279 
280 	if (device_find_child(parent, "acpi_video", -1) == NULL)
281 		device_add_child(parent, "acpi_video", -1);
282 }
283 
284 static int
acpi_video_probe(device_t dev)285 acpi_video_probe(device_t dev)
286 {
287 	ACPI_HANDLE devh, h;
288 	ACPI_OBJECT_TYPE t_dos;
289 
290 	devh = acpi_get_handle(dev);
291 	if (acpi_disabled("video") ||
292 	    ACPI_FAILURE(AcpiGetHandle(devh, "_DOD", &h)) ||
293 	    ACPI_FAILURE(AcpiGetHandle(devh, "_DOS", &h)) ||
294 	    ACPI_FAILURE(AcpiGetType(h, &t_dos)) ||
295 	    t_dos != ACPI_TYPE_METHOD)
296 		return (ENXIO);
297 
298 	device_set_desc(dev, "ACPI video extension");
299 	return (0);
300 }
301 
302 static int
acpi_video_attach(device_t dev)303 acpi_video_attach(device_t dev)
304 {
305 	struct acpi_softc *acpi_sc;
306 	struct acpi_video_softc *sc;
307 #ifdef EVDEV_SUPPORT
308 	int i;
309 #endif
310 
311 	sc = device_get_softc(dev);
312 
313 	acpi_sc = devclass_get_softc(devclass_find("acpi"), 0);
314 	if (acpi_sc == NULL)
315 		return (ENXIO);
316 
317 #ifdef EVDEV_SUPPORT
318 	sc->evdev = evdev_alloc();
319 	evdev_set_name(sc->evdev, device_get_desc(dev));
320 	evdev_set_phys(sc->evdev, device_get_nameunit(dev));
321 	evdev_set_id(sc->evdev, BUS_HOST, 0, 0, 1);
322 	evdev_support_event(sc->evdev, EV_SYN);
323 	evdev_support_event(sc->evdev, EV_KEY);
324 	for (i = 0; i < nitems(acpi_video_evdev_map); i++)
325 		evdev_support_key(sc->evdev, acpi_video_evdev_map[i].key);
326 
327 	if (evdev_register(sc->evdev) != 0)
328 		return (ENXIO);
329 #endif
330 
331 	ACPI_SERIAL_BEGIN(video);
332 	if (acpi_video_sysctl_tree == NULL) {
333 		acpi_video_sysctl_tree = SYSCTL_ADD_NODE(&acpi_video_sysctl_ctx,
334 		    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
335 		    "video", CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
336 		    "video extension control");
337 	}
338 	ACPI_SERIAL_END(video);
339 
340 	sc->device = dev;
341 	sc->handle = acpi_get_handle(dev);
342 	STAILQ_INIT(&sc->vid_outputs);
343 
344 	AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
345 				 acpi_video_notify_handler, sc);
346 	sc->vid_pwr_evh = EVENTHANDLER_REGISTER(power_profile_change,
347 				 acpi_video_power_profile, sc, 0);
348 
349 	ACPI_SERIAL_BEGIN(video);
350 	acpi_video_bind_outputs(sc);
351 	ACPI_SERIAL_END(video);
352 
353 	/*
354 	 * Notify the BIOS that we want to switch both active outputs and
355 	 * brightness levels.
356 	 */
357 	vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_OSPM |
358 	    DOS_BRIGHTNESS_BY_OSPM);
359 
360 	acpi_video_power_profile(sc);
361 
362 	return (0);
363 }
364 
365 static int
acpi_video_detach(device_t dev)366 acpi_video_detach(device_t dev)
367 {
368 	struct acpi_video_softc *sc;
369 	struct acpi_video_output *vo, *vn;
370 
371 	sc = device_get_softc(dev);
372 
373 	vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS);
374 	EVENTHANDLER_DEREGISTER(power_profile_change, sc->vid_pwr_evh);
375 	AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
376 				acpi_video_notify_handler);
377 
378 	ACPI_SERIAL_BEGIN(video);
379 	STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vn) {
380 		acpi_video_vo_destroy(vo);
381 	}
382 	ACPI_SERIAL_END(video);
383 
384 #ifdef EVDEV_SUPPORT
385 	evdev_free(sc->evdev);
386 #endif
387 
388 	return (0);
389 }
390 
391 static int
acpi_video_resume(device_t dev)392 acpi_video_resume(device_t dev)
393 {
394 	struct acpi_video_softc *sc;
395 	struct acpi_video_output *vo, *vn;
396 	int level;
397 
398 	sc = device_get_softc(dev);
399 
400 	/* Restore brightness level */
401 	ACPI_SERIAL_BEGIN(video);
402 	ACPI_SERIAL_BEGIN(video_output);
403 	STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vn) {
404 		if ((vo->adr & DOD_DEVID_MASK_FULL) != DOD_DEVID_LCD &&
405 		    (vo->adr & DOD_DEVID_MASK) != DOD_DEVID_INTDFP)
406 			continue;
407 
408 		if ((vo_get_device_status(vo->handle) & DCS_ACTIVE) == 0)
409 			continue;
410 
411 		level = vo_get_brightness(vo);
412 		if (level != -1)
413 			vo_set_brightness(vo, level);
414 	}
415 	ACPI_SERIAL_END(video_output);
416 	ACPI_SERIAL_END(video);
417 
418 	return (0);
419 }
420 
421 static int
acpi_video_shutdown(device_t dev)422 acpi_video_shutdown(device_t dev)
423 {
424 	struct acpi_video_softc *sc;
425 
426 	sc = device_get_softc(dev);
427 	vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS);
428 
429 	return (0);
430 }
431 
432 static void
acpi_video_invoke_event_handler(void * context)433 acpi_video_invoke_event_handler(void *context)
434 {
435 	EVENTHANDLER_INVOKE(acpi_video_event, (int)(intptr_t)context);
436 }
437 
438 static void
acpi_video_notify_handler(ACPI_HANDLE handle,UINT32 notify,void * context)439 acpi_video_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context)
440 {
441 	struct acpi_video_softc *sc;
442 	struct acpi_video_output *vo, *vo_tmp;
443 	ACPI_HANDLE lasthand;
444 	UINT32 dcs, dss, dss_p;
445 
446 	sc = (struct acpi_video_softc *)context;
447 
448 	switch (notify) {
449 	case VID_NOTIFY_SWITCHED:
450 		dss_p = 0;
451 		lasthand = NULL;
452 		ACPI_SERIAL_BEGIN(video);
453 		ACPI_SERIAL_BEGIN(video_output);
454 		STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) {
455 			dss = vo_get_graphics_state(vo->handle);
456 			dcs = vo_get_device_status(vo->handle);
457 			if (!(dcs & DCS_READY))
458 				dss = DSS_INACTIVE;
459 			if (((dcs & DCS_ACTIVE) && dss == DSS_INACTIVE) ||
460 			    (!(dcs & DCS_ACTIVE) && dss == DSS_ACTIVE)) {
461 				if (lasthand != NULL)
462 					vo_set_device_state(lasthand, dss_p);
463 				dss_p = dss;
464 				lasthand = vo->handle;
465 			}
466 		}
467 		if (lasthand != NULL)
468 			vo_set_device_state(lasthand, dss_p|DSS_COMMIT);
469 		ACPI_SERIAL_END(video_output);
470 		ACPI_SERIAL_END(video);
471 		break;
472 	case VID_NOTIFY_REPROBE:
473 		ACPI_SERIAL_BEGIN(video);
474 		STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next)
475 			vo->handle = NULL;
476 		acpi_video_bind_outputs(sc);
477 		STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vo_tmp) {
478 			if (vo->handle == NULL) {
479 				STAILQ_REMOVE(&sc->vid_outputs, vo,
480 				    acpi_video_output, vo_next);
481 				acpi_video_vo_destroy(vo);
482 			}
483 		}
484 		ACPI_SERIAL_END(video);
485 		break;
486 	/* Next events should not appear if DOS_SWITCH_BY_OSPM policy is set */
487 	case VID_NOTIFY_CYCLE_OUT:
488 	case VID_NOTIFY_NEXT_OUT:
489 	case VID_NOTIFY_PREV_OUT:
490 	default:
491 		device_printf(sc->device, "unknown notify event 0x%x\n",
492 		    notify);
493 	}
494 	AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_video_invoke_event_handler,
495 	    (void *)(uintptr_t)notify);
496 #ifdef EVDEV_SUPPORT
497 	ACPI_SERIAL_BEGIN(video_output);
498 	acpi_video_push_evdev_event(sc->evdev, notify);
499 	ACPI_SERIAL_END(video_output);
500 #endif
501 }
502 
503 static void
acpi_video_power_profile(void * context)504 acpi_video_power_profile(void *context)
505 {
506 	int state;
507 	struct acpi_video_softc *sc;
508 	struct acpi_video_output *vo;
509 
510 	sc = context;
511 	state = power_profile_get_state();
512 	if (state != POWER_PROFILE_PERFORMANCE &&
513 	    state != POWER_PROFILE_ECONOMY)
514 		return;
515 
516 	ACPI_SERIAL_BEGIN(video);
517 	ACPI_SERIAL_BEGIN(video_output);
518 	STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) {
519 		if (vo->vo_levels != NULL && vo->vo_brightness == -1)
520 			vo_set_brightness(vo,
521 			    state == POWER_PROFILE_ECONOMY ?
522 			    vo->vo_economy : vo->vo_fullpower);
523 	}
524 	ACPI_SERIAL_END(video_output);
525 	ACPI_SERIAL_END(video);
526 }
527 
528 static void
acpi_video_bind_outputs_subr(ACPI_HANDLE handle,UINT32 adr,void * context)529 acpi_video_bind_outputs_subr(ACPI_HANDLE handle, UINT32 adr, void *context)
530 {
531 	struct acpi_video_softc *sc;
532 	struct acpi_video_output *vo;
533 
534 	ACPI_SERIAL_ASSERT(video);
535 	sc = context;
536 
537 	STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) {
538 		if (vo->adr == adr) {
539 			acpi_video_vo_bind(vo, handle);
540 			return;
541 		}
542 	}
543 	vo = acpi_video_vo_init(adr);
544 	if (vo != NULL) {
545 #ifdef EVDEV_SUPPORT
546 		vo->evdev = sc->evdev;
547 #endif
548 		acpi_video_vo_bind(vo, handle);
549 		STAILQ_INSERT_TAIL(&sc->vid_outputs, vo, vo_next);
550 	}
551 }
552 
553 static void
acpi_video_bind_outputs(struct acpi_video_softc * sc)554 acpi_video_bind_outputs(struct acpi_video_softc *sc)
555 {
556 
557 	ACPI_SERIAL_ASSERT(video);
558 	vid_enum_outputs(sc->handle, acpi_video_bind_outputs_subr, sc);
559 }
560 
561 static struct acpi_video_output *
acpi_video_vo_init(UINT32 adr)562 acpi_video_vo_init(UINT32 adr)
563 {
564 	struct acpi_video_output *vn, *vo, *vp;
565 	int n, x;
566 	char name[8], env[32];
567 	const char *type, *desc;
568 	struct acpi_video_output_queue *voqh;
569 
570 	ACPI_SERIAL_ASSERT(video);
571 
572 	switch (adr & DOD_DEVID_MASK) {
573 	case DOD_DEVID_MONITOR:
574 		if ((adr & DOD_DEVID_MASK_FULL) == DOD_DEVID_LCD) {
575 			/* DOD_DEVID_LCD is a common, backward compatible ID */
576 			desc = "Internal/Integrated Digital Flat Panel";
577 			type = "lcd";
578 			voqh = &lcd_units;
579 		} else {
580 			desc = "VGA CRT or VESA Compatible Analog Monitor";
581 			type = "crt";
582 			voqh = &crt_units;
583 		}
584 		break;
585 	case DOD_DEVID_TV:
586 		desc = "TV/HDTV or Analog-Video Monitor";
587 		type = "tv";
588 		voqh = &tv_units;
589 		break;
590 	case DOD_DEVID_EXT:
591 		desc = "External Digital Monitor";
592 		type = "ext";
593 		voqh = &ext_units;
594 		break;
595 	case DOD_DEVID_INTDFP:
596 		desc = "Internal/Integrated Digital Flat Panel";
597 		type = "lcd";
598 		voqh = &lcd_units;
599 		break;
600 	default:
601 		desc = "unknown output";
602 		type = "out";
603 		voqh = &other_units;
604 	}
605 
606 	n = 0;
607 	vp = NULL;
608 	STAILQ_FOREACH(vn, voqh, vo_unit.next) {
609 		if (vn->vo_unit.num != n)
610 			break;
611 		vp = vn;
612 		n++;
613 	}
614 
615 	snprintf(name, sizeof(name), "%s%d", type, n);
616 
617 	vo = malloc(sizeof(*vo), M_ACPIVIDEO, M_NOWAIT);
618 	if (vo != NULL) {
619 		vo->handle = NULL;
620 		vo->adr = adr;
621 		vo->vo_unit.num = n;
622 		vo->vo_hasbqc = -1;
623 		vo->vo_level = -1;
624 		vo->vo_brightness = -1;
625 		vo->vo_fullpower = -1;	/* TODO: override with tunables */
626 		vo->vo_economy = -1;
627 		vo->vo_numlevels = 0;
628 		vo->vo_levels = NULL;
629 		snprintf(env, sizeof(env), "hw.acpi.video.%s.fullpower", name);
630 		if (getenv_int(env, &x))
631 			vo->vo_fullpower = x;
632 		snprintf(env, sizeof(env), "hw.acpi.video.%s.economy", name);
633 		if (getenv_int(env, &x))
634 			vo->vo_economy = x;
635 
636 		sysctl_ctx_init(&vo->vo_sysctl_ctx);
637 		if (vp != NULL)
638 			STAILQ_INSERT_AFTER(voqh, vp, vo, vo_unit.next);
639 		else
640 			STAILQ_INSERT_TAIL(voqh, vo, vo_unit.next);
641 		if (acpi_video_sysctl_tree != NULL)
642 			vo->vo_sysctl_tree =
643 			    SYSCTL_ADD_NODE(&vo->vo_sysctl_ctx,
644 				SYSCTL_CHILDREN(acpi_video_sysctl_tree),
645 				OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE,
646 				0, desc);
647 		if (vo->vo_sysctl_tree != NULL) {
648 			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
649 			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
650 			    OID_AUTO, "active",
651 			    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, vo,
652 			    0, acpi_video_vo_active_sysctl, "I",
653 			    "current activity of this device");
654 			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
655 			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
656 			    OID_AUTO, "brightness",
657 			    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, vo,
658 			    0, acpi_video_vo_bright_sysctl, "I",
659 			    "current brightness level");
660 			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
661 			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
662 			    OID_AUTO, "fullpower",
663 			    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, vo,
664 			    POWER_PROFILE_PERFORMANCE,
665 			    acpi_video_vo_presets_sysctl, "I",
666 			    "preset level for full power mode");
667 			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
668 			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
669 			    OID_AUTO, "economy",
670 			    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, vo,
671 			    POWER_PROFILE_ECONOMY,
672 			    acpi_video_vo_presets_sysctl, "I",
673 			    "preset level for economy mode");
674 			SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx,
675 			    SYSCTL_CHILDREN(vo->vo_sysctl_tree),
676 			    OID_AUTO, "levels",
677 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, vo,
678 			    0, acpi_video_vo_levels_sysctl, "I",
679 			    "supported brightness levels");
680 		} else
681 			printf("%s: sysctl node creation failed\n", type);
682 	} else
683 		printf("%s: softc allocation failed\n", type);
684 
685 	if (bootverbose) {
686 		printf("found %s(%x)", desc, adr & DOD_DEVID_MASK_FULL);
687 		printf(", idx#%x", adr & DOD_DEVID_MASK_DISPIDX);
688 		printf(", port#%x", (adr & DOD_DEVID_MASK_DISPPORT) >> 4);
689 		if (adr & DOD_BIOS)
690 			printf(", detectable by BIOS");
691 		if (adr & DOD_NONVGA)
692 			printf(" (Non-VGA output device whose power "
693 			    "is related to the VGA device)");
694 		printf(", head #%d\n",
695 			(adr & DOD_HEAD_ID_MASK) >> DOD_HEAD_ID_SHIFT);
696 	}
697 	return (vo);
698 }
699 
700 static void
acpi_video_vo_bind(struct acpi_video_output * vo,ACPI_HANDLE handle)701 acpi_video_vo_bind(struct acpi_video_output *vo, ACPI_HANDLE handle)
702 {
703 
704 	ACPI_SERIAL_BEGIN(video_output);
705 	if (vo->vo_levels != NULL) {
706 		AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY,
707 		    acpi_video_vo_notify_handler);
708 		AcpiOsFree(vo->vo_levels);
709 		vo->vo_levels = NULL;
710 	}
711 	vo->handle = handle;
712 	vo->vo_numlevels = vo_get_brightness_levels(handle, &vo->vo_levels);
713 	if (vo->vo_numlevels >= 2) {
714 		if (vo->vo_fullpower == -1 ||
715 		    acpi_video_vo_check_level(vo, vo->vo_fullpower) != 0) {
716 			/* XXX - can't deal with rebinding... */
717 			vo->vo_fullpower = vo->vo_levels[BCL_FULLPOWER];
718 		}
719 		if (vo->vo_economy == -1 ||
720 		    acpi_video_vo_check_level(vo, vo->vo_economy) != 0) {
721 			/* XXX - see above. */
722 			vo->vo_economy = vo->vo_levels[BCL_ECONOMY];
723 		}
724 		AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY,
725 		    acpi_video_vo_notify_handler, vo);
726 	}
727 	ACPI_SERIAL_END(video_output);
728 }
729 
730 static void
acpi_video_vo_destroy(struct acpi_video_output * vo)731 acpi_video_vo_destroy(struct acpi_video_output *vo)
732 {
733 	struct acpi_video_output_queue *voqh;
734 
735 	ACPI_SERIAL_ASSERT(video);
736 	if (vo->vo_sysctl_tree != NULL) {
737 		vo->vo_sysctl_tree = NULL;
738 		sysctl_ctx_free(&vo->vo_sysctl_ctx);
739 	}
740 	if (vo->vo_levels != NULL) {
741 		AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY,
742 		    acpi_video_vo_notify_handler);
743 		AcpiOsFree(vo->vo_levels);
744 	}
745 
746 	switch (vo->adr & DOD_DEVID_MASK) {
747 	case DOD_DEVID_MONITOR:
748 		if ((vo->adr & DOD_DEVID_MASK_FULL) == DOD_DEVID_LCD)
749 			voqh = &lcd_units;
750 		else
751 			voqh = &crt_units;
752 		break;
753 	case DOD_DEVID_TV:
754 		voqh = &tv_units;
755 		break;
756 	case DOD_DEVID_EXT:
757 		voqh = &ext_units;
758 		break;
759 	case DOD_DEVID_INTDFP:
760 		voqh = &lcd_units;
761 		break;
762 	default:
763 		voqh = &other_units;
764 	}
765 	STAILQ_REMOVE(voqh, vo, acpi_video_output, vo_unit.next);
766 	free(vo, M_ACPIVIDEO);
767 }
768 
769 static int
acpi_video_vo_check_level(struct acpi_video_output * vo,int level)770 acpi_video_vo_check_level(struct acpi_video_output *vo, int level)
771 {
772 	int i;
773 
774 	ACPI_SERIAL_ASSERT(video_output);
775 	if (vo->vo_levels == NULL)
776 		return (ENODEV);
777 	for (i = 0; i < vo->vo_numlevels; i++)
778 		if (vo->vo_levels[i] == level)
779 			return (0);
780 	return (EINVAL);
781 }
782 
783 static void
acpi_video_vo_notify_handler(ACPI_HANDLE handle,UINT32 notify,void * context)784 acpi_video_vo_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context)
785 {
786 	struct acpi_video_output *vo;
787 	int i, j, level, new_level;
788 
789 	vo = context;
790 	ACPI_SERIAL_BEGIN(video_output);
791 	if (vo->handle != handle)
792 		goto out;
793 
794 	switch (notify) {
795 	case VID_NOTIFY_CYCLE_BRN:
796 		if (vo->vo_numlevels <= 3)
797 			goto out;
798 		/* FALLTHROUGH */
799 	case VID_NOTIFY_INC_BRN:
800 	case VID_NOTIFY_DEC_BRN:
801 	case VID_NOTIFY_ZERO_BRN:
802 	case VID_NOTIFY_DISP_OFF:
803 		if (vo->vo_levels == NULL)
804 			goto out;
805 		level = vo_get_brightness(vo);
806 		if (level < 0)
807 			goto out;
808 		break;
809 	default:
810 		printf("unknown notify event 0x%x from %s\n",
811 		    notify, acpi_name(handle));
812 		goto out;
813 	}
814 
815 	new_level = level;
816 	switch (notify) {
817 	case VID_NOTIFY_CYCLE_BRN:
818 		for (i = 2; i < vo->vo_numlevels; i++)
819 			if (vo->vo_levels[i] == level) {
820 				new_level = vo->vo_numlevels > i + 1 ?
821 				     vo->vo_levels[i + 1] : vo->vo_levels[2];
822 				break;
823 			}
824 		break;
825 	case VID_NOTIFY_INC_BRN:
826 	case VID_NOTIFY_DEC_BRN:
827 		for (i = 0; i < vo->vo_numlevels; i++) {
828 			j = vo->vo_levels[i];
829 			if (notify == VID_NOTIFY_INC_BRN) {
830 				if (j > level &&
831 				    (j < new_level || level == new_level))
832 					new_level = j;
833 			} else {
834 				if (j < level &&
835 				    (j > new_level || level == new_level))
836 					new_level = j;
837 			}
838 		}
839 		break;
840 	case VID_NOTIFY_ZERO_BRN:
841 		for (i = 0; i < vo->vo_numlevels; i++)
842 			if (vo->vo_levels[i] == 0) {
843 				new_level = 0;
844 				break;
845 			}
846 		break;
847 	case VID_NOTIFY_DISP_OFF:
848 		acpi_pwr_switch_consumer(handle, ACPI_STATE_D3);
849 		break;
850 	}
851 	if (new_level != level) {
852 		vo_set_brightness(vo, new_level);
853 		vo->vo_brightness = new_level;
854 	}
855 #ifdef EVDEV_SUPPORT
856 	acpi_video_push_evdev_event(vo->evdev, notify);
857 #endif
858 
859 out:
860 	ACPI_SERIAL_END(video_output);
861 
862 	AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_video_invoke_event_handler,
863 	    (void *)(uintptr_t)notify);
864 }
865 
866 /* ARGSUSED */
867 static int
acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS)868 acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS)
869 {
870 	struct acpi_video_output *vo;
871 	int state, err;
872 
873 	vo = (struct acpi_video_output *)arg1;
874 	if (vo->handle == NULL)
875 		return (ENXIO);
876 	ACPI_SERIAL_BEGIN(video_output);
877 	state = (vo_get_device_status(vo->handle) & DCS_ACTIVE) ? 1 : 0;
878 	err = sysctl_handle_int(oidp, &state, 0, req);
879 	if (err != 0 || req->newptr == NULL)
880 		goto out;
881 	vo_set_device_state(vo->handle,
882 	    DSS_COMMIT | (state ? DSS_ACTIVE : DSS_INACTIVE));
883 out:
884 	ACPI_SERIAL_END(video_output);
885 	return (err);
886 }
887 
888 /* ARGSUSED */
889 static int
acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS)890 acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS)
891 {
892 	struct acpi_video_output *vo;
893 	int level, preset, err;
894 
895 	vo = (struct acpi_video_output *)arg1;
896 	ACPI_SERIAL_BEGIN(video_output);
897 	if (vo->handle == NULL) {
898 		err = ENXIO;
899 		goto out;
900 	}
901 	if (vo->vo_levels == NULL) {
902 		err = ENODEV;
903 		goto out;
904 	}
905 
906 	preset = (power_profile_get_state() == POWER_PROFILE_ECONOMY) ?
907 		  vo->vo_economy : vo->vo_fullpower;
908 	level = vo->vo_brightness;
909 	if (level == -1)
910 		level = preset;
911 
912 	err = sysctl_handle_int(oidp, &level, 0, req);
913 	if (err != 0 || req->newptr == NULL)
914 		goto out;
915 	if (level < -1 || level > 100) {
916 		err = EINVAL;
917 		goto out;
918 	}
919 
920 	if (level != -1 && (err = acpi_video_vo_check_level(vo, level)))
921 		goto out;
922 	vo->vo_brightness = level;
923 	vo_set_brightness(vo, (level == -1) ? preset : level);
924 
925 out:
926 	ACPI_SERIAL_END(video_output);
927 	return (err);
928 }
929 
930 static int
acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS)931 acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS)
932 {
933 	struct acpi_video_output *vo;
934 	int i, level, *preset, err;
935 
936 	vo = (struct acpi_video_output *)arg1;
937 	ACPI_SERIAL_BEGIN(video_output);
938 	if (vo->handle == NULL) {
939 		err = ENXIO;
940 		goto out;
941 	}
942 	if (vo->vo_levels == NULL) {
943 		err = ENODEV;
944 		goto out;
945 	}
946 	preset = (arg2 == POWER_PROFILE_ECONOMY) ?
947 		  &vo->vo_economy : &vo->vo_fullpower;
948 	level = *preset;
949 	err = sysctl_handle_int(oidp, &level, 0, req);
950 	if (err != 0 || req->newptr == NULL)
951 		goto out;
952 	if (level < -1 || level > 100) {
953 		err = EINVAL;
954 		goto out;
955 	}
956 	if (level == -1) {
957 		i = (arg2 == POWER_PROFILE_ECONOMY) ?
958 		    BCL_ECONOMY : BCL_FULLPOWER;
959 		level = vo->vo_levels[i];
960 	} else if ((err = acpi_video_vo_check_level(vo, level)) != 0)
961 		goto out;
962 
963 	if (vo->vo_brightness == -1 && (power_profile_get_state() == arg2))
964 		vo_set_brightness(vo, level);
965 	*preset = level;
966 
967 out:
968 	ACPI_SERIAL_END(video_output);
969 	return (err);
970 }
971 
972 /* ARGSUSED */
973 static int
acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS)974 acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS)
975 {
976 	struct acpi_video_output *vo;
977 	int err;
978 
979 	vo = (struct acpi_video_output *)arg1;
980 	ACPI_SERIAL_BEGIN(video_output);
981 	if (vo->vo_levels == NULL) {
982 		err = ENODEV;
983 		goto out;
984 	}
985 	if (req->newptr != NULL) {
986 		err = EPERM;
987 		goto out;
988 	}
989 	err = sysctl_handle_opaque(oidp, vo->vo_levels,
990 	    vo->vo_numlevels * sizeof(*vo->vo_levels), req);
991 
992 out:
993 	ACPI_SERIAL_END(video_output);
994 	return (err);
995 }
996 
997 static void
vid_set_switch_policy(ACPI_HANDLE handle,UINT32 policy)998 vid_set_switch_policy(ACPI_HANDLE handle, UINT32 policy)
999 {
1000 	ACPI_STATUS status;
1001 
1002 	status = acpi_SetInteger(handle, "_DOS", policy);
1003 	if (ACPI_FAILURE(status))
1004 		printf("can't evaluate %s._DOS - %s\n",
1005 		       acpi_name(handle), AcpiFormatException(status));
1006 }
1007 
1008 struct enum_callback_arg {
1009 	void (*callback)(ACPI_HANDLE, UINT32, void *);
1010 	void *context;
1011 	ACPI_OBJECT *dod_pkg;
1012 	int count;
1013 };
1014 
1015 static ACPI_STATUS
vid_enum_outputs_subr(ACPI_HANDLE handle,UINT32 level __unused,void * context,void ** retp __unused)1016 vid_enum_outputs_subr(ACPI_HANDLE handle, UINT32 level __unused,
1017 		      void *context, void **retp __unused)
1018 {
1019 	ACPI_STATUS status;
1020 	UINT32 adr, val;
1021 	struct enum_callback_arg *argset;
1022 	size_t i;
1023 
1024 	ACPI_SERIAL_ASSERT(video);
1025 	argset = context;
1026 	status = acpi_GetInteger(handle, "_ADR", &adr);
1027 	if (ACPI_FAILURE(status))
1028 		return (AE_OK);
1029 
1030 	for (i = 0; i < argset->dod_pkg->Package.Count; i++) {
1031 		if (acpi_PkgInt32(argset->dod_pkg, i, &val) == 0 &&
1032 		    (val & DOD_DEVID_MASK_FULL) ==
1033 		    (adr & DOD_DEVID_MASK_FULL)) {
1034 			argset->callback(handle, val, argset->context);
1035 			argset->count++;
1036 		}
1037 	}
1038 
1039 	return (AE_OK);
1040 }
1041 
1042 static int
vid_enum_outputs(ACPI_HANDLE handle,void (* callback)(ACPI_HANDLE,UINT32,void *),void * context)1043 vid_enum_outputs(ACPI_HANDLE handle,
1044 		 void (*callback)(ACPI_HANDLE, UINT32, void *), void *context)
1045 {
1046 	ACPI_STATUS status;
1047 	ACPI_BUFFER dod_buf;
1048 	ACPI_OBJECT *res;
1049 	struct enum_callback_arg argset;
1050 
1051 	ACPI_SERIAL_ASSERT(video);
1052 	dod_buf.Length = ACPI_ALLOCATE_BUFFER;
1053 	dod_buf.Pointer = NULL;
1054 	status = AcpiEvaluateObject(handle, "_DOD", NULL, &dod_buf);
1055 	if (ACPI_FAILURE(status)) {
1056 		if (status != AE_NOT_FOUND)
1057 			printf("can't evaluate %s._DOD - %s\n",
1058 			       acpi_name(handle), AcpiFormatException(status));
1059 		argset.count = -1;
1060 		goto out;
1061 	}
1062 	res = (ACPI_OBJECT *)dod_buf.Pointer;
1063 	if (!ACPI_PKG_VALID(res, 1)) {
1064 		printf("evaluation of %s._DOD makes no sense\n",
1065 		       acpi_name(handle));
1066 		argset.count = -1;
1067 		goto out;
1068 	}
1069 	if (callback == NULL) {
1070 		argset.count = res->Package.Count;
1071 		goto out;
1072 	}
1073 	argset.callback = callback;
1074 	argset.context  = context;
1075 	argset.dod_pkg  = res;
1076 	argset.count    = 0;
1077 	status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1,
1078 	    vid_enum_outputs_subr, NULL, &argset, NULL);
1079 	if (ACPI_FAILURE(status))
1080 		printf("failed walking down %s - %s\n",
1081 		       acpi_name(handle), AcpiFormatException(status));
1082 out:
1083 	if (dod_buf.Pointer != NULL)
1084 		AcpiOsFree(dod_buf.Pointer);
1085 	return (argset.count);
1086 }
1087 
1088 static int
vo_get_brightness_levels(ACPI_HANDLE handle,int ** levelp)1089 vo_get_brightness_levels(ACPI_HANDLE handle, int **levelp)
1090 {
1091 	ACPI_STATUS status;
1092 	ACPI_BUFFER bcl_buf;
1093 	ACPI_OBJECT *res;
1094 	int num, i, n, *levels;
1095 
1096 	bcl_buf.Length = ACPI_ALLOCATE_BUFFER;
1097 	bcl_buf.Pointer = NULL;
1098 	status = AcpiEvaluateObject(handle, "_BCL", NULL, &bcl_buf);
1099 	if (ACPI_FAILURE(status)) {
1100 		if (status != AE_NOT_FOUND)
1101 			printf("can't evaluate %s._BCL - %s\n",
1102 			       acpi_name(handle), AcpiFormatException(status));
1103 		goto out;
1104 	}
1105 	res = (ACPI_OBJECT *)bcl_buf.Pointer;
1106 	if (!ACPI_PKG_VALID(res, 2)) {
1107 		printf("evaluation of %s._BCL makes no sense\n",
1108 		       acpi_name(handle));
1109 		goto out;
1110 	}
1111 	num = res->Package.Count;
1112 	if (num < 2 || levelp == NULL)
1113 		goto out;
1114 	levels = AcpiOsAllocate(num * sizeof(*levels));
1115 	if (levels == NULL)
1116 		goto out;
1117 	for (i = 0, n = 0; i < num; i++)
1118 		if (acpi_PkgInt32(res, i, &levels[n]) == 0)
1119 			n++;
1120 	if (n < 2) {
1121 		AcpiOsFree(levels);
1122 		goto out;
1123 	}
1124 	*levelp = levels;
1125 	return (n);
1126 
1127 out:
1128 	if (bcl_buf.Pointer != NULL)
1129 		AcpiOsFree(bcl_buf.Pointer);
1130 	return (0);
1131 }
1132 
1133 static int
vo_get_bqc(struct acpi_video_output * vo,UINT32 * level)1134 vo_get_bqc(struct acpi_video_output *vo, UINT32 *level)
1135 {
1136 	ACPI_STATUS status;
1137 
1138 	switch (vo->vo_hasbqc) {
1139 	case 1:
1140 	case -1:
1141 		status = acpi_GetInteger(vo->handle, "_BQC", level);
1142 		if (vo->vo_hasbqc == 1)
1143 			break;
1144 		vo->vo_hasbqc = status != AE_NOT_FOUND;
1145 		if (vo->vo_hasbqc == 1)
1146 			break;
1147 		/* FALLTHROUGH */
1148 	default:
1149 		KASSERT(vo->vo_hasbqc == 0,
1150 		    ("bad vo_hasbqc state %d", vo->vo_hasbqc));
1151 		*level = vo->vo_level;
1152 		status = AE_OK;
1153 	}
1154 	return (status);
1155 }
1156 
1157 static int
vo_get_brightness(struct acpi_video_output * vo)1158 vo_get_brightness(struct acpi_video_output *vo)
1159 {
1160 	UINT32 level;
1161 	ACPI_STATUS status;
1162 
1163 	ACPI_SERIAL_ASSERT(video_output);
1164 	status = vo_get_bqc(vo, &level);
1165 	if (ACPI_FAILURE(status)) {
1166 		printf("can't evaluate %s._BQC - %s\n", acpi_name(vo->handle),
1167 		    AcpiFormatException(status));
1168 		return (-1);
1169 	}
1170 	if (level > 100)
1171 		return (-1);
1172 
1173 	return (level);
1174 }
1175 
1176 static void
vo_set_brightness(struct acpi_video_output * vo,int level)1177 vo_set_brightness(struct acpi_video_output *vo, int level)
1178 {
1179 	char notify_buf[16];
1180 	ACPI_STATUS status;
1181 
1182 	ACPI_SERIAL_ASSERT(video_output);
1183 	status = acpi_SetInteger(vo->handle, "_BCM", level);
1184 	if (ACPI_FAILURE(status)) {
1185 		printf("can't evaluate %s._BCM - %s\n",
1186 		    acpi_name(vo->handle), AcpiFormatException(status));
1187 	} else {
1188 		vo->vo_level = level;
1189 	}
1190 	snprintf(notify_buf, sizeof(notify_buf), "notify=%d", level);
1191 	devctl_notify("ACPI", "Video", "brightness", notify_buf);
1192 }
1193 
1194 static UINT32
vo_get_device_status(ACPI_HANDLE handle)1195 vo_get_device_status(ACPI_HANDLE handle)
1196 {
1197 	UINT32 dcs;
1198 	ACPI_STATUS status;
1199 
1200 	ACPI_SERIAL_ASSERT(video_output);
1201 	dcs = 0;
1202 	status = acpi_GetInteger(handle, "_DCS", &dcs);
1203 	if (ACPI_FAILURE(status)) {
1204 		/*
1205 		 * If the method is missing, assume that the device is always
1206 		 * operational.
1207 		 */
1208 		if (status != AE_NOT_FOUND) {
1209 			printf("can't evaluate %s._DCS - %s\n",
1210 			    acpi_name(handle), AcpiFormatException(status));
1211 		} else {
1212 			dcs = 0xff;
1213 		}
1214 	}
1215 
1216 	return (dcs);
1217 }
1218 
1219 static UINT32
vo_get_graphics_state(ACPI_HANDLE handle)1220 vo_get_graphics_state(ACPI_HANDLE handle)
1221 {
1222 	UINT32 dgs;
1223 	ACPI_STATUS status;
1224 
1225 	dgs = 0;
1226 	status = acpi_GetInteger(handle, "_DGS", &dgs);
1227 	if (ACPI_FAILURE(status)) {
1228 		/*
1229 		 * If the method is missing, assume that the device is always
1230 		 * operational.
1231 		 */
1232 		if (status != AE_NOT_FOUND) {
1233 			printf("can't evaluate %s._DGS - %s\n",
1234 			    acpi_name(handle), AcpiFormatException(status));
1235 		} else {
1236 			dgs = 0xff;
1237 		}
1238 	}
1239 
1240 	return (dgs);
1241 }
1242 
1243 static void
vo_set_device_state(ACPI_HANDLE handle,UINT32 state)1244 vo_set_device_state(ACPI_HANDLE handle, UINT32 state)
1245 {
1246 	ACPI_STATUS status;
1247 
1248 	ACPI_SERIAL_ASSERT(video_output);
1249 	status = acpi_SetInteger(handle, "_DSS", state);
1250 	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
1251 		printf("can't evaluate %s._DSS - %s\n",
1252 		    acpi_name(handle), AcpiFormatException(status));
1253 }
1254