1 /* $OpenBSD: abl.c,v 1.6 2025/01/23 11:24:34 kettenis Exp $ */ 2 3 /* 4 * Copyright (c) 2020 Marcus Glocker <mglocker@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Driver for controlling the screen backlight brightness on Apple machines. 21 */ 22 #include <sys/param.h> 23 #include <sys/systm.h> 24 25 #include <dev/acpi/acpivar.h> 26 #include <dev/acpi/acpidev.h> 27 #include <dev/acpi/amltypes.h> 28 #include <dev/acpi/dsdt.h> 29 30 #include <dev/pci/pcidevs.h> 31 32 #include <dev/wscons/wsconsio.h> 33 #include <dev/wscons/wsdisplayvar.h> 34 35 #ifdef ABL_DEBUG 36 #define DPRINTF(x) printf x 37 #else 38 #define DPRINTF(x) 39 #endif 40 41 #define ABL_IO_BASE_INTEL 0xb2 42 #define ABL_IO_BASE_NVIDIA 0x52e 43 #define ABL_IO_SIZE 2 44 #define ABL_IO_LO 0x00 45 #define ABL_IO_HI 0x01 46 #define ABL_BRIGHTNESS_MIN 0 47 #define ABL_BRIGHTNESS_MAX 15 48 49 struct abl_softc { 50 struct device sc_dev; 51 52 struct acpi_softc *sc_acpi; 53 struct aml_node *sc_devnode; 54 55 bus_space_tag_t sc_bt; 56 bus_space_handle_t sc_bh; 57 58 bus_addr_t sc_io_base; 59 uint8_t sc_brightness; 60 }; 61 62 int abl_match(struct device *, void *, void *); 63 void abl_attach(struct device *, struct device *, void *); 64 65 int abl_get_brightness(struct abl_softc *); 66 int abl_set_brightness(struct abl_softc *, uint8_t); 67 68 /* Hooks for wsconsole brightness control. */ 69 int abl_get_param(struct wsdisplay_param *); 70 int abl_set_param(struct wsdisplay_param *); 71 72 const struct cfattach abl_ca = { 73 sizeof(struct abl_softc), abl_match, abl_attach, NULL, NULL 74 }; 75 76 struct cfdriver abl_cd = { 77 NULL, "abl", DV_DULL 78 }; 79 80 const char *abl_hids[] = { 81 "APP0002", NULL 82 }; 83 84 int 85 abl_match(struct device *parent, void *match, void *aux) 86 { 87 struct acpi_attach_args *aa = aux; 88 struct cfdata *cf = match; 89 90 return acpi_matchhids(aa, abl_hids, cf->cf_driver->cd_name); 91 } 92 93 void 94 abl_attach(struct device *parent, struct device *self, void *aux) 95 { 96 struct abl_softc *sc = (struct abl_softc *)self; 97 struct acpi_attach_args *aaa = aux; 98 struct aml_value res; 99 int64_t sta; 100 int reg; 101 pci_chipset_tag_t pc; 102 pcitag_t tag; 103 104 sc->sc_acpi = (struct acpi_softc *)parent; 105 sc->sc_devnode = aaa->aaa_node; 106 107 printf(": %s", sc->sc_devnode->name); 108 109 sta = acpi_getsta(sc->sc_acpi, sc->sc_devnode); 110 if ((sta & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) != 111 (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) { 112 printf(": not enabled\n"); 113 return; 114 } 115 116 if (!(aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CID", 0, NULL, &res))) 117 printf(" (%s)", res.v_string); 118 119 /* Backlight on non-iMacs is already handled differently. */ 120 if (strncmp(hw_prod, "iMac", 4)) { 121 printf("\n"); 122 return; 123 } 124 125 /* 126 * We need to check on what type of PCI controller we're running on to 127 * access the right I/O space. 128 */ 129 pc = pci_lookup_segment(0, 0); 130 tag = pci_make_tag(pc, 0, 0, 0); 131 reg = pci_conf_read(pc, tag, PCI_ID_REG); 132 133 switch (PCI_VENDOR(reg)) { 134 case PCI_VENDOR_INTEL: 135 sc->sc_io_base = ABL_IO_BASE_INTEL; 136 break; 137 case PCI_VENDOR_NVIDIA: 138 sc->sc_io_base = ABL_IO_BASE_NVIDIA; 139 break; 140 default: 141 printf(": pci controller not supported\n"); 142 return; 143 } 144 145 /* 146 * Map I/O space. This driver uses the ACPI SMI command port. 147 * This port has already been claimed by the generic ACPI code 148 * so we need to work around that here by calling _bus_space_map(). 149 */ 150 sc->sc_bt = aaa->aaa_iot; 151 if (_bus_space_map(sc->sc_bt, sc->sc_io_base, ABL_IO_SIZE, 0, 152 &sc->sc_bh)) { 153 printf(": can't map register\n"); 154 return; 155 } 156 157 /* Read the current brightness value initially. */ 158 sc->sc_brightness = abl_get_brightness(sc); 159 160 /* Map wsconsole hook functions. */ 161 ws_get_param = abl_get_param; 162 ws_set_param = abl_set_param; 163 164 printf("\n"); 165 } 166 167 int 168 abl_get_brightness(struct abl_softc *sc) 169 { 170 uint8_t val; 171 172 bus_space_write_1(sc->sc_bt, sc->sc_bh, ABL_IO_HI, 0x03); 173 bus_space_write_1(sc->sc_bt, sc->sc_bh, ABL_IO_LO, 0xbf); 174 val = bus_space_read_1(sc->sc_bt, sc->sc_bh, ABL_IO_HI); 175 176 return (val >> 4); 177 } 178 179 int 180 abl_set_brightness(struct abl_softc *sc, uint8_t val) 181 { 182 bus_space_write_1(sc->sc_bt, sc->sc_bh, ABL_IO_HI, 0x04 | (val << 4)); 183 bus_space_write_1(sc->sc_bt, sc->sc_bh, ABL_IO_LO, 0xbf); 184 185 return 0; 186 } 187 188 int 189 abl_get_param(struct wsdisplay_param *dp) 190 { 191 struct abl_softc *sc = abl_cd.cd_devs[0]; 192 193 if (sc == NULL) 194 return -1; 195 196 switch (dp->param) { 197 case WSDISPLAYIO_PARAM_BRIGHTNESS: 198 DPRINTF(("abl_get_param: sc->sc_brightness = %d\n", 199 sc->sc_brightness)); 200 dp->min = ABL_BRIGHTNESS_MIN; 201 dp->max = ABL_BRIGHTNESS_MAX; 202 dp->curval = sc->sc_brightness; 203 return 0; 204 default: 205 return -1; 206 } 207 } 208 209 int 210 abl_set_param(struct wsdisplay_param *dp) 211 { 212 struct abl_softc *sc = abl_cd.cd_devs[0]; 213 214 if (sc == NULL) 215 return -1; 216 217 switch (dp->param) { 218 case WSDISPLAYIO_PARAM_BRIGHTNESS: 219 DPRINTF(("abl_set_param: curval = %d\n", dp->curval)); 220 if (dp->curval < ABL_BRIGHTNESS_MIN) 221 dp->curval = 0; 222 if (dp->curval > ABL_BRIGHTNESS_MAX) 223 dp->curval = ABL_BRIGHTNESS_MAX; 224 abl_set_brightness(sc, dp->curval); 225 sc->sc_brightness = dp->curval; 226 return 0; 227 default: 228 return -1; 229 } 230 } 231