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