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