1d0ec68d4SJustin Hibbits /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 371e3c308SPedro F. Giffuni * 4d0ec68d4SJustin Hibbits * Copyright (c) 2012 Justin Hibbits 5d0ec68d4SJustin Hibbits * All rights reserved. 6d0ec68d4SJustin Hibbits * 7d0ec68d4SJustin Hibbits * Redistribution and use in source and binary forms, with or without 8d0ec68d4SJustin Hibbits * modification, are permitted provided that the following conditions 9d0ec68d4SJustin Hibbits * are met: 10d0ec68d4SJustin Hibbits * 1. Redistributions of source code must retain the above copyright 11d0ec68d4SJustin Hibbits * notice, this list of conditions and the following disclaimer. 12d0ec68d4SJustin Hibbits * 2. Redistributions in binary form must reproduce the above copyright 13d0ec68d4SJustin Hibbits * notice, this list of conditions and the following disclaimer in the 14d0ec68d4SJustin Hibbits * documentation and/or other materials provided with the distribution. 15d0ec68d4SJustin Hibbits * 16d0ec68d4SJustin Hibbits * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17d0ec68d4SJustin Hibbits * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18d0ec68d4SJustin Hibbits * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19d0ec68d4SJustin Hibbits * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20d0ec68d4SJustin Hibbits * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21d0ec68d4SJustin Hibbits * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22d0ec68d4SJustin Hibbits * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23d0ec68d4SJustin Hibbits * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24d0ec68d4SJustin Hibbits * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25d0ec68d4SJustin Hibbits * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26d0ec68d4SJustin Hibbits * SUCH DAMAGE. 27d0ec68d4SJustin Hibbits */ 28d0ec68d4SJustin Hibbits 29d0ec68d4SJustin Hibbits #include <sys/param.h> 30d0ec68d4SJustin Hibbits #include <sys/bus.h> 31d0ec68d4SJustin Hibbits #include <sys/systm.h> 32d0ec68d4SJustin Hibbits #include <sys/module.h> 33d0ec68d4SJustin Hibbits #include <sys/kernel.h> 34d0ec68d4SJustin Hibbits #include <sys/rman.h> 35d0ec68d4SJustin Hibbits #include <sys/sysctl.h> 36d0ec68d4SJustin Hibbits 37d0ec68d4SJustin Hibbits #include <machine/bus.h> 38d0ec68d4SJustin Hibbits 39d0ec68d4SJustin Hibbits #include <dev/ofw/openfirm.h> 409fdc5d59SJustin Hibbits #include <dev/pci/pcivar.h> 419fdc5d59SJustin Hibbits 429fdc5d59SJustin Hibbits #define PCI_VENDOR_ID_NVIDIA 0x10de 43d0ec68d4SJustin Hibbits 44d0ec68d4SJustin Hibbits #define NVIDIA_BRIGHT_MIN (0x0ec) 45d0ec68d4SJustin Hibbits #define NVIDIA_BRIGHT_MAX (0x538) 46d0ec68d4SJustin Hibbits #define NVIDIA_BRIGHT_SCALE ((NVIDIA_BRIGHT_MAX - NVIDIA_BRIGHT_MIN)/100) 47d0ec68d4SJustin Hibbits /* nVidia's MMIO registers are at PCI BAR[0] */ 48d0ec68d4SJustin Hibbits #define NVIDIA_MMIO_PMC (0x0) 49d0ec68d4SJustin Hibbits #define NVIDIA_PMC_OFF (NVIDIA_MMIO_PMC + 0x10f0) 50d0ec68d4SJustin Hibbits #define NVIDIA_PMC_BL_SHIFT (16) 517a22215cSEitan Adler #define NVIDIA_PMC_BL_EN (1U << 31) 52d0ec68d4SJustin Hibbits 53d0ec68d4SJustin Hibbits struct nvbl_softc { 54d0ec68d4SJustin Hibbits device_t dev; 55d0ec68d4SJustin Hibbits struct resource *sc_memr; 56d0ec68d4SJustin Hibbits }; 57d0ec68d4SJustin Hibbits 58d0ec68d4SJustin Hibbits static void nvbl_identify(driver_t *driver, device_t parent); 59d0ec68d4SJustin Hibbits static int nvbl_probe(device_t dev); 60d0ec68d4SJustin Hibbits static int nvbl_attach(device_t dev); 61d0ec68d4SJustin Hibbits static int nvbl_setlevel(struct nvbl_softc *sc, int newlevel); 62d0ec68d4SJustin Hibbits static int nvbl_getlevel(struct nvbl_softc *sc); 63d0ec68d4SJustin Hibbits static int nvbl_sysctl(SYSCTL_HANDLER_ARGS); 64d0ec68d4SJustin Hibbits 65d0ec68d4SJustin Hibbits static device_method_t nvbl_methods[] = { 66d0ec68d4SJustin Hibbits /* Device interface */ 67d0ec68d4SJustin Hibbits DEVMETHOD(device_identify, nvbl_identify), 68d0ec68d4SJustin Hibbits DEVMETHOD(device_probe, nvbl_probe), 69d0ec68d4SJustin Hibbits DEVMETHOD(device_attach, nvbl_attach), 70d0ec68d4SJustin Hibbits {0, 0}, 71d0ec68d4SJustin Hibbits }; 72d0ec68d4SJustin Hibbits 73d0ec68d4SJustin Hibbits static driver_t nvbl_driver = { 74d0ec68d4SJustin Hibbits "backlight", 75d0ec68d4SJustin Hibbits nvbl_methods, 76d0ec68d4SJustin Hibbits sizeof(struct nvbl_softc) 77d0ec68d4SJustin Hibbits }; 78d0ec68d4SJustin Hibbits 79992ae60bSJohn Baldwin DRIVER_MODULE(nvbl, vgapci, nvbl_driver, 0, 0); 80d0ec68d4SJustin Hibbits 81d0ec68d4SJustin Hibbits static void 82d0ec68d4SJustin Hibbits nvbl_identify(driver_t *driver, device_t parent) 83d0ec68d4SJustin Hibbits { 84427467b3SJustin Hibbits if (OF_finddevice("mac-io/backlight") == -1) 85427467b3SJustin Hibbits return; 86d0ec68d4SJustin Hibbits if (device_find_child(parent, "backlight", -1) == NULL) 87*5b56413dSWarner Losh device_add_child(parent, "backlight", DEVICE_UNIT_ANY); 88d0ec68d4SJustin Hibbits } 89d0ec68d4SJustin Hibbits 90d0ec68d4SJustin Hibbits static int 91d0ec68d4SJustin Hibbits nvbl_probe(device_t dev) 92d0ec68d4SJustin Hibbits { 93d0ec68d4SJustin Hibbits char control[8]; 94d0ec68d4SJustin Hibbits phandle_t handle; 95d0ec68d4SJustin Hibbits 96d0ec68d4SJustin Hibbits handle = OF_finddevice("mac-io/backlight"); 97d0ec68d4SJustin Hibbits 981342232fSJustin Hibbits if (handle == -1) 99d0ec68d4SJustin Hibbits return (ENXIO); 100d0ec68d4SJustin Hibbits 101d0ec68d4SJustin Hibbits if (OF_getprop(handle, "backlight-control", &control, sizeof(control)) < 0) 102d0ec68d4SJustin Hibbits return (ENXIO); 103d0ec68d4SJustin Hibbits 1049fdc5d59SJustin Hibbits if ((strcmp(control, "mnca") != 0) || 1059fdc5d59SJustin Hibbits pci_get_vendor(device_get_parent(dev)) != PCI_VENDOR_ID_NVIDIA) 106d0ec68d4SJustin Hibbits return (ENXIO); 107d0ec68d4SJustin Hibbits 108d0ec68d4SJustin Hibbits device_set_desc(dev, "PowerBook backlight for nVidia graphics"); 109d0ec68d4SJustin Hibbits 110d0ec68d4SJustin Hibbits return (0); 111d0ec68d4SJustin Hibbits } 112d0ec68d4SJustin Hibbits 113d0ec68d4SJustin Hibbits static int 114d0ec68d4SJustin Hibbits nvbl_attach(device_t dev) 115d0ec68d4SJustin Hibbits { 116d0ec68d4SJustin Hibbits struct nvbl_softc *sc; 117d0ec68d4SJustin Hibbits struct sysctl_ctx_list *ctx; 118d0ec68d4SJustin Hibbits struct sysctl_oid *tree; 119d0ec68d4SJustin Hibbits int rid; 120d0ec68d4SJustin Hibbits 121d0ec68d4SJustin Hibbits sc = device_get_softc(dev); 122d0ec68d4SJustin Hibbits 123d0ec68d4SJustin Hibbits rid = 0x10; /* BAR[0], for the MMIO register */ 124d0ec68d4SJustin Hibbits sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 125d0ec68d4SJustin Hibbits RF_ACTIVE | RF_SHAREABLE); 126d0ec68d4SJustin Hibbits if (sc->sc_memr == NULL) { 127d0ec68d4SJustin Hibbits device_printf(dev, "Could not alloc mem resource!\n"); 128d0ec68d4SJustin Hibbits return (ENXIO); 129d0ec68d4SJustin Hibbits } 130d0ec68d4SJustin Hibbits 131d0ec68d4SJustin Hibbits /* Turn on big-endian mode */ 132d0ec68d4SJustin Hibbits if (!(bus_read_stream_4(sc->sc_memr, NVIDIA_MMIO_PMC + 4) & 0x01000001)) { 133d0ec68d4SJustin Hibbits bus_write_stream_4(sc->sc_memr, NVIDIA_MMIO_PMC + 4, 0x01000001); 134d0ec68d4SJustin Hibbits mb(); 135d0ec68d4SJustin Hibbits } 136d0ec68d4SJustin Hibbits 137d0ec68d4SJustin Hibbits ctx = device_get_sysctl_ctx(dev); 138d0ec68d4SJustin Hibbits tree = device_get_sysctl_tree(dev); 139d0ec68d4SJustin Hibbits 140d0ec68d4SJustin Hibbits SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 1417029da5cSPawel Biernacki "level", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, 142d0ec68d4SJustin Hibbits nvbl_sysctl, "I", "Backlight level (0-100)"); 143d0ec68d4SJustin Hibbits 144d0ec68d4SJustin Hibbits return (0); 145d0ec68d4SJustin Hibbits } 146d0ec68d4SJustin Hibbits 147d0ec68d4SJustin Hibbits static int 148d0ec68d4SJustin Hibbits nvbl_setlevel(struct nvbl_softc *sc, int newlevel) 149d0ec68d4SJustin Hibbits { 150d0ec68d4SJustin Hibbits uint32_t pmc_reg; 151d0ec68d4SJustin Hibbits 152d0ec68d4SJustin Hibbits if (newlevel > 100) 153d0ec68d4SJustin Hibbits newlevel = 100; 154d0ec68d4SJustin Hibbits 155d0ec68d4SJustin Hibbits if (newlevel < 0) 156d0ec68d4SJustin Hibbits newlevel = 0; 157d0ec68d4SJustin Hibbits 158d0ec68d4SJustin Hibbits if (newlevel > 0) 159d0ec68d4SJustin Hibbits newlevel = (newlevel * NVIDIA_BRIGHT_SCALE) + NVIDIA_BRIGHT_MIN; 160d0ec68d4SJustin Hibbits 161d0ec68d4SJustin Hibbits pmc_reg = bus_read_stream_4(sc->sc_memr, NVIDIA_PMC_OFF) & 0xffff; 162d0ec68d4SJustin Hibbits pmc_reg |= NVIDIA_PMC_BL_EN | (newlevel << NVIDIA_PMC_BL_SHIFT); 163d0ec68d4SJustin Hibbits bus_write_stream_4(sc->sc_memr, NVIDIA_PMC_OFF, pmc_reg); 164d0ec68d4SJustin Hibbits 165d0ec68d4SJustin Hibbits return (0); 166d0ec68d4SJustin Hibbits } 167d0ec68d4SJustin Hibbits 168d0ec68d4SJustin Hibbits static int 169d0ec68d4SJustin Hibbits nvbl_getlevel(struct nvbl_softc *sc) 170d0ec68d4SJustin Hibbits { 171d0ec68d4SJustin Hibbits uint16_t level; 172d0ec68d4SJustin Hibbits 173d0ec68d4SJustin Hibbits level = bus_read_stream_2(sc->sc_memr, NVIDIA_PMC_OFF) & 0x7fff; 174d0ec68d4SJustin Hibbits 175d0ec68d4SJustin Hibbits if (level < NVIDIA_BRIGHT_MIN) 176d0ec68d4SJustin Hibbits return 0; 177d0ec68d4SJustin Hibbits 178d0ec68d4SJustin Hibbits level = (level - NVIDIA_BRIGHT_MIN) / NVIDIA_BRIGHT_SCALE; 179d0ec68d4SJustin Hibbits 180d0ec68d4SJustin Hibbits return (level); 181d0ec68d4SJustin Hibbits } 182d0ec68d4SJustin Hibbits 183d0ec68d4SJustin Hibbits static int 184d0ec68d4SJustin Hibbits nvbl_sysctl(SYSCTL_HANDLER_ARGS) 185d0ec68d4SJustin Hibbits { 186d0ec68d4SJustin Hibbits struct nvbl_softc *sc; 187d0ec68d4SJustin Hibbits int newlevel, error; 188d0ec68d4SJustin Hibbits 189d0ec68d4SJustin Hibbits sc = arg1; 190d0ec68d4SJustin Hibbits 191d0ec68d4SJustin Hibbits newlevel = nvbl_getlevel(sc); 192d0ec68d4SJustin Hibbits 193d0ec68d4SJustin Hibbits error = sysctl_handle_int(oidp, &newlevel, 0, req); 194d0ec68d4SJustin Hibbits 195d0ec68d4SJustin Hibbits if (error || !req->newptr) 196d0ec68d4SJustin Hibbits return (error); 197d0ec68d4SJustin Hibbits 198d0ec68d4SJustin Hibbits return (nvbl_setlevel(sc, newlevel)); 199d0ec68d4SJustin Hibbits } 200