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