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