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