1d0ec68d4SJustin Hibbits /*- 2*4d846d26SWarner 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/cdefs.h> 30d0ec68d4SJustin Hibbits __FBSDID("$FreeBSD$"); 31d0ec68d4SJustin Hibbits 32d0ec68d4SJustin Hibbits #include <sys/param.h> 33d0ec68d4SJustin Hibbits #include <sys/bus.h> 34d0ec68d4SJustin Hibbits #include <sys/systm.h> 35d0ec68d4SJustin Hibbits #include <sys/module.h> 36d0ec68d4SJustin Hibbits #include <sys/kernel.h> 37d0ec68d4SJustin Hibbits #include <sys/rman.h> 38d0ec68d4SJustin Hibbits #include <sys/sysctl.h> 39d0ec68d4SJustin Hibbits 40d0ec68d4SJustin Hibbits #include <machine/bus.h> 41d0ec68d4SJustin Hibbits 42d0ec68d4SJustin Hibbits #include <dev/ofw/openfirm.h> 439fdc5d59SJustin Hibbits #include <dev/pci/pcivar.h> 449fdc5d59SJustin Hibbits 459fdc5d59SJustin Hibbits #define PCI_VENDOR_ID_NVIDIA 0x10de 46d0ec68d4SJustin Hibbits 47d0ec68d4SJustin Hibbits #define NVIDIA_BRIGHT_MIN (0x0ec) 48d0ec68d4SJustin Hibbits #define NVIDIA_BRIGHT_MAX (0x538) 49d0ec68d4SJustin Hibbits #define NVIDIA_BRIGHT_SCALE ((NVIDIA_BRIGHT_MAX - NVIDIA_BRIGHT_MIN)/100) 50d0ec68d4SJustin Hibbits /* nVidia's MMIO registers are at PCI BAR[0] */ 51d0ec68d4SJustin Hibbits #define NVIDIA_MMIO_PMC (0x0) 52d0ec68d4SJustin Hibbits #define NVIDIA_PMC_OFF (NVIDIA_MMIO_PMC + 0x10f0) 53d0ec68d4SJustin Hibbits #define NVIDIA_PMC_BL_SHIFT (16) 547a22215cSEitan Adler #define NVIDIA_PMC_BL_EN (1U << 31) 55d0ec68d4SJustin Hibbits 56d0ec68d4SJustin Hibbits struct nvbl_softc { 57d0ec68d4SJustin Hibbits device_t dev; 58d0ec68d4SJustin Hibbits struct resource *sc_memr; 59d0ec68d4SJustin Hibbits }; 60d0ec68d4SJustin Hibbits 61d0ec68d4SJustin Hibbits static void nvbl_identify(driver_t *driver, device_t parent); 62d0ec68d4SJustin Hibbits static int nvbl_probe(device_t dev); 63d0ec68d4SJustin Hibbits static int nvbl_attach(device_t dev); 64d0ec68d4SJustin Hibbits static int nvbl_setlevel(struct nvbl_softc *sc, int newlevel); 65d0ec68d4SJustin Hibbits static int nvbl_getlevel(struct nvbl_softc *sc); 66d0ec68d4SJustin Hibbits static int nvbl_sysctl(SYSCTL_HANDLER_ARGS); 67d0ec68d4SJustin Hibbits 68d0ec68d4SJustin Hibbits static device_method_t nvbl_methods[] = { 69d0ec68d4SJustin Hibbits /* Device interface */ 70d0ec68d4SJustin Hibbits DEVMETHOD(device_identify, nvbl_identify), 71d0ec68d4SJustin Hibbits DEVMETHOD(device_probe, nvbl_probe), 72d0ec68d4SJustin Hibbits DEVMETHOD(device_attach, nvbl_attach), 73d0ec68d4SJustin Hibbits {0, 0}, 74d0ec68d4SJustin Hibbits }; 75d0ec68d4SJustin Hibbits 76d0ec68d4SJustin Hibbits static driver_t nvbl_driver = { 77d0ec68d4SJustin Hibbits "backlight", 78d0ec68d4SJustin Hibbits nvbl_methods, 79d0ec68d4SJustin Hibbits sizeof(struct nvbl_softc) 80d0ec68d4SJustin Hibbits }; 81d0ec68d4SJustin Hibbits 82992ae60bSJohn Baldwin DRIVER_MODULE(nvbl, vgapci, nvbl_driver, 0, 0); 83d0ec68d4SJustin Hibbits 84d0ec68d4SJustin Hibbits static void 85d0ec68d4SJustin Hibbits nvbl_identify(driver_t *driver, device_t parent) 86d0ec68d4SJustin Hibbits { 87427467b3SJustin Hibbits if (OF_finddevice("mac-io/backlight") == -1) 88427467b3SJustin Hibbits return; 89d0ec68d4SJustin Hibbits if (device_find_child(parent, "backlight", -1) == NULL) 90d0ec68d4SJustin Hibbits device_add_child(parent, "backlight", -1); 91d0ec68d4SJustin Hibbits } 92d0ec68d4SJustin Hibbits 93d0ec68d4SJustin Hibbits static int 94d0ec68d4SJustin Hibbits nvbl_probe(device_t dev) 95d0ec68d4SJustin Hibbits { 96d0ec68d4SJustin Hibbits char control[8]; 97d0ec68d4SJustin Hibbits phandle_t handle; 98d0ec68d4SJustin Hibbits 99d0ec68d4SJustin Hibbits handle = OF_finddevice("mac-io/backlight"); 100d0ec68d4SJustin Hibbits 1011342232fSJustin Hibbits if (handle == -1) 102d0ec68d4SJustin Hibbits return (ENXIO); 103d0ec68d4SJustin Hibbits 104d0ec68d4SJustin Hibbits if (OF_getprop(handle, "backlight-control", &control, sizeof(control)) < 0) 105d0ec68d4SJustin Hibbits return (ENXIO); 106d0ec68d4SJustin Hibbits 1079fdc5d59SJustin Hibbits if ((strcmp(control, "mnca") != 0) || 1089fdc5d59SJustin Hibbits pci_get_vendor(device_get_parent(dev)) != PCI_VENDOR_ID_NVIDIA) 109d0ec68d4SJustin Hibbits return (ENXIO); 110d0ec68d4SJustin Hibbits 111d0ec68d4SJustin Hibbits device_set_desc(dev, "PowerBook backlight for nVidia graphics"); 112d0ec68d4SJustin Hibbits 113d0ec68d4SJustin Hibbits return (0); 114d0ec68d4SJustin Hibbits } 115d0ec68d4SJustin Hibbits 116d0ec68d4SJustin Hibbits static int 117d0ec68d4SJustin Hibbits nvbl_attach(device_t dev) 118d0ec68d4SJustin Hibbits { 119d0ec68d4SJustin Hibbits struct nvbl_softc *sc; 120d0ec68d4SJustin Hibbits struct sysctl_ctx_list *ctx; 121d0ec68d4SJustin Hibbits struct sysctl_oid *tree; 122d0ec68d4SJustin Hibbits int rid; 123d0ec68d4SJustin Hibbits 124d0ec68d4SJustin Hibbits sc = device_get_softc(dev); 125d0ec68d4SJustin Hibbits 126d0ec68d4SJustin Hibbits rid = 0x10; /* BAR[0], for the MMIO register */ 127d0ec68d4SJustin Hibbits sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 128d0ec68d4SJustin Hibbits RF_ACTIVE | RF_SHAREABLE); 129d0ec68d4SJustin Hibbits if (sc->sc_memr == NULL) { 130d0ec68d4SJustin Hibbits device_printf(dev, "Could not alloc mem resource!\n"); 131d0ec68d4SJustin Hibbits return (ENXIO); 132d0ec68d4SJustin Hibbits } 133d0ec68d4SJustin Hibbits 134d0ec68d4SJustin Hibbits /* Turn on big-endian mode */ 135d0ec68d4SJustin Hibbits if (!(bus_read_stream_4(sc->sc_memr, NVIDIA_MMIO_PMC + 4) & 0x01000001)) { 136d0ec68d4SJustin Hibbits bus_write_stream_4(sc->sc_memr, NVIDIA_MMIO_PMC + 4, 0x01000001); 137d0ec68d4SJustin Hibbits mb(); 138d0ec68d4SJustin Hibbits } 139d0ec68d4SJustin Hibbits 140d0ec68d4SJustin Hibbits ctx = device_get_sysctl_ctx(dev); 141d0ec68d4SJustin Hibbits tree = device_get_sysctl_tree(dev); 142d0ec68d4SJustin Hibbits 143d0ec68d4SJustin Hibbits SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 1447029da5cSPawel Biernacki "level", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, 145d0ec68d4SJustin Hibbits nvbl_sysctl, "I", "Backlight level (0-100)"); 146d0ec68d4SJustin Hibbits 147d0ec68d4SJustin Hibbits return (0); 148d0ec68d4SJustin Hibbits } 149d0ec68d4SJustin Hibbits 150d0ec68d4SJustin Hibbits static int 151d0ec68d4SJustin Hibbits nvbl_setlevel(struct nvbl_softc *sc, int newlevel) 152d0ec68d4SJustin Hibbits { 153d0ec68d4SJustin Hibbits uint32_t pmc_reg; 154d0ec68d4SJustin Hibbits 155d0ec68d4SJustin Hibbits if (newlevel > 100) 156d0ec68d4SJustin Hibbits newlevel = 100; 157d0ec68d4SJustin Hibbits 158d0ec68d4SJustin Hibbits if (newlevel < 0) 159d0ec68d4SJustin Hibbits newlevel = 0; 160d0ec68d4SJustin Hibbits 161d0ec68d4SJustin Hibbits if (newlevel > 0) 162d0ec68d4SJustin Hibbits newlevel = (newlevel * NVIDIA_BRIGHT_SCALE) + NVIDIA_BRIGHT_MIN; 163d0ec68d4SJustin Hibbits 164d0ec68d4SJustin Hibbits pmc_reg = bus_read_stream_4(sc->sc_memr, NVIDIA_PMC_OFF) & 0xffff; 165d0ec68d4SJustin Hibbits pmc_reg |= NVIDIA_PMC_BL_EN | (newlevel << NVIDIA_PMC_BL_SHIFT); 166d0ec68d4SJustin Hibbits bus_write_stream_4(sc->sc_memr, NVIDIA_PMC_OFF, pmc_reg); 167d0ec68d4SJustin Hibbits 168d0ec68d4SJustin Hibbits return (0); 169d0ec68d4SJustin Hibbits } 170d0ec68d4SJustin Hibbits 171d0ec68d4SJustin Hibbits static int 172d0ec68d4SJustin Hibbits nvbl_getlevel(struct nvbl_softc *sc) 173d0ec68d4SJustin Hibbits { 174d0ec68d4SJustin Hibbits uint16_t level; 175d0ec68d4SJustin Hibbits 176d0ec68d4SJustin Hibbits level = bus_read_stream_2(sc->sc_memr, NVIDIA_PMC_OFF) & 0x7fff; 177d0ec68d4SJustin Hibbits 178d0ec68d4SJustin Hibbits if (level < NVIDIA_BRIGHT_MIN) 179d0ec68d4SJustin Hibbits return 0; 180d0ec68d4SJustin Hibbits 181d0ec68d4SJustin Hibbits level = (level - NVIDIA_BRIGHT_MIN) / NVIDIA_BRIGHT_SCALE; 182d0ec68d4SJustin Hibbits 183d0ec68d4SJustin Hibbits return (level); 184d0ec68d4SJustin Hibbits } 185d0ec68d4SJustin Hibbits 186d0ec68d4SJustin Hibbits static int 187d0ec68d4SJustin Hibbits nvbl_sysctl(SYSCTL_HANDLER_ARGS) 188d0ec68d4SJustin Hibbits { 189d0ec68d4SJustin Hibbits struct nvbl_softc *sc; 190d0ec68d4SJustin Hibbits int newlevel, error; 191d0ec68d4SJustin Hibbits 192d0ec68d4SJustin Hibbits sc = arg1; 193d0ec68d4SJustin Hibbits 194d0ec68d4SJustin Hibbits newlevel = nvbl_getlevel(sc); 195d0ec68d4SJustin Hibbits 196d0ec68d4SJustin Hibbits error = sysctl_handle_int(oidp, &newlevel, 0, req); 197d0ec68d4SJustin Hibbits 198d0ec68d4SJustin Hibbits if (error || !req->newptr) 199d0ec68d4SJustin Hibbits return (error); 200d0ec68d4SJustin Hibbits 201d0ec68d4SJustin Hibbits return (nvbl_setlevel(sc, newlevel)); 202d0ec68d4SJustin Hibbits } 203