acpi_display.c revision 1.16.16.4 1 /* $NetBSD: acpi_display.c,v 1.16.16.4 2024/08/23 16:10:11 martin Exp $ */
2
3 /*-
4 * Copyright (c) 2010 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Gregoire Sutre.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * ACPI Display Adapter Driver.
34 *
35 * Appendix B of the ACPI specification presents ACPI extensions for display
36 * adapters. Systems containing a built-in display adapter are required to
37 * implement these extensions (in their ACPI BIOS). This driver uses these
38 * extensions to provide generic support for brightness control and display
39 * switching.
40 *
41 * If brightness control methods are absent or non-functional, ACPI brightness
42 * notifications are relayed to the PMF framework.
43 *
44 * This driver sets the BIOS switch policy (_DOS method) as follows:
45 * - The BIOS should automatically switch the active display output, with no
46 * interaction required on the OS part.
47 * - The BIOS should not automatically control the brightness levels.
48 *
49 * Brightness and BIOS switch policy can be adjusted from userland, via the
50 * sysctl variables acpivga<n>.policy and acpiout<n>.brightness under hw.acpi.
51 */
52
53 /*
54 * The driver uses mutex(9) protection since changes to the hardware/software
55 * state may be initiated both by the BIOS (ACPI notifications) and by the user
56 * (sysctl). The ACPI display adapter's mutex is shared with all ACPI display
57 * output devices attached to it.
58 *
59 * The mutex only prevents undesired interleavings of ACPI notify handlers,
60 * sysctl callbacks, and pmf(9) suspend/resume routines. Race conditions with
61 * autoconf(9) detachment routines could, in theory, still occur.
62 *
63 * The array of connected output devices (sc_odinfo) is, after attachment, only
64 * used in ACPI notify handler callbacks. Since two such callbacks cannot be
65 * running simultaneously, this information does not need protection.
66 */
67
68 #include <sys/cdefs.h>
69 __KERNEL_RCSID(0, "$NetBSD: acpi_display.c,v 1.16.16.4 2024/08/23 16:10:11 martin Exp $");
70
71 #include <sys/param.h>
72 #include <sys/device.h>
73 #include <sys/kmem.h>
74 #include <sys/module.h>
75 #include <sys/mutex.h>
76 #include <sys/sysctl.h>
77 #include <sys/systm.h>
78
79 #include <dev/pci/pcireg.h>
80 #include <dev/pci/pcidevs.h>
81
82 #include <dev/acpi/acpireg.h>
83 #include <dev/acpi/acpivar.h>
84
85 #define _COMPONENT ACPI_DISPLAY_COMPONENT
86 ACPI_MODULE_NAME ("acpi_display")
87
88 /* Notifications specific to display adapter devices (ACPI 4.0a, Sec. B.5). */
89 #define ACPI_NOTIFY_CycleOutputDevice 0x80
90 #define ACPI_NOTIFY_OutputDeviceStatusChange 0x81
91 #define ACPI_NOTIFY_CycleDisplayOutputHotkeyPressed 0x82
92 #define ACPI_NOTIFY_NextDisplayOutputHotkeyPressed 0x83
93 #define ACPI_NOTIFY_PreviousDisplayOutputHotkeyPressed 0x84
94
95 /* Notifications specific to display output devices (ACPI 4.0a, Sec. B.7). */
96 #define ACPI_NOTIFY_CycleBrightness 0x85
97 #define ACPI_NOTIFY_IncreaseBrightness 0x86
98 #define ACPI_NOTIFY_DecreaseBrightness 0x87
99 #define ACPI_NOTIFY_ZeroBrightness 0x88
100 #define ACPI_NOTIFY_DisplayDeviceOff 0x89
101
102 /* Format of the BIOS switch policy set by _DOS (ACPI 4.0a, Sec. B.4.1). */
103 typedef union acpidisp_bios_policy_t {
104 uint8_t raw;
105 struct {
106 uint8_t output:2;
107 uint8_t brightness:1;
108 uint8_t reserved:5;
109 } __packed fmt;
110 } acpidisp_bios_policy_t;
111
112 /* Default BIOS switch policy (ACPI 4.0a, Sec. B.4.1). */
113 static const acpidisp_bios_policy_t acpidisp_default_bios_policy = {
114 .raw = 0x1
115 };
116
117 /* BIOS output switch policies (ACPI 4.0a, Sec. B.4.1). */
118 #define ACPI_DISP_POLICY_OUTPUT_NORMAL 0x0
119 #define ACPI_DISP_POLICY_OUTPUT_AUTO 0x1
120 #define ACPI_DISP_POLICY_OUTPUT_LOCKED 0x2
121 #define ACPI_DISP_POLICY_OUTPUT_HOTKEY 0x3
122
123 /* BIOS brightness switch policies (ACPI 4.0a, Sec. B.4.1). */
124 #define ACPI_DISP_POLICY_BRIGHTNESS_AUTO 0x0
125 #define ACPI_DISP_POLICY_BRIGHTNESS_NORMAL 0x1
126
127 /* Format of output device attributes (ACPI 4.0a, Table B-2). */
128 typedef union acpidisp_od_attrs_t {
129 uint16_t device_id;
130 uint32_t raw;
131 struct {
132 uint8_t index:4;
133 uint8_t port:4;
134 uint8_t type:4;
135 uint8_t vendor_specific:4;
136 uint8_t bios_detect:1;
137 uint8_t non_vga:1;
138 uint8_t head_id:3;
139 uint16_t reserved:10;
140 uint8_t device_id_scheme:1;
141 } __packed fmt;
142 } acpidisp_od_attrs_t;
143
144 /* Common legacy output device IDs (ACPI 2.0c, Table B-3). */
145 #define ACPI_DISP_OUT_LEGACY_DEVID_MONITOR 0x0100
146 #define ACPI_DISP_OUT_LEGACY_DEVID_PANEL 0x0110
147 #define ACPI_DISP_OUT_LEGACY_DEVID_TV 0x0200
148
149 /* Output device display types (ACPI 4.0a, Table B-2). */
150 #define ACPI_DISP_OUT_ATTR_TYPE_OTHER 0x0
151 #define ACPI_DISP_OUT_ATTR_TYPE_VGA 0x1
152 #define ACPI_DISP_OUT_ATTR_TYPE_TV 0x2
153 #define ACPI_DISP_OUT_ATTR_TYPE_EXTDIG 0x3
154 #define ACPI_DISP_OUT_ATTR_TYPE_INTDFP 0x4
155
156 /* Format of output device status (ACPI 4.0a, Table B-4). */
157 typedef union acpidisp_od_status_t {
158 uint32_t raw;
159 struct {
160 uint8_t exists:1;
161 uint8_t activated:1;
162 uint8_t ready:1;
163 uint8_t not_defective:1;
164 uint8_t attached:1;
165 uint32_t reserved:27;
166 } __packed fmt;
167 } acpidisp_od_status_t;
168
169 /* Format of output device state (ACPI 4.0a, Table B-6). */
170 typedef union acpidisp_od_state_t {
171 uint32_t raw;
172 struct {
173 uint8_t active:1;
174 uint32_t reserved:29;
175 uint8_t no_switch:1;
176 uint8_t commit:1;
177 } __packed fmt;
178 } acpidisp_od_state_t;
179
180 /*
181 * acpidisp_outdev:
182 *
183 * Description of an ACPI display output device. This structure groups
184 * together:
185 * - the output device attributes, given in the display adapter's _DOD
186 * method (ACPI 4.0a, Sec. B.4.2).
187 * - the corresponding instance of the acpiout driver (if any).
188 */
189 struct acpidisp_outdev {
190 acpidisp_od_attrs_t od_attrs; /* Attributes */
191 device_t od_device; /* Matching base device */
192 };
193
194 /*
195 * acpidisp_odinfo:
196 *
197 * Information on connected output devices (ACPI 4.0a, Sec. B.4.2). This
198 * structure enumerates all devices (_DOD package) connected to a display
199 * adapter. Currently, this information is only used for display output
200 * switching via hotkey.
201 *
202 * Invariants (after initialization):
203 *
204 * (oi_dev != NULL) && (oi_dev_count > 0)
205 */
206 struct acpidisp_odinfo {
207 struct acpidisp_outdev *oi_dev; /* Array of output devices */
208 uint32_t oi_dev_count; /* Number of output devices */
209 };
210
211 /*
212 * acpidisp_vga_softc:
213 *
214 * Software state of an ACPI display adapter.
215 *
216 * Invariants (after attachment):
217 *
218 * ((sc_caps & ACPI_DISP_VGA_CAP__DOD) == 0) => (sc_odinfo == NULL)
219 */
220 struct acpidisp_vga_softc {
221 device_t sc_dev; /* Base device info */
222 struct acpi_devnode *sc_node; /* ACPI device node */
223 struct sysctllog *sc_log; /* Sysctl log */
224 kmutex_t sc_mtx; /* Mutex (shared w/ outputs) */
225 uint16_t sc_caps; /* Capabilities (methods) */
226 acpidisp_bios_policy_t sc_policy; /* BIOS switch policy (_DOS) */
227 struct acpidisp_odinfo *sc_odinfo; /* Connected output devices */
228 };
229
230 /*
231 * ACPI display adapter capabilities (methods).
232 */
233 #define ACPI_DISP_VGA_CAP__DOS __BIT(0)
234 #define ACPI_DISP_VGA_CAP__DOD __BIT(1)
235 #define ACPI_DISP_VGA_CAP__ROM __BIT(2)
236 #define ACPI_DISP_VGA_CAP__GPD __BIT(3)
237 #define ACPI_DISP_VGA_CAP__SPD __BIT(4)
238 #define ACPI_DISP_VGA_CAP__VPO __BIT(5)
239
240 /*
241 * acpidisp_acpivga_attach_args:
242 *
243 * Attachment structure for the acpivga interface. Used to attach display
244 * output devices under a display adapter.
245 */
246 struct acpidisp_acpivga_attach_args {
247 struct acpi_devnode *aa_node; /* ACPI device node */
248 kmutex_t *aa_mtx; /* Shared mutex */
249 };
250
251 /*
252 * acpidisp_brctl:
253 *
254 * Brightness control (ACPI 4.0a, Sec. B.6.2 to B.6.4). This structure
255 * contains the supported brightness levels (_BCL package) and the current
256 * level. Following Windows 7 brightness control, we ignore the fullpower
257 * and battery levels (as it simplifies the code).
258 *
259 * The array bc_level is sorted in strictly ascending order.
260 *
261 * Invariants (after initialization):
262 *
263 * (bc_level != NULL) && (bc_level_count > 0)
264 */
265 struct acpidisp_brctl {
266 uint8_t *bc_level; /* Array of levels */
267 uint16_t bc_level_count; /* Number of levels */
268 uint8_t bc_current; /* Current level */
269
270 /*
271 * Quirk if firmware returns wrong values for _BQC
272 * (acpidisp_get_brightness)
273 */
274 bool bc_bqc_broken;
275 };
276
277 /*
278 * Minimum brightness increment/decrement in response to increase/decrease
279 * brightness hotkey notifications. Must be strictly positive.
280 */
281 #define ACPI_DISP_BRCTL_STEP 5
282
283 /*
284 * acpidisp_out_softc:
285 *
286 * Software state of an ACPI display output device.
287 *
288 * Invariants (after attachment):
289 *
290 * ((sc_caps & ACPI_DISP_OUT_CAP__BCL) == 0) => (sc_brctl == NULL)
291 * ((sc_caps & ACPI_DISP_OUT_CAP__BCM) == 0) => (sc_brctl == NULL)
292 */
293 struct acpidisp_out_softc {
294 device_t sc_dev; /* Base device info */
295 struct acpi_devnode *sc_node; /* ACPI device node */
296 struct sysctllog *sc_log; /* Sysctl log */
297 kmutex_t *sc_mtx; /* Mutex (shared w/ adapter) */
298 uint16_t sc_caps; /* Capabilities (methods) */
299 struct acpidisp_brctl *sc_brctl; /* Brightness control */
300 };
301
302 /*
303 * ACPI display output device capabilities (methods).
304 */
305 #define ACPI_DISP_OUT_CAP__BCL __BIT(0)
306 #define ACPI_DISP_OUT_CAP__BCM __BIT(1)
307 #define ACPI_DISP_OUT_CAP__BQC __BIT(2)
308 #define ACPI_DISP_OUT_CAP__DDC __BIT(3)
309 #define ACPI_DISP_OUT_CAP__DCS __BIT(4)
310 #define ACPI_DISP_OUT_CAP__DGS __BIT(5)
311 #define ACPI_DISP_OUT_CAP__DSS __BIT(6)
312
313 static int acpidisp_vga_match(device_t, cfdata_t, void *);
314 static void acpidisp_vga_attach(device_t, device_t, void *);
315 static int acpidisp_vga_detach(device_t, int);
316 static void acpidisp_vga_childdetached(device_t, device_t);
317
318 static void acpidisp_vga_scan_outdevs(struct acpidisp_vga_softc *);
319 static int acpidisp_acpivga_print(void *, const char *);
320
321 static int acpidisp_out_match(device_t, cfdata_t, void *);
322 static void acpidisp_out_attach(device_t, device_t, void *);
323 static int acpidisp_out_detach(device_t, int);
324
325 CFATTACH_DECL2_NEW(acpivga, sizeof(struct acpidisp_vga_softc),
326 acpidisp_vga_match, acpidisp_vga_attach, acpidisp_vga_detach, NULL,
327 NULL, acpidisp_vga_childdetached);
328
329 CFATTACH_DECL_NEW(acpiout, sizeof(struct acpidisp_out_softc),
330 acpidisp_out_match, acpidisp_out_attach, acpidisp_out_detach, NULL);
331
332 static bool acpidisp_vga_resume(device_t, const pmf_qual_t *);
333 static bool acpidisp_out_suspend(device_t, const pmf_qual_t *);
334 static bool acpidisp_out_resume(device_t, const pmf_qual_t *);
335
336 static uint16_t acpidisp_vga_capabilities(const struct acpi_devnode *);
337 static uint16_t acpidisp_out_capabilities(const struct acpi_devnode *);
338 static void acpidisp_vga_print_capabilities(device_t, uint16_t);
339 static void acpidisp_out_print_capabilities(device_t, uint16_t);
340
341 static void acpidisp_vga_notify_handler(ACPI_HANDLE, uint32_t, void *);
342 static void acpidisp_out_notify_handler(ACPI_HANDLE, uint32_t, void *);
343
344 static void acpidisp_vga_cycle_output_device_callback(void *);
345 static void acpidisp_vga_output_device_change_callback(void *);
346 static void acpidisp_out_increase_brightness_callback(void *);
347 static void acpidisp_out_decrease_brightness_callback(void *);
348 static void acpidisp_out_cycle_brightness_callback(void *);
349 static void acpidisp_out_zero_brightness_callback(void *);
350
351 static void acpidisp_vga_sysctl_setup(struct acpidisp_vga_softc *);
352 static void acpidisp_out_sysctl_setup(struct acpidisp_out_softc *);
353 #ifdef ACPI_DEBUG
354 static int acpidisp_vga_sysctl_policy(SYSCTLFN_PROTO);
355 #endif
356 static int acpidisp_vga_sysctl_policy_output(SYSCTLFN_PROTO);
357 #ifdef ACPI_DISP_SWITCH_SYSCTLS
358 static int acpidisp_out_sysctl_status(SYSCTLFN_PROTO);
359 static int acpidisp_out_sysctl_state(SYSCTLFN_PROTO);
360 #endif
361 static int acpidisp_out_sysctl_brightness(SYSCTLFN_PROTO);
362
363 static struct acpidisp_odinfo *
364 acpidisp_init_odinfo(const struct acpidisp_vga_softc *);
365 static void acpidisp_vga_bind_outdevs(struct acpidisp_vga_softc *);
366 static struct acpidisp_brctl *
367 acpidisp_init_brctl(const struct acpidisp_out_softc *);
368
369 static int acpidisp_set_policy(const struct acpidisp_vga_softc *,
370 uint8_t);
371 static int acpidisp_get_status(const struct acpidisp_out_softc *,
372 uint32_t *);
373 static int acpidisp_get_state(const struct acpidisp_out_softc *,
374 uint32_t *);
375 static int acpidisp_set_state(const struct acpidisp_out_softc *,
376 uint32_t);
377 static int acpidisp_get_brightness(const struct acpidisp_out_softc *,
378 uint8_t *);
379 static int acpidisp_set_brightness(const struct acpidisp_out_softc *,
380 uint8_t);
381 static int acpidisp_quirk_get_brightness(const struct acpidisp_out_softc *);
382
383 static void acpidisp_print_odinfo(device_t, const struct acpidisp_odinfo *);
384 static void acpidisp_print_brctl(device_t, const struct acpidisp_brctl *);
385 static void acpidisp_print_od_attrs(acpidisp_od_attrs_t);
386
387 static bool acpidisp_has_method(ACPI_HANDLE, const char *,
388 ACPI_OBJECT_TYPE);
389 static ACPI_STATUS
390 acpidisp_eval_package(ACPI_HANDLE, const char *, ACPI_OBJECT **,
391 unsigned int);
392 static void acpidisp_array_search(const uint8_t *, uint16_t, int, uint8_t *,
393 uint8_t *);
394
395 /*
396 * Autoconfiguration for the acpivga driver.
397 */
398
399 static int
400 acpidisp_vga_match(device_t parent, cfdata_t match, void *aux)
401 {
402 struct acpi_attach_args *aa = aux;
403 struct acpi_devnode *ad = aa->aa_node;
404 struct acpi_pci_info *ap;
405 pcireg_t id, class;
406 pcitag_t tag;
407
408 if (ad->ad_type != ACPI_TYPE_DEVICE)
409 return 0;
410
411 ap = ad->ad_pciinfo;
412
413 if (ap == NULL)
414 return 0;
415
416 if ((ap->ap_flags & ACPI_PCI_INFO_DEVICE) == 0)
417 return 0;
418
419 if (ap->ap_function == 0xffff)
420 return 0;
421
422 KASSERT(ap->ap_bus < 256);
423 KASSERT(ap->ap_device < 32);
424 KASSERT(ap->ap_function < 8);
425
426 /*
427 * Check that the PCI device is present, verify
428 * the class of the PCI device, and finally see
429 * if the ACPI device is capable of something.
430 */
431 tag = pci_make_tag(aa->aa_pc, ap->ap_bus,
432 ap->ap_device, ap->ap_function);
433
434 id = pci_conf_read(aa->aa_pc, tag, PCI_ID_REG);
435
436 if (PCI_VENDOR(id) == PCI_VENDOR_INVALID || PCI_VENDOR(id) == 0)
437 return 0;
438
439 class = pci_conf_read(aa->aa_pc, tag, PCI_CLASS_REG);
440
441 if (PCI_CLASS(class) != PCI_CLASS_DISPLAY)
442 return 0;
443
444 if (acpidisp_vga_capabilities(ad) == 0)
445 return 0;
446
447 return 1;
448 }
449
450 static void
451 acpidisp_vga_attach(device_t parent, device_t self, void *aux)
452 {
453 struct acpidisp_vga_softc *asc = device_private(self);
454 struct acpi_attach_args *aa = aux;
455 struct acpi_devnode *ad = aa->aa_node;
456
457 aprint_naive(": ACPI Display Adapter\n");
458 aprint_normal(": ACPI Display Adapter\n");
459
460 asc->sc_node = ad;
461 asc->sc_dev = self;
462 asc->sc_log = NULL;
463
464 mutex_init(&asc->sc_mtx, MUTEX_DEFAULT, IPL_NONE);
465
466 asc->sc_caps = acpidisp_vga_capabilities(ad);
467 asc->sc_policy = acpidisp_default_bios_policy;
468 asc->sc_odinfo = NULL;
469
470 acpidisp_vga_print_capabilities(self, asc->sc_caps);
471
472 /*
473 * Enumerate connected output devices, attach
474 * output display devices, and bind the attached
475 * output devices to the enumerated ones.
476 */
477 asc->sc_odinfo = acpidisp_init_odinfo(asc);
478
479 acpidisp_vga_scan_outdevs(asc);
480
481 if (asc->sc_odinfo != NULL) {
482 acpidisp_vga_bind_outdevs(asc);
483 acpidisp_print_odinfo(self, asc->sc_odinfo);
484 }
485
486 /*
487 * Set BIOS automatic switch policy.
488 *
489 * Many laptops do not support output device switching with
490 * the methods specified in the ACPI extensions for display
491 * adapters. Therefore, we leave the BIOS output switch policy
492 * on "auto" instead of setting it to "normal".
493 */
494 asc->sc_policy.fmt.output = ACPI_DISP_POLICY_OUTPUT_AUTO;
495 asc->sc_policy.fmt.brightness = ACPI_DISP_POLICY_BRIGHTNESS_NORMAL;
496
497 if (acpidisp_set_policy(asc, asc->sc_policy.raw))
498 asc->sc_policy = acpidisp_default_bios_policy;
499
500 acpidisp_vga_sysctl_setup(asc);
501
502 (void)pmf_device_register(self, NULL, acpidisp_vga_resume);
503 (void)acpi_register_notify(asc->sc_node, acpidisp_vga_notify_handler);
504 }
505
506 static int
507 acpidisp_vga_detach(device_t self, int flags)
508 {
509 struct acpidisp_vga_softc *asc = device_private(self);
510 struct acpidisp_odinfo *oi = asc->sc_odinfo;
511 int rc;
512
513 pmf_device_deregister(self);
514
515 if (asc->sc_log != NULL)
516 sysctl_teardown(&asc->sc_log);
517
518 asc->sc_policy = acpidisp_default_bios_policy;
519 acpidisp_set_policy(asc, asc->sc_policy.raw);
520
521 acpi_deregister_notify(asc->sc_node);
522
523 if ((rc = config_detach_children(self, flags)) != 0)
524 return rc;
525
526 if (oi != NULL) {
527 kmem_free(oi->oi_dev,
528 oi->oi_dev_count * sizeof(*oi->oi_dev));
529 kmem_free(oi, sizeof(*oi));
530 }
531
532 mutex_destroy(&asc->sc_mtx);
533
534 return 0;
535 }
536
537 void
538 acpidisp_vga_childdetached(device_t self, device_t child)
539 {
540 struct acpidisp_vga_softc *asc = device_private(self);
541 struct acpidisp_odinfo *oi = asc->sc_odinfo;
542 struct acpidisp_outdev *od;
543 struct acpi_devnode *ad;
544 uint32_t i;
545
546 SIMPLEQ_FOREACH(ad, &asc->sc_node->ad_child_head, ad_child_list) {
547
548 if (ad->ad_device == child)
549 ad->ad_device = NULL;
550 }
551
552 if (oi == NULL)
553 return;
554
555 for (i = 0, od = oi->oi_dev; i < oi->oi_dev_count; i++, od++) {
556 if (od->od_device == child)
557 od->od_device = NULL;
558 }
559 }
560
561 /*
562 * Attachment of acpiout under acpivga.
563 */
564
565 static void
566 acpidisp_vga_scan_outdevs(struct acpidisp_vga_softc *asc)
567 {
568 struct acpidisp_acpivga_attach_args aa;
569 struct acpi_devnode *ad;
570
571 /*
572 * Display output devices are ACPI children of the display adapter.
573 */
574 SIMPLEQ_FOREACH(ad, &asc->sc_node->ad_child_head, ad_child_list) {
575
576 if (ad->ad_device != NULL) /* This should not happen. */
577 continue;
578
579 aa.aa_node = ad;
580 aa.aa_mtx = &asc->sc_mtx;
581
582 ad->ad_device = config_found_ia(asc->sc_dev,
583 "acpivga", &aa, acpidisp_acpivga_print);
584 }
585 }
586
587 static int
588 acpidisp_acpivga_print(void *aux, const char *pnp)
589 {
590 struct acpidisp_acpivga_attach_args *aa = aux;
591 struct acpi_devnode *ad = aa->aa_node;
592
593 if (pnp) {
594 aprint_normal("%s at %s", ad->ad_name, pnp);
595 } else {
596 aprint_normal(" (%s", ad->ad_name);
597 if (ad->ad_devinfo->Valid & ACPI_VALID_ADR)
598 aprint_normal(", 0x%04"PRIx64, ad->ad_devinfo->Address);
599 aprint_normal(")");
600 }
601
602 return UNCONF;
603 }
604
605 /*
606 * Autoconfiguration for the acpiout driver.
607 */
608
609 static int
610 acpidisp_out_match(device_t parent, cfdata_t match, void *aux)
611 {
612 struct acpidisp_acpivga_attach_args *aa = aux;
613 struct acpi_devnode *ad = aa->aa_node;
614
615 if (ad->ad_type != ACPI_TYPE_DEVICE)
616 return 0;
617
618 /*
619 * The method _ADR is required for display output
620 * devices (ACPI 4.0a, Sec. B.6.1).
621 */
622 if (!(acpidisp_has_method(ad->ad_handle, "_ADR", ACPI_TYPE_INTEGER)))
623 return 0;
624
625 return 1;
626 }
627
628 static void
629 acpidisp_out_attach(device_t parent, device_t self, void *aux)
630 {
631 struct acpidisp_out_softc *osc = device_private(self);
632 struct acpidisp_acpivga_attach_args *aa = aux;
633 struct acpi_devnode *ad = aa->aa_node;
634 struct acpidisp_brctl *bc;
635
636 aprint_naive("\n");
637 aprint_normal(": ACPI Display Output Device\n");
638
639 osc->sc_dev = self;
640 osc->sc_node = ad;
641 osc->sc_log = NULL;
642 osc->sc_mtx = aa->aa_mtx;
643 osc->sc_caps = acpidisp_out_capabilities(ad);
644 osc->sc_brctl = NULL;
645
646 acpidisp_out_print_capabilities(self, osc->sc_caps);
647
648 osc->sc_brctl = acpidisp_init_brctl(osc);
649 bc = osc->sc_brctl;
650 if (bc != NULL) {
651 bc->bc_current = bc->bc_level[bc->bc_level_count - 1];
652
653 /*
654 * Synchronize ACPI and driver brightness levels, and
655 * check that brightness control is working.
656 */
657 if (acpidisp_get_brightness(osc, &bc->bc_current) &&
658 acpidisp_set_brightness(osc, bc->bc_current)) {
659 kmem_free(bc->bc_level,
660 bc->bc_level_count * sizeof(*bc->bc_level));
661 kmem_free(bc, sizeof(*bc));
662 osc->sc_brctl = NULL;
663 } else {
664 if (acpidisp_quirk_get_brightness(osc)) {
665 aprint_error_dev(self,
666 "failed to test _BQC quirk\n");
667 }
668 acpidisp_print_brctl(self, osc->sc_brctl);
669 }
670 }
671
672 /* Install ACPI notify handler. */
673 (void)acpi_register_notify(osc->sc_node, acpidisp_out_notify_handler);
674
675 /* Setup sysctl. */
676 acpidisp_out_sysctl_setup(osc);
677
678 /* Power management. */
679 if (!pmf_device_register(self, acpidisp_out_suspend,
680 acpidisp_out_resume))
681 aprint_error_dev(self, "couldn't establish power handler\n");
682 }
683
684 static int
685 acpidisp_out_detach(device_t self, int flags)
686 {
687 struct acpidisp_out_softc *osc = device_private(self);
688 struct acpidisp_brctl *bc = osc->sc_brctl;
689
690 pmf_device_deregister(self);
691
692 if (osc->sc_log != NULL)
693 sysctl_teardown(&osc->sc_log);
694
695 acpi_deregister_notify(osc->sc_node);
696
697 if (bc != NULL) {
698 kmem_free(bc->bc_level,
699 bc->bc_level_count * sizeof(*bc->bc_level));
700 kmem_free(bc, sizeof(*bc));
701 }
702
703 return 0;
704 }
705
706 /*
707 * Power management.
708 */
709
710 static bool
711 acpidisp_vga_resume(device_t self, const pmf_qual_t *qual)
712 {
713 struct acpidisp_vga_softc *asc = device_private(self);
714
715 mutex_enter(&asc->sc_mtx);
716 (void)acpidisp_set_policy(asc, asc->sc_policy.raw);
717 mutex_exit(&asc->sc_mtx);
718
719 return true;
720 }
721
722 static bool
723 acpidisp_out_suspend(device_t self, const pmf_qual_t *qual)
724 {
725 struct acpidisp_out_softc *osc = device_private(self);
726
727 mutex_enter(osc->sc_mtx);
728 if (osc->sc_brctl != NULL)
729 (void)acpidisp_get_brightness(osc, &osc->sc_brctl->bc_current);
730 mutex_exit(osc->sc_mtx);
731
732 return true;
733 }
734
735 static bool
736 acpidisp_out_resume(device_t self, const pmf_qual_t *qual)
737 {
738 struct acpidisp_out_softc *osc = device_private(self);
739
740 mutex_enter(osc->sc_mtx);
741 if (osc->sc_brctl != NULL)
742 (void)acpidisp_set_brightness(osc, osc->sc_brctl->bc_current);
743 mutex_exit(osc->sc_mtx);
744
745 return true;
746 }
747
748 /*
749 * Capabilities (available methods).
750 */
751
752 static uint16_t
753 acpidisp_vga_capabilities(const struct acpi_devnode *ad)
754 {
755 uint16_t cap;
756
757 cap = 0;
758
759 if (acpidisp_has_method(ad->ad_handle, "_DOS", ACPI_TYPE_METHOD))
760 cap |= ACPI_DISP_VGA_CAP__DOS;
761
762 if (acpidisp_has_method(ad->ad_handle, "_DOD", ACPI_TYPE_PACKAGE))
763 cap |= ACPI_DISP_VGA_CAP__DOD;
764
765 if (acpidisp_has_method(ad->ad_handle, "_ROM", ACPI_TYPE_BUFFER))
766 cap |= ACPI_DISP_VGA_CAP__ROM;
767
768 if (acpidisp_has_method(ad->ad_handle, "_GPD", ACPI_TYPE_INTEGER))
769 cap |= ACPI_DISP_VGA_CAP__GPD;
770
771 if (acpidisp_has_method(ad->ad_handle, "_SPD", ACPI_TYPE_METHOD))
772 cap |= ACPI_DISP_VGA_CAP__SPD;
773
774 if (acpidisp_has_method(ad->ad_handle, "_VPO", ACPI_TYPE_INTEGER))
775 cap |= ACPI_DISP_VGA_CAP__VPO;
776
777 return cap;
778 }
779
780 static void
781 acpidisp_vga_print_capabilities(device_t self, uint16_t cap)
782 {
783 aprint_debug_dev(self, "capabilities:%s%s%s%s%s%s\n",
784 (cap & ACPI_DISP_VGA_CAP__DOS) ? " _DOS" : "",
785 (cap & ACPI_DISP_VGA_CAP__DOD) ? " _DOD" : "",
786 (cap & ACPI_DISP_VGA_CAP__ROM) ? " _ROM" : "",
787 (cap & ACPI_DISP_VGA_CAP__GPD) ? " _GPD" : "",
788 (cap & ACPI_DISP_VGA_CAP__SPD) ? " _SPD" : "",
789 (cap & ACPI_DISP_VGA_CAP__VPO) ? " _VPO" : "");
790 }
791
792 static uint16_t
793 acpidisp_out_capabilities(const struct acpi_devnode *ad)
794 {
795 uint16_t cap;
796
797 cap = 0;
798
799 /* List of Brigthness levels */
800 if (acpidisp_has_method(ad->ad_handle, "_BCL", ACPI_TYPE_PACKAGE))
801 cap |= ACPI_DISP_OUT_CAP__BCL;
802
803 /* Set brightness level */
804 if (acpidisp_has_method(ad->ad_handle, "_BCM", ACPI_TYPE_METHOD))
805 cap |= ACPI_DISP_OUT_CAP__BCM;
806
807 /* Get brightless level */
808 if (acpidisp_has_method(ad->ad_handle, "_BQC", ACPI_TYPE_INTEGER))
809 cap |= ACPI_DISP_OUT_CAP__BQC;
810
811 /* Return EDID */
812 if (acpidisp_has_method(ad->ad_handle, "_DDC", ACPI_TYPE_METHOD))
813 cap |= ACPI_DISP_OUT_CAP__DDC;
814
815 /* Get Status */
816 if (acpidisp_has_method(ad->ad_handle, "_DCS", ACPI_TYPE_INTEGER))
817 cap |= ACPI_DISP_OUT_CAP__DCS;
818
819 /* Get Graphics State */
820 if (acpidisp_has_method(ad->ad_handle, "_DGS", ACPI_TYPE_INTEGER))
821 cap |= ACPI_DISP_OUT_CAP__DGS;
822
823 /* Set Graphics State */
824 if (acpidisp_has_method(ad->ad_handle, "_DSS", ACPI_TYPE_METHOD))
825 cap |= ACPI_DISP_OUT_CAP__DSS;
826
827 return cap;
828 }
829
830 static void
831 acpidisp_out_print_capabilities(device_t self, uint16_t cap)
832 {
833 aprint_debug_dev(self, "capabilities:%s%s%s%s%s%s%s\n",
834 (cap & ACPI_DISP_OUT_CAP__BCL) ? " _BCL" : "",
835 (cap & ACPI_DISP_OUT_CAP__BCM) ? " _BCM" : "",
836 (cap & ACPI_DISP_OUT_CAP__BQC) ? " _BQC" : "",
837 (cap & ACPI_DISP_OUT_CAP__DDC) ? " _DDC" : "",
838 (cap & ACPI_DISP_OUT_CAP__DCS) ? " _DCS" : "",
839 (cap & ACPI_DISP_OUT_CAP__DGS) ? " _DGS" : "",
840 (cap & ACPI_DISP_OUT_CAP__DSS) ? " _DSS" : "");
841 }
842
843 /*
844 * ACPI notify handlers.
845 */
846
847 static void
848 acpidisp_vga_notify_handler(ACPI_HANDLE handle, uint32_t notify,
849 void *context)
850 {
851 struct acpidisp_vga_softc *asc = device_private(context);
852 ACPI_OSD_EXEC_CALLBACK callback;
853
854 callback = NULL;
855
856 switch (notify) {
857 case ACPI_NOTIFY_CycleOutputDevice:
858 callback = acpidisp_vga_cycle_output_device_callback;
859 break;
860 case ACPI_NOTIFY_OutputDeviceStatusChange:
861 callback = acpidisp_vga_output_device_change_callback;
862 break;
863 case ACPI_NOTIFY_CycleDisplayOutputHotkeyPressed:
864 case ACPI_NOTIFY_NextDisplayOutputHotkeyPressed:
865 case ACPI_NOTIFY_PreviousDisplayOutputHotkeyPressed:
866 aprint_debug_dev(asc->sc_dev,
867 "unhandled notify: 0x%"PRIx32"\n", notify);
868 return;
869 default:
870 aprint_error_dev(asc->sc_dev,
871 "unknown notify: 0x%"PRIx32"\n", notify);
872 return;
873 }
874
875 KASSERT(callback != NULL);
876 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, callback, asc);
877 }
878
879 static void
880 acpidisp_out_notify_handler(ACPI_HANDLE handle, uint32_t notify,
881 void *context)
882 {
883 struct acpidisp_out_softc *osc = device_private(context);
884 ACPI_OSD_EXEC_CALLBACK callback;
885
886 callback = NULL;
887
888 switch (notify) {
889 case ACPI_NOTIFY_IncreaseBrightness:
890 callback = acpidisp_out_increase_brightness_callback;
891 break;
892 case ACPI_NOTIFY_DecreaseBrightness:
893 callback = acpidisp_out_decrease_brightness_callback;
894 break;
895 case ACPI_NOTIFY_CycleBrightness:
896 callback = acpidisp_out_cycle_brightness_callback;
897 break;
898 case ACPI_NOTIFY_ZeroBrightness:
899 callback = acpidisp_out_zero_brightness_callback;
900 break;
901 case ACPI_NOTIFY_DisplayDeviceOff:
902 aprint_debug_dev(osc->sc_dev,
903 "unhandled notify: 0x%"PRIx32"\n", notify);
904 return;
905 default:
906 aprint_error_dev(osc->sc_dev,
907 "unknown notify: 0x%"PRIx32"\n", notify);
908 return;
909 }
910
911 KASSERT(callback != NULL);
912 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, callback, osc);
913 }
914
915 /*
916 * ACPI notify callbacks.
917 *
918 * Exclusive access to the sc_odinfo field of struct acpidisp_vga_softc is
919 * guaranteed since:
920 *
921 * (a) this field is only used in ACPI display notify callbacks,
922 * (b) ACPI display notify callbacks are scheduled with AcpiOsExecute,
923 * (c) callbacks scheduled with AcpiOsExecute are executed sequentially.
924 */
925
926 static void
927 acpidisp_vga_cycle_output_device_callback(void *arg)
928 {
929 struct acpidisp_vga_softc *asc = arg;
930 struct acpidisp_odinfo *oi = asc->sc_odinfo;
931 struct acpidisp_outdev *od;
932 struct acpidisp_out_softc *osc, *last_osc;
933 acpidisp_od_state_t state, last_state;
934 acpidisp_od_status_t status;
935 acpidisp_bios_policy_t lock_policy;
936 uint32_t i;
937
938 if (oi == NULL)
939 return;
940
941 /* Mutual exclusion with callbacks of connected output devices. */
942 mutex_enter(&asc->sc_mtx);
943
944 /* Lock the _DGS values. */
945 lock_policy = asc->sc_policy;
946 lock_policy.fmt.output = ACPI_DISP_POLICY_OUTPUT_LOCKED;
947 (void)acpidisp_set_policy(asc, lock_policy.raw);
948
949 last_osc = NULL;
950 for (i = 0, od = oi->oi_dev; i < oi->oi_dev_count; i++, od++) {
951 if (od->od_device == NULL)
952 continue;
953 osc = device_private(od->od_device);
954
955 if (!(osc->sc_caps & ACPI_DISP_OUT_CAP__DSS))
956 continue;
957 if (acpidisp_get_state(osc, &state.raw))
958 continue;
959
960 if (acpidisp_get_status(osc, &status.raw)) {
961 state.fmt.no_switch = 0;
962 } else {
963 state.fmt.active &= status.fmt.ready;
964
965 if (state.fmt.active == status.fmt.activated)
966 state.fmt.no_switch = 1;
967 else
968 state.fmt.no_switch = 0;
969 }
970
971 state.fmt.commit = 0;
972
973 if (last_osc != NULL)
974 (void)acpidisp_set_state(last_osc, last_state.raw);
975
976 last_osc = osc;
977 last_state = state;
978 }
979
980 if (last_osc != NULL) {
981 last_state.fmt.commit = 1;
982 (void)acpidisp_set_state(last_osc, last_state.raw);
983 }
984
985 /* Restore the original BIOS policy. */
986 (void)acpidisp_set_policy(asc, asc->sc_policy.raw);
987
988 mutex_exit(&asc->sc_mtx);
989 }
990
991 static void
992 acpidisp_vga_output_device_change_callback(void *arg)
993 {
994 struct acpidisp_vga_softc *asc = arg;
995 struct acpidisp_odinfo *oi = asc->sc_odinfo;
996 bool switch_outputs;
997
998 if (oi != NULL) {
999 kmem_free(oi->oi_dev,
1000 oi->oi_dev_count * sizeof(*oi->oi_dev));
1001 kmem_free(oi, sizeof(*oi));
1002 }
1003
1004 asc->sc_odinfo = acpidisp_init_odinfo(asc);
1005 if (asc->sc_odinfo != NULL) {
1006 acpidisp_vga_bind_outdevs(asc);
1007 acpidisp_print_odinfo(asc->sc_dev, asc->sc_odinfo);
1008 }
1009
1010 /* Perform display output switch if needed. */
1011 mutex_enter(&asc->sc_mtx);
1012 switch_outputs =
1013 (asc->sc_policy.fmt.output == ACPI_DISP_POLICY_OUTPUT_NORMAL);
1014 mutex_exit(&asc->sc_mtx);
1015 if (switch_outputs)
1016 acpidisp_vga_cycle_output_device_callback(arg);
1017 }
1018
1019 static void
1020 acpidisp_out_increase_brightness_callback(void *arg)
1021 {
1022 struct acpidisp_out_softc *osc = arg;
1023 struct acpidisp_brctl *bc = osc->sc_brctl;
1024 uint8_t max, lo, up;
1025 int cur;
1026
1027 if (bc == NULL) {
1028 /* Fallback to pmf(9). */
1029 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP);
1030 return;
1031 }
1032
1033 mutex_enter(osc->sc_mtx);
1034 max = bc->bc_level[bc->bc_level_count - 1];
1035 if (acpidisp_get_brightness(osc, &bc->bc_current))
1036 goto out;
1037 for (cur = bc->bc_current; (cur += ACPI_DISP_BRCTL_STEP) <= max;) {
1038 acpidisp_array_search(bc->bc_level, bc->bc_level_count, cur,
1039 &lo, &up);
1040 bc->bc_current = up;
1041 if (acpidisp_set_brightness(osc, bc->bc_current))
1042 goto out;
1043 if (acpidisp_get_brightness(osc, &bc->bc_current))
1044 goto out;
1045 if (bc->bc_current >= cur)
1046 break;
1047 }
1048 out: mutex_exit(osc->sc_mtx);
1049 }
1050
1051 static void
1052 acpidisp_out_decrease_brightness_callback(void *arg)
1053 {
1054 struct acpidisp_out_softc *osc = arg;
1055 struct acpidisp_brctl *bc = osc->sc_brctl;
1056 uint8_t min, lo, up;
1057 int cur;
1058
1059 if (bc == NULL) {
1060 /* Fallback to pmf(9). */
1061 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN);
1062 return;
1063 }
1064
1065 mutex_enter(osc->sc_mtx);
1066 min = bc->bc_level[0];
1067 if (acpidisp_get_brightness(osc, &bc->bc_current))
1068 goto out;
1069 for (cur = bc->bc_current; (cur -= ACPI_DISP_BRCTL_STEP) >= min;) {
1070 acpidisp_array_search(bc->bc_level, bc->bc_level_count, cur,
1071 &lo, &up);
1072 bc->bc_current = lo;
1073 if (acpidisp_set_brightness(osc, bc->bc_current))
1074 goto out;
1075 if (acpidisp_get_brightness(osc, &bc->bc_current))
1076 goto out;
1077 if (bc->bc_current <= cur)
1078 break;
1079 }
1080 out: mutex_exit(osc->sc_mtx);
1081 }
1082
1083 static void
1084 acpidisp_out_cycle_brightness_callback(void *arg)
1085 {
1086 struct acpidisp_out_softc *osc = arg;
1087 struct acpidisp_brctl *bc = osc->sc_brctl;
1088 uint8_t lo, up;
1089
1090 if (bc == NULL) {
1091 /* No fallback. */
1092 return;
1093 }
1094
1095 mutex_enter(osc->sc_mtx);
1096
1097 (void)acpidisp_get_brightness(osc, &bc->bc_current);
1098
1099 if (bc->bc_current >= bc->bc_level[bc->bc_level_count - 1]) {
1100 bc->bc_current = bc->bc_level[0];
1101 } else {
1102 acpidisp_array_search(bc->bc_level, bc->bc_level_count,
1103 bc->bc_current + 1, &lo, &up);
1104 bc->bc_current = up;
1105 }
1106
1107 (void)acpidisp_set_brightness(osc, bc->bc_current);
1108
1109 mutex_exit(osc->sc_mtx);
1110 }
1111
1112 static void
1113 acpidisp_out_zero_brightness_callback(void *arg)
1114 {
1115 struct acpidisp_out_softc *osc = arg;
1116 struct acpidisp_brctl *bc = osc->sc_brctl;
1117
1118 if (bc == NULL) {
1119 /* Fallback to pmf(9). */
1120 /* XXX Is this the intended meaning of PMFE_DISPLAY_REDUCED? */
1121 pmf_event_inject(NULL, PMFE_DISPLAY_REDUCED);
1122 return;
1123 }
1124
1125 mutex_enter(osc->sc_mtx);
1126
1127 bc->bc_current = bc->bc_level[0];
1128 (void)acpidisp_set_brightness(osc, bc->bc_current);
1129
1130 mutex_exit(osc->sc_mtx);
1131 }
1132
1133 /*
1134 * Sysctl setup.
1135 */
1136
1137 static void
1138 acpidisp_vga_sysctl_setup(struct acpidisp_vga_softc *asc)
1139 {
1140 const struct sysctlnode *rnode;
1141
1142 if (asc->sc_caps & ACPI_DISP_VGA_CAP__DOS) {
1143 if ((sysctl_createv(&asc->sc_log, 0, NULL, &rnode,
1144 0, CTLTYPE_NODE, "acpi", NULL,
1145 NULL, 0, NULL, 0,
1146 CTL_HW, CTL_CREATE, CTL_EOL)) != 0)
1147 goto fail;
1148
1149 if ((sysctl_createv(&asc->sc_log, 0, &rnode, &rnode,
1150 0, CTLTYPE_NODE, device_xname(asc->sc_dev),
1151 SYSCTL_DESCR("ACPI display adapter controls"),
1152 NULL, 0, NULL, 0,
1153 CTL_CREATE, CTL_EOL)) != 0)
1154 goto fail;
1155
1156 #ifdef ACPI_DEBUG
1157 (void)sysctl_createv(&asc->sc_log, 0, &rnode, NULL,
1158 CTLFLAG_READWRITE | CTLFLAG_HEX, CTLTYPE_INT, "bios_policy",
1159 SYSCTL_DESCR("Current BIOS switch policies (debug)"),
1160 acpidisp_vga_sysctl_policy, 0, (void *)asc, 0,
1161 CTL_CREATE, CTL_EOL);
1162 #endif
1163
1164 (void)sysctl_createv(&asc->sc_log, 0, &rnode, NULL,
1165 CTLFLAG_READWRITE, CTLTYPE_BOOL, "bios_switch",
1166 SYSCTL_DESCR("Current BIOS output switching policy"),
1167 acpidisp_vga_sysctl_policy_output, 0, (void *)asc, 0,
1168 CTL_CREATE, CTL_EOL);
1169 }
1170
1171 return;
1172
1173 fail:
1174 aprint_error_dev(asc->sc_dev, "couldn't add sysctl nodes\n");
1175 }
1176
1177 static void
1178 acpidisp_out_sysctl_setup(struct acpidisp_out_softc *osc)
1179 {
1180 const struct sysctlnode *rnode;
1181
1182 #ifdef ACPI_DISP_SWITCH_SYSCTLS
1183 if ((osc->sc_brctl != NULL) ||
1184 (osc->sc_caps & ACPI_DISP_OUT_CAP__DCS) ||
1185 (osc->sc_caps & ACPI_DISP_OUT_CAP__DGS)) {
1186 #else
1187 if (osc->sc_brctl != NULL) {
1188 #endif
1189 if ((sysctl_createv(&osc->sc_log, 0, NULL, &rnode,
1190 0, CTLTYPE_NODE, "acpi", NULL,
1191 NULL, 0, NULL, 0,
1192 CTL_HW, CTL_CREATE, CTL_EOL)) != 0)
1193 goto fail;
1194
1195 if ((sysctl_createv(&osc->sc_log, 0, &rnode, &rnode,
1196 0, CTLTYPE_NODE, device_xname(osc->sc_dev),
1197 SYSCTL_DESCR("ACPI display output device controls"),
1198 NULL, 0, NULL, 0,
1199 CTL_CREATE, CTL_EOL)) != 0)
1200 goto fail;
1201 }
1202
1203 if (osc->sc_brctl != NULL) {
1204 (void)sysctl_createv(&osc->sc_log, 0, &rnode, NULL,
1205 CTLFLAG_READWRITE, CTLTYPE_INT, "brightness",
1206 SYSCTL_DESCR("Current brightness level"),
1207 acpidisp_out_sysctl_brightness, 0, (void *)osc, 0,
1208 CTL_CREATE, CTL_EOL);
1209 }
1210
1211 #ifdef ACPI_DISP_SWITCH_SYSCTLS
1212 if (osc->sc_caps & ACPI_DISP_OUT_CAP__DCS) {
1213 (void)sysctl_createv(&osc->sc_log, 0, &rnode, NULL,
1214 CTLFLAG_READONLY | CTLFLAG_HEX, CTLTYPE_INT, "status",
1215 SYSCTL_DESCR("Current status"),
1216 acpidisp_out_sysctl_status, 0, (void *)osc, 0,
1217 CTL_CREATE, CTL_EOL);
1218 }
1219
1220 if (osc->sc_caps & ACPI_DISP_OUT_CAP__DGS) {
1221 int access;
1222
1223 if (osc->sc_caps & ACPI_DISP_OUT_CAP__DSS)
1224 access = CTLFLAG_READWRITE;
1225 else
1226 access = CTLFLAG_READONLY;
1227
1228 (void)sysctl_createv(&osc->sc_log, 0, &rnode, NULL,
1229 access | CTLFLAG_HEX, CTLTYPE_INT, "state",
1230 SYSCTL_DESCR("Next state (active or inactive)"),
1231 acpidisp_out_sysctl_state, 0, (void *)osc, 0,
1232 CTL_CREATE, CTL_EOL);
1233 }
1234 #endif
1235
1236 return;
1237
1238 fail:
1239 aprint_error_dev(osc->sc_dev, "couldn't add sysctl nodes\n");
1240 }
1241
1242 /*
1243 * Sysctl callbacks.
1244 */
1245
1246 #ifdef ACPI_DEBUG
1247 static int
1248 acpidisp_vga_sysctl_policy(SYSCTLFN_ARGS)
1249 {
1250 struct sysctlnode node;
1251 struct acpidisp_vga_softc *asc;
1252 uint32_t val;
1253 int error;
1254
1255 node = *rnode;
1256 asc = node.sysctl_data;
1257
1258 mutex_enter(&asc->sc_mtx);
1259 val = (uint32_t)asc->sc_policy.raw;
1260 mutex_exit(&asc->sc_mtx);
1261
1262 node.sysctl_data = &val;
1263 error = sysctl_lookup(SYSCTLFN_CALL(&node));
1264 if (error || newp == NULL)
1265 return error;
1266
1267 if (val > 0x7)
1268 return EINVAL;
1269
1270 mutex_enter(&asc->sc_mtx);
1271 asc->sc_policy.raw = (uint8_t)val;
1272 error = acpidisp_set_policy(asc, asc->sc_policy.raw);
1273 mutex_exit(&asc->sc_mtx);
1274
1275 return error;
1276 }
1277 #endif
1278
1279 static int
1280 acpidisp_vga_sysctl_policy_output(SYSCTLFN_ARGS)
1281 {
1282 struct sysctlnode node;
1283 struct acpidisp_vga_softc *asc;
1284 bool val;
1285 int error;
1286
1287 node = *rnode;
1288 asc = node.sysctl_data;
1289
1290 mutex_enter(&asc->sc_mtx);
1291 val = (asc->sc_policy.fmt.output == ACPI_DISP_POLICY_OUTPUT_AUTO);
1292 mutex_exit(&asc->sc_mtx);
1293
1294 node.sysctl_data = &val;
1295 error = sysctl_lookup(SYSCTLFN_CALL(&node));
1296 if (error || newp == NULL)
1297 return error;
1298
1299 mutex_enter(&asc->sc_mtx);
1300 if (val)
1301 asc->sc_policy.fmt.output = ACPI_DISP_POLICY_OUTPUT_AUTO;
1302 else
1303 asc->sc_policy.fmt.output = ACPI_DISP_POLICY_OUTPUT_NORMAL;
1304 error = acpidisp_set_policy(asc, asc->sc_policy.raw);
1305 mutex_exit(&asc->sc_mtx);
1306
1307 return error;
1308 }
1309
1310 #ifdef ACPI_DISP_SWITCH_SYSCTLS
1311 static int
1312 acpidisp_out_sysctl_status(SYSCTLFN_ARGS)
1313 {
1314 struct sysctlnode node;
1315 struct acpidisp_out_softc *osc;
1316 uint32_t val;
1317 int error;
1318
1319 node = *rnode;
1320 osc = node.sysctl_data;
1321
1322 mutex_enter(osc->sc_mtx);
1323 error = acpidisp_get_status(osc, &val);
1324 mutex_exit(osc->sc_mtx);
1325
1326 if (error)
1327 return error;
1328
1329 node.sysctl_data = &val;
1330 error = sysctl_lookup(SYSCTLFN_CALL(&node));
1331 if (error || newp == NULL)
1332 return error;
1333
1334 return 0;
1335 }
1336
1337 static int
1338 acpidisp_out_sysctl_state(SYSCTLFN_ARGS)
1339 {
1340 struct sysctlnode node;
1341 struct acpidisp_out_softc *osc;
1342 uint32_t val;
1343 int error;
1344
1345 node = *rnode;
1346 osc = node.sysctl_data;
1347
1348 mutex_enter(osc->sc_mtx);
1349 error = acpidisp_get_state(osc, &val);
1350 mutex_exit(osc->sc_mtx);
1351
1352 if (error)
1353 return error;
1354
1355 node.sysctl_data = &val;
1356 error = sysctl_lookup(SYSCTLFN_CALL(&node));
1357 if (error || newp == NULL)
1358 return error;
1359
1360 mutex_enter(osc->sc_mtx);
1361 error = acpidisp_set_state(osc, val);
1362 mutex_exit(osc->sc_mtx);
1363
1364 return error;
1365 }
1366 #endif
1367
1368 static int
1369 acpidisp_out_sysctl_brightness(SYSCTLFN_ARGS)
1370 {
1371 struct sysctlnode node;
1372 struct acpidisp_out_softc *osc;
1373 struct acpidisp_brctl *bc;
1374 int val, error;
1375 uint8_t lo, up, level;
1376
1377 node = *rnode;
1378 osc = node.sysctl_data;
1379 bc = osc->sc_brctl;
1380
1381 KASSERT(bc != NULL);
1382
1383 mutex_enter(osc->sc_mtx);
1384 (void)acpidisp_get_brightness(osc, &bc->bc_current);
1385 val = (int)bc->bc_current;
1386 mutex_exit(osc->sc_mtx);
1387
1388 node.sysctl_data = &val;
1389 error = sysctl_lookup(SYSCTLFN_CALL(&node));
1390 if (error || newp == NULL)
1391 return error;
1392
1393 acpidisp_array_search(bc->bc_level, bc->bc_level_count, val, &lo, &up);
1394 if ((lo != up) && (val - lo) < (up - val))
1395 level = lo;
1396 else
1397 level = up;
1398
1399 mutex_enter(osc->sc_mtx);
1400 bc->bc_current = level;
1401 error = acpidisp_set_brightness(osc, bc->bc_current);
1402 mutex_exit(osc->sc_mtx);
1403
1404 return error;
1405 }
1406
1407 /*
1408 * Initialization of acpidisp_odinfo (_DOD) and acpidisp_brctl (_BCL).
1409 */
1410
1411 /*
1412 * Regarding _DOD (ACPI 4.0a, Sec. B.4.2):
1413 *
1414 * "The _DOD method returns a list of devices attached to the graphics adapter,
1415 * along with device-specific configuration information."
1416 *
1417 * "Every child device enumerated in the ACPI namespace under the graphics
1418 * adapter must be specified in this list of devices. Each display device
1419 * must have its own ID, which is unique with respect to any other attachable
1420 * devices enumerated."
1421 *
1422 * "Return value: a package containing a variable-length list of integers,
1423 * each of which contains the 32-bit device attribute of a child device."
1424 */
1425
1426 static struct acpidisp_odinfo *
1427 acpidisp_init_odinfo(const struct acpidisp_vga_softc *asc)
1428 {
1429 ACPI_HANDLE hdl = asc->sc_node->ad_handle;
1430 ACPI_STATUS rv;
1431 ACPI_OBJECT *pkg;
1432 struct acpidisp_odinfo *oi;
1433 struct acpidisp_outdev *devp;
1434 uint32_t count, i;
1435
1436 if (!(asc->sc_caps & ACPI_DISP_VGA_CAP__DOD))
1437 return NULL;
1438
1439 oi = NULL;
1440 pkg = NULL;
1441
1442 rv = acpidisp_eval_package(hdl, "_DOD", &pkg, 1);
1443 if (ACPI_FAILURE(rv))
1444 goto fail;
1445
1446 /*
1447 * Allocate and fill the struct acpidisp_odinfo to be returned.
1448 */
1449 oi = kmem_zalloc(sizeof(*oi), KM_SLEEP);
1450 oi->oi_dev_count = pkg->Package.Count;
1451 oi->oi_dev = kmem_zalloc(oi->oi_dev_count * sizeof(*oi->oi_dev),
1452 KM_SLEEP);
1453
1454 /*
1455 * Fill the array oi->oi_dev.
1456 */
1457 for (count = 0, i = 0; i < pkg->Package.Count; i++) {
1458 /* List of 32-bit integers (ACPI 4.0a, Sec. B.4.2). */
1459 if (pkg->Package.Elements[i].Type != ACPI_TYPE_INTEGER ||
1460 pkg->Package.Elements[i].Integer.Value > UINT32_MAX)
1461 continue;
1462
1463 oi->oi_dev[count].od_attrs.raw =
1464 (uint32_t)pkg->Package.Elements[i].Integer.Value;
1465 count++;
1466 }
1467
1468 if (count == 0) {
1469 rv = AE_BAD_DATA;
1470 goto fail;
1471 }
1472
1473 ACPI_FREE(pkg);
1474 pkg = NULL;
1475
1476 /*
1477 * Resize the array oi->oi_dev if needed.
1478 */
1479 if (count < oi->oi_dev_count) {
1480 devp = kmem_alloc(count * sizeof(*devp), KM_SLEEP);
1481 (void)memcpy(devp, oi->oi_dev, count * sizeof(*devp));
1482 kmem_free(oi->oi_dev, oi->oi_dev_count * sizeof(*oi->oi_dev));
1483 oi->oi_dev = devp;
1484 oi->oi_dev_count = count;
1485 }
1486
1487 return oi;
1488
1489 fail:
1490 aprint_error_dev(asc->sc_dev, "failed to evaluate %s.%s: %s\n",
1491 acpi_name(hdl), "_DOD", AcpiFormatException(rv));
1492 if (pkg != NULL)
1493 ACPI_FREE(pkg);
1494 if (oi != NULL) {
1495 if (oi->oi_dev != NULL)
1496 kmem_free(oi->oi_dev,
1497 oi->oi_dev_count * sizeof(*oi->oi_dev));
1498 kmem_free(oi, sizeof(*oi));
1499 }
1500 return NULL;
1501 }
1502
1503 /*
1504 * acpidisp_vga_bind_outdevs:
1505 *
1506 * Bind each acpiout device attached under an acpivga device to the
1507 * corresponding (_DOD enumerated) connected output device.
1508 */
1509 static void
1510 acpidisp_vga_bind_outdevs(struct acpidisp_vga_softc *asc)
1511 {
1512 struct acpidisp_odinfo *oi = asc->sc_odinfo;
1513 struct acpidisp_out_softc *osc;
1514 struct acpidisp_outdev *od;
1515 struct acpi_devnode *ad;
1516 ACPI_HANDLE hdl;
1517 ACPI_INTEGER val;
1518 ACPI_STATUS rv;
1519 uint16_t devid;
1520 uint32_t i;
1521
1522 KASSERT(oi != NULL);
1523
1524 /* Reset all bindings. */
1525 for (i = 0, od = oi->oi_dev; i < oi->oi_dev_count; i++, od++)
1526 od->od_device = NULL;
1527
1528 /*
1529 * Iterate over all ACPI children that have been attached under this
1530 * acpivga device (as acpiout devices).
1531 */
1532 SIMPLEQ_FOREACH(ad, &asc->sc_node->ad_child_head, ad_child_list) {
1533 if ((ad->ad_device == NULL) ||
1534 (device_parent(ad->ad_device) != asc->sc_dev))
1535 continue;
1536
1537 KASSERT(device_is_a(ad->ad_device, "acpiout"));
1538
1539 osc = device_private(ad->ad_device);
1540
1541 /*
1542 * For display output devices, the method _ADR returns
1543 * the device's ID (ACPI 4.0a, Sec. B.6.1). We do not
1544 * cache the result of _ADR since it may vary.
1545 */
1546 hdl = osc->sc_node->ad_handle;
1547 rv = acpi_eval_integer(hdl, "_ADR", &val);
1548 if (ACPI_FAILURE(rv)) {
1549 aprint_error_dev(asc->sc_dev,
1550 "failed to evaluate %s.%s: %s\n",
1551 acpi_name(hdl), "_ADR", AcpiFormatException(rv));
1552 continue;
1553 }
1554
1555 /* The device ID is a 16-bit integer (ACPI 4.0a, Table B-2). */
1556 devid = (uint16_t)val;
1557
1558 /*
1559 * The device ID must be unique (among output devices), and must
1560 * appear in the list returned by _DOD (ACPI 4.0a, Sec. B.6.1).
1561 */
1562 for (i = 0, od = oi->oi_dev; i < oi->oi_dev_count; i++, od++) {
1563 if (devid == od->od_attrs.device_id) {
1564 if (od->od_device != NULL)
1565 aprint_error_dev(asc->sc_dev,
1566 "%s has same device ID as %s\n",
1567 device_xname(osc->sc_dev),
1568 device_xname(od->od_device));
1569 else
1570 od->od_device = osc->sc_dev;
1571 break;
1572 }
1573 }
1574 if (i == oi->oi_dev_count)
1575 aprint_debug_dev(asc->sc_dev,
1576 "output device %s not connected\n",
1577 device_xname(osc->sc_dev));
1578 }
1579 }
1580
1581 /*
1582 * Regarding _BCL (ACPI 4.0a, Sec. B.6.2):
1583 *
1584 * "This method allows the OS to query a list of brightness levels supported by
1585 * built-in display output devices."
1586 *
1587 * "Return value: a variable-length package containing a list of integers
1588 * representing the supported brightness levels. Each integer has 8 bits of
1589 * significant data."
1590 */
1591
1592 static struct acpidisp_brctl *
1593 acpidisp_init_brctl(const struct acpidisp_out_softc *osc)
1594 {
1595 ACPI_HANDLE hdl = osc->sc_node->ad_handle;
1596 ACPI_STATUS rv;
1597 ACPI_OBJECT *pkg;
1598 struct acpidisp_brctl *bc;
1599 uint8_t *levelp;
1600 uint32_t i;
1601 int32_t j;
1602 uint16_t count, k;
1603 uint8_t level;
1604
1605 if (!(osc->sc_caps & ACPI_DISP_OUT_CAP__BCL))
1606 return NULL;
1607
1608 bc = NULL;
1609 pkg = NULL;
1610
1611 rv = acpidisp_eval_package(hdl, "_BCL", &pkg, 2);
1612 if (ACPI_FAILURE(rv))
1613 goto fail;
1614
1615 /*
1616 * Allocate and fill the struct acpidisp_brctl to be returned.
1617 */
1618 bc = kmem_zalloc(sizeof(*bc), KM_SLEEP);
1619
1620 /* At most 256 brightness levels (8-bit integers). */
1621 if (pkg->Package.Count > 256)
1622 bc->bc_level_count = 256;
1623 else
1624 bc->bc_level_count = (uint16_t)pkg->Package.Count;
1625
1626 bc->bc_level = kmem_zalloc(bc->bc_level_count * sizeof(*bc->bc_level),
1627 KM_SLEEP);
1628
1629 /*
1630 * Fill the array bc->bc_level with an insertion sort.
1631 */
1632 for (count = 0, i = 0; i < pkg->Package.Count; i++) {
1633 /* List of 8-bit integers (ACPI 4.0a, Sec. B.6.2). */
1634 if (pkg->Package.Elements[i].Type != ACPI_TYPE_INTEGER ||
1635 pkg->Package.Elements[i].Integer.Value > UINT8_MAX)
1636 continue;
1637
1638 level = (uint8_t)pkg->Package.Elements[i].Integer.Value;
1639
1640 /* Find the correct slot but do not modify the array yet. */
1641 for (j = count; --j >= 0 && bc->bc_level[j] > level; );
1642 if (j >= 0 && bc->bc_level[j] == level)
1643 continue;
1644 j++;
1645
1646 /* Make room for the new level. */
1647 for (k = count; k > j; k--)
1648 bc->bc_level[k] = bc->bc_level[k-1];
1649
1650 /* Insert the new level. */
1651 bc->bc_level[j] = level;
1652 count++;
1653 }
1654
1655 if (count == 0) {
1656 rv = AE_BAD_DATA;
1657 goto fail;
1658 }
1659
1660 ACPI_FREE(pkg);
1661 pkg = NULL;
1662
1663 /*
1664 * Resize the array bc->bc_level if needed.
1665 */
1666 if (count < bc->bc_level_count) {
1667 levelp = kmem_alloc(count * sizeof(*levelp), KM_SLEEP);
1668 (void)memcpy(levelp, bc->bc_level, count * sizeof(*levelp));
1669 kmem_free(bc->bc_level,
1670 bc->bc_level_count * sizeof(*bc->bc_level));
1671 bc->bc_level = levelp;
1672 bc->bc_level_count = count;
1673 }
1674
1675 return bc;
1676
1677 fail:
1678 aprint_error_dev(osc->sc_dev, "failed to evaluate %s.%s: %s\n",
1679 acpi_name(hdl), "_BCL", AcpiFormatException(rv));
1680 if (pkg != NULL)
1681 ACPI_FREE(pkg);
1682 if (bc != NULL) {
1683 if (bc->bc_level != NULL)
1684 kmem_free(bc->bc_level,
1685 bc->bc_level_count * sizeof(*bc->bc_level));
1686 kmem_free(bc, sizeof(*bc));
1687 }
1688 return NULL;
1689 }
1690
1691 /*
1692 * Evaluation of simple ACPI display methods.
1693 */
1694
1695 static int
1696 acpidisp_set_policy(const struct acpidisp_vga_softc *asc, uint8_t value)
1697 {
1698 ACPI_HANDLE hdl = asc->sc_node->ad_handle;
1699 ACPI_INTEGER val;
1700 ACPI_STATUS rv;
1701
1702 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: set %s: 0x%"PRIx8"\n",
1703 device_xname(asc->sc_dev), "policy", value));
1704
1705 if (!(asc->sc_caps & ACPI_DISP_VGA_CAP__DOS))
1706 return ENODEV;
1707
1708 val = (ACPI_INTEGER)value;
1709 rv = acpi_eval_set_integer(hdl, "_DOS", val);
1710 if (ACPI_FAILURE(rv)) {
1711 aprint_error_dev(asc->sc_dev, "failed to evaluate %s.%s: %s\n",
1712 acpi_name(hdl), "_DOS", AcpiFormatException(rv));
1713 return EIO;
1714 }
1715
1716 return 0;
1717 }
1718
1719 static int
1720 acpidisp_get_status(const struct acpidisp_out_softc *osc, uint32_t *valuep)
1721 {
1722 ACPI_HANDLE hdl = osc->sc_node->ad_handle;
1723 ACPI_INTEGER val;
1724 ACPI_STATUS rv;
1725
1726 if (!(osc->sc_caps & ACPI_DISP_OUT_CAP__DCS))
1727 return ENODEV;
1728
1729 rv = acpi_eval_integer(hdl, "_DCS", &val);
1730 if (ACPI_FAILURE(rv)) {
1731 aprint_error_dev(osc->sc_dev, "failed to evaluate %s.%s: %s\n",
1732 acpi_name(hdl), "_DCS", AcpiFormatException(rv));
1733 return EIO;
1734 }
1735
1736 if (val > UINT32_MAX)
1737 return ERANGE;
1738
1739 *valuep = (uint32_t)val;
1740
1741 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: get %s: 0x%"PRIx32"\n",
1742 device_xname(osc->sc_dev), "status", *valuep));
1743
1744 return 0;
1745 }
1746
1747 static int
1748 acpidisp_get_state(const struct acpidisp_out_softc *osc, uint32_t *valuep)
1749 {
1750 ACPI_HANDLE hdl = osc->sc_node->ad_handle;
1751 ACPI_INTEGER val;
1752 ACPI_STATUS rv;
1753
1754 if (!(osc->sc_caps & ACPI_DISP_OUT_CAP__DGS))
1755 return ENODEV;
1756
1757 rv = acpi_eval_integer(hdl, "_DGS", &val);
1758 if (ACPI_FAILURE(rv)) {
1759 aprint_error_dev(osc->sc_dev, "failed to evaluate %s.%s: %s\n",
1760 acpi_name(hdl), "_DGS", AcpiFormatException(rv));
1761 return EIO;
1762 }
1763
1764 if (val > UINT32_MAX)
1765 return ERANGE;
1766
1767 *valuep = (uint32_t)val;
1768
1769 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: get %s: 0x%"PRIx32"\n",
1770 device_xname(osc->sc_dev), "state", *valuep));
1771
1772 return 0;
1773 }
1774
1775 static int
1776 acpidisp_set_state(const struct acpidisp_out_softc *osc, uint32_t value)
1777 {
1778 ACPI_HANDLE hdl = osc->sc_node->ad_handle;
1779 ACPI_INTEGER val;
1780 ACPI_STATUS rv;
1781
1782 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: set %s: 0x%"PRIx32"\n",
1783 device_xname(osc->sc_dev), "state", value));
1784
1785 if (!(osc->sc_caps & ACPI_DISP_OUT_CAP__DSS))
1786 return ENODEV;
1787
1788 val = (ACPI_INTEGER)value;
1789 rv = acpi_eval_set_integer(hdl, "_DSS", val);
1790 if (ACPI_FAILURE(rv)) {
1791 aprint_error_dev(osc->sc_dev, "failed to evaluate %s.%s: %s\n",
1792 acpi_name(hdl), "_DSS", AcpiFormatException(rv));
1793 return EIO;
1794 }
1795
1796 return 0;
1797 }
1798
1799 static int
1800 acpidisp_get_brightness(const struct acpidisp_out_softc *osc, uint8_t *valuep)
1801 {
1802 ACPI_HANDLE hdl = osc->sc_node->ad_handle;
1803 ACPI_INTEGER val;
1804 ACPI_STATUS rv;
1805
1806 if (!(osc->sc_caps & ACPI_DISP_OUT_CAP__BQC))
1807 return ENODEV;
1808
1809 if (osc->sc_brctl->bc_bqc_broken) {
1810 *valuep = osc->sc_brctl->bc_current;
1811 return 0;
1812 }
1813
1814 rv = acpi_eval_integer(hdl, "_BQC", &val);
1815 if (ACPI_FAILURE(rv)) {
1816 aprint_error_dev(osc->sc_dev, "failed to evaluate %s.%s: %s\n",
1817 acpi_name(hdl), "_BQC", AcpiFormatException(rv));
1818 return EIO;
1819 }
1820
1821 if (val > UINT8_MAX)
1822 return ERANGE;
1823
1824 *valuep = (uint8_t)val;
1825
1826 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: get %s: %"PRIu8"\n",
1827 device_xname(osc->sc_dev), "brightness", *valuep));
1828
1829 return 0;
1830 }
1831
1832 /*
1833 * Quirk for when getting the brightness value always returns the same
1834 * result, which breaks brightness controls which try to lower the
1835 * brightness by a specific value and then check if it worked.
1836 */
1837 static int
1838 acpidisp_quirk_get_brightness(const struct acpidisp_out_softc *osc)
1839 {
1840 struct acpidisp_brctl *bc;
1841 uint8_t original_brightness, test_brightness;
1842 int error;
1843
1844 bc = osc->sc_brctl;
1845
1846 /* Avoid false results if quirk already enabled */
1847 bc->bc_bqc_broken = false;
1848
1849 error = acpidisp_get_brightness(osc, &bc->bc_current);
1850 if (error)
1851 return error;
1852 original_brightness = bc->bc_current;
1853
1854 /* Find a different brightness value */
1855 test_brightness = bc->bc_level[bc->bc_level_count - 1];
1856 if (test_brightness == original_brightness)
1857 test_brightness = bc->bc_level[0];
1858
1859 if (test_brightness == original_brightness) {
1860 aprint_error_dev(osc->sc_dev,
1861 "couldn't find different brightness levels"
1862 " for _BQC quirk test\n");
1863 return 0;
1864 }
1865
1866 bc->bc_current = test_brightness;
1867 error = acpidisp_set_brightness(osc, bc->bc_current);
1868 if (error)
1869 return error;
1870
1871 error = acpidisp_get_brightness(osc, &bc->bc_current);
1872 if (error)
1873 return error;
1874
1875 /* We set a different value, but got the original value back */
1876 if (bc->bc_current == original_brightness) {
1877 aprint_normal_dev(osc->sc_dev, "_BQC broken, enabling quirk\n");
1878 bc->bc_bqc_broken = true;
1879 }
1880
1881 /* Restore original value */
1882 bc->bc_current = original_brightness;
1883 return acpidisp_set_brightness(osc, bc->bc_current);
1884 }
1885
1886 static int
1887 acpidisp_set_brightness(const struct acpidisp_out_softc *osc, uint8_t value)
1888 {
1889 ACPI_HANDLE hdl = osc->sc_node->ad_handle;
1890 ACPI_INTEGER val;
1891 ACPI_STATUS rv;
1892
1893 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: set %s: %"PRIu8"\n",
1894 device_xname(osc->sc_dev), "brightness", value));
1895
1896 if (!(osc->sc_caps & ACPI_DISP_OUT_CAP__BCM))
1897 return ENODEV;
1898
1899 val = (ACPI_INTEGER)value;
1900 rv = acpi_eval_set_integer(hdl, "_BCM", val);
1901 if (ACPI_FAILURE(rv)) {
1902 aprint_error_dev(osc->sc_dev, "failed to evaluate %s.%s: %s\n",
1903 acpi_name(hdl), "_BCM", AcpiFormatException(rv));
1904 return EIO;
1905 }
1906
1907 return 0;
1908 }
1909
1910 /*
1911 * Pretty printing.
1912 */
1913
1914 static void
1915 acpidisp_print_odinfo(device_t self, const struct acpidisp_odinfo *oi)
1916 {
1917 struct acpidisp_outdev *od;
1918 uint32_t i;
1919
1920 KASSERT(oi != NULL);
1921
1922 aprint_verbose_dev(self, "connected output devices:\n");
1923 for (i = 0, od = oi->oi_dev; i < oi->oi_dev_count; i++, od++) {
1924 aprint_verbose_dev(self, " 0x%04"PRIx16, od->od_attrs.device_id);
1925 if (od->od_device != NULL)
1926 aprint_verbose(" (%s)", device_xname(od->od_device));
1927 aprint_verbose(": ");
1928 acpidisp_print_od_attrs(od->od_attrs);
1929 aprint_verbose("\n");
1930 }
1931 }
1932
1933 /*
1934 * general purpose range printing function
1935 * 1 -> 1
1936 * 1 2 4 6 7-> [1-2,4,6-7]
1937 */
1938 static void
1939 ranger(uint8_t *a, size_t l, void (*pr)(const char *, ...) __printflike(1, 2))
1940 {
1941 uint8_t b, e;
1942
1943 if (l > 1)
1944 (*pr)("[");
1945
1946 for (size_t i = 0; i < l; i++) {
1947 for (b = e = a[i]; i + 1 < l && a[i + 1] == e + 1; i++, e++)
1948 continue;
1949 (*pr)("%"PRIu8, b);
1950 if (b != e)
1951 (*pr)("-%"PRIu8, e);
1952 if (i < l - 1)
1953 (*pr)(",");
1954 }
1955
1956 if (l > 1)
1957 (*pr)("]");
1958 }
1959
1960 static void
1961 acpidisp_print_brctl(device_t self, const struct acpidisp_brctl *bc)
1962 {
1963 KASSERT(bc != NULL);
1964
1965 aprint_verbose_dev(self, "brightness levels: ");
1966 ranger(bc->bc_level, bc->bc_level_count, aprint_verbose);
1967 aprint_verbose("\n");
1968 }
1969
1970 static void
1971 acpidisp_print_od_attrs(acpidisp_od_attrs_t oda)
1972 {
1973 const char *type;
1974
1975 if (oda.fmt.device_id_scheme == 1) {
1976 /* Uses the device ID scheme introduced in ACPI 3.0. */
1977 switch (oda.fmt.type) {
1978 case ACPI_DISP_OUT_ATTR_TYPE_OTHER:
1979 type = "Other";
1980 break;
1981 case ACPI_DISP_OUT_ATTR_TYPE_VGA:
1982 type = "VGA Analog Monitor";
1983 break;
1984 case ACPI_DISP_OUT_ATTR_TYPE_TV:
1985 type = "TV/HDTV Monitor";
1986 break;
1987 case ACPI_DISP_OUT_ATTR_TYPE_EXTDIG:
1988 type = "Ext. Digital Monitor";
1989 break;
1990 case ACPI_DISP_OUT_ATTR_TYPE_INTDFP:
1991 type = "Int. Digital Flat Panel";
1992 break;
1993 default:
1994 type = "Invalid";
1995 break;
1996 }
1997
1998 aprint_verbose("%s, index %d, port %d",
1999 type, oda.fmt.index, oda.fmt.port);
2000 } else {
2001 /* Uses vendor-specific device IDs. */
2002 switch (oda.device_id) {
2003 case ACPI_DISP_OUT_LEGACY_DEVID_MONITOR:
2004 type = "Ext. Monitor";
2005 break;
2006 case ACPI_DISP_OUT_LEGACY_DEVID_PANEL:
2007 type = "LCD Panel";
2008 break;
2009 case ACPI_DISP_OUT_LEGACY_DEVID_TV:
2010 type = "TV";
2011 break;
2012 default:
2013 type = "Unknown Output Device";
2014 break;
2015 }
2016
2017 aprint_verbose("%s", type);
2018 }
2019
2020 aprint_verbose(", head %d", oda.fmt.head_id);
2021 if (oda.fmt.bios_detect)
2022 aprint_verbose(", bios detect");
2023 if (oda.fmt.non_vga)
2024 aprint_verbose(", non vga");
2025 }
2026
2027 /*
2028 * General-purpose utility functions.
2029 */
2030
2031 /*
2032 * acpidisp_has_method:
2033 *
2034 * Returns true if and only if (a) the object handle.path exists and
2035 * (b) this object is a method or has the given type.
2036 */
2037 static bool
2038 acpidisp_has_method(ACPI_HANDLE handle, const char *path, ACPI_OBJECT_TYPE type)
2039 {
2040 ACPI_HANDLE hdl;
2041 ACPI_OBJECT_TYPE typ;
2042
2043 KASSERT(handle != NULL);
2044
2045 if (ACPI_FAILURE(AcpiGetHandle(handle, path, &hdl)))
2046 return false;
2047
2048 if (ACPI_FAILURE(AcpiGetType(hdl, &typ)))
2049 return false;
2050
2051 if (typ != ACPI_TYPE_METHOD && typ != type)
2052 return false;
2053
2054 return true;
2055 }
2056
2057 /*
2058 * acpidisp_eval_package:
2059 *
2060 * Evaluate a package (with an expected minimum number of elements).
2061 * Caller must free *pkg by ACPI_FREE().
2062 */
2063 static ACPI_STATUS
2064 acpidisp_eval_package(ACPI_HANDLE handle, const char *path, ACPI_OBJECT **pkg,
2065 unsigned int mincount)
2066 {
2067 ACPI_BUFFER buf;
2068 ACPI_OBJECT *obj;
2069 ACPI_STATUS rv;
2070
2071 rv = acpi_eval_struct(handle, path, &buf);
2072 if (ACPI_FAILURE(rv))
2073 return rv;
2074
2075 if (buf.Length == 0 || buf.Pointer == NULL)
2076 return AE_NULL_OBJECT;
2077
2078 obj = buf.Pointer;
2079
2080 if (obj->Type != ACPI_TYPE_PACKAGE) {
2081 ACPI_FREE(obj);
2082 return AE_TYPE;
2083 }
2084
2085 if (obj->Package.Count < mincount) {
2086 ACPI_FREE(obj);
2087 return AE_BAD_DATA;
2088 }
2089
2090 *pkg = obj;
2091 return rv;
2092 }
2093
2094 /*
2095 * acpidisp_array_search:
2096 *
2097 * Look for a value v in a sorted array a of n integers (n > 0). Fill *l
2098 * and *u as follows:
2099 *
2100 * *l = Max {a[i] | a[i] <= v or i = 0}
2101 * *u = Min {a[i] | a[i] >= v or i = n-1}
2102 */
2103 static void
2104 acpidisp_array_search(const uint8_t *a, uint16_t n, int v, uint8_t *l, uint8_t *u)
2105 {
2106 uint16_t i, j, m;
2107
2108 if (v <= a[0]) {
2109 *l = a[0];
2110 *u = a[0];
2111 return;
2112 }
2113 if (v >= a[n-1]) {
2114 *l = a[n-1];
2115 *u = a[n-1];
2116 return;
2117 }
2118
2119 for (i = 0, j = n - 1; j - i > 1; ) {
2120 m = (i + j) / 2;
2121
2122 if (a[m] == v) {
2123 *l = v;
2124 *u = v;
2125 return;
2126 }
2127
2128 if (a[m] < v)
2129 i = m;
2130 else
2131 j = m;
2132 }
2133
2134 /* Here a[i] < v < a[j] and j = i + 1. */
2135 *l = a[i];
2136 *u = a[j];
2137 return;
2138 }
2139
2140 MODULE(MODULE_CLASS_DRIVER, acpivga, NULL);
2141
2142 #ifdef _MODULE
2143 #include "ioconf.c"
2144 #endif
2145
2146 static int
2147 acpivga_modcmd(modcmd_t cmd, void *aux)
2148 {
2149 int rv = 0;
2150
2151 switch (cmd) {
2152
2153 case MODULE_CMD_INIT:
2154
2155 #ifdef _MODULE
2156 rv = config_init_component(cfdriver_ioconf_acpivga,
2157 cfattach_ioconf_acpivga, cfdata_ioconf_acpivga);
2158 #endif
2159 break;
2160
2161 case MODULE_CMD_FINI:
2162
2163 #ifdef _MODULE
2164 rv = config_fini_component(cfdriver_ioconf_acpivga,
2165 cfattach_ioconf_acpivga, cfdata_ioconf_acpivga);
2166 #endif
2167 break;
2168
2169 default:
2170 rv = ENOTTY;
2171 }
2172
2173 return rv;
2174 }
2175