1 /* $OpenBSD: abl.c,v 1.2 2020/09/16 09:35:14 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 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 /* 120 * We need to check on what type of PCI controler we're running on to 121 * access the right I/O space. 122 */ 123 pc = pci_lookup_segment(0); 124 tag = pci_make_tag(pc, 0, 0, 0); 125 reg = pci_conf_read(pc, tag, PCI_ID_REG); 126 127 switch (PCI_VENDOR(reg)) { 128 case PCI_VENDOR_INTEL: 129 sc->sc_io_base = ABL_IO_BASE_INTEL; 130 break; 131 case PCI_VENDOR_NVIDIA: 132 sc->sc_io_base = ABL_IO_BASE_NVIDIA; 133 break; 134 default: 135 printf(": pci controller not supported\n"); 136 return; 137 } 138 139 /* 140 * Map I/O space. This driver uses the ACPI SMI command port. 141 * This port has already been claimed by the generic ACPI code 142 * so we need to work around that here by calling _bus_space_map(). 143 */ 144 sc->sc_bt = aaa->aaa_iot; 145 if (_bus_space_map(sc->sc_bt, sc->sc_io_base, ABL_IO_SIZE, 0, 146 &sc->sc_bh)) { 147 printf(": can't map register\n"); 148 return; 149 } 150 151 /* Read the current brightness value initially. */ 152 sc->sc_brightness = abl_get_brightness(sc); 153 154 /* Map wsconsole hook functions. */ 155 ws_get_param = abl_get_param; 156 ws_set_param = abl_set_param; 157 158 printf("\n"); 159 } 160 161 int 162 abl_get_brightness(struct abl_softc *sc) 163 { 164 uint8_t val; 165 166 bus_space_write_1(sc->sc_bt, sc->sc_bh, ABL_IO_HI, 0x03); 167 bus_space_write_1(sc->sc_bt, sc->sc_bh, ABL_IO_LO, 0xbf); 168 val = bus_space_read_1(sc->sc_bt, sc->sc_bh, ABL_IO_HI); 169 170 return (val >> 4); 171 } 172 173 int 174 abl_set_brightness(struct abl_softc *sc, uint8_t val) 175 { 176 bus_space_write_1(sc->sc_bt, sc->sc_bh, ABL_IO_HI, 0x04 | (val << 4)); 177 bus_space_write_1(sc->sc_bt, sc->sc_bh, ABL_IO_LO, 0xbf); 178 179 return 0; 180 } 181 182 int 183 abl_get_param(struct wsdisplay_param *dp) 184 { 185 struct abl_softc *sc = abl_cd.cd_devs[0]; 186 187 if (sc == NULL) 188 return -1; 189 190 switch (dp->param) { 191 case WSDISPLAYIO_PARAM_BRIGHTNESS: 192 DPRINTF(("abl_get_param: sc->sc_brightness = %d\n", 193 sc->sc_brightness)); 194 dp->min = ABL_BRIGHTNESS_MIN; 195 dp->max = ABL_BRIGHTNESS_MAX; 196 dp->curval = sc->sc_brightness; 197 return 0; 198 default: 199 return -1; 200 } 201 } 202 203 int 204 abl_set_param(struct wsdisplay_param *dp) 205 { 206 struct abl_softc *sc = abl_cd.cd_devs[0]; 207 208 if (sc == NULL) 209 return -1; 210 211 switch (dp->param) { 212 case WSDISPLAYIO_PARAM_BRIGHTNESS: 213 DPRINTF(("abl_set_param: curval = %d\n", dp->curval)); 214 if (dp->curval < ABL_BRIGHTNESS_MIN) 215 dp->curval = 0; 216 if (dp->curval > ABL_BRIGHTNESS_MAX) 217 dp->curval = ABL_BRIGHTNESS_MAX; 218 abl_set_brightness(sc, dp->curval); 219 sc->sc_brightness = dp->curval; 220 return 0; 221 default: 222 return -1; 223 } 224 } 225