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