1d0ec68d4SJustin Hibbits /*- 271e3c308SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 57d0ec68d4SJustin Hibbits struct nvbl_softc { 58d0ec68d4SJustin Hibbits device_t dev; 59d0ec68d4SJustin Hibbits struct resource *sc_memr; 60d0ec68d4SJustin Hibbits }; 61d0ec68d4SJustin Hibbits 62d0ec68d4SJustin Hibbits static void nvbl_identify(driver_t *driver, device_t parent); 63d0ec68d4SJustin Hibbits static int nvbl_probe(device_t dev); 64d0ec68d4SJustin Hibbits static int nvbl_attach(device_t dev); 65d0ec68d4SJustin Hibbits static int nvbl_setlevel(struct nvbl_softc *sc, int newlevel); 66d0ec68d4SJustin Hibbits static int nvbl_getlevel(struct nvbl_softc *sc); 67d0ec68d4SJustin Hibbits static int nvbl_sysctl(SYSCTL_HANDLER_ARGS); 68d0ec68d4SJustin Hibbits 69d0ec68d4SJustin Hibbits static device_method_t nvbl_methods[] = { 70d0ec68d4SJustin Hibbits /* Device interface */ 71d0ec68d4SJustin Hibbits DEVMETHOD(device_identify, nvbl_identify), 72d0ec68d4SJustin Hibbits DEVMETHOD(device_probe, nvbl_probe), 73d0ec68d4SJustin Hibbits DEVMETHOD(device_attach, nvbl_attach), 74d0ec68d4SJustin Hibbits {0, 0}, 75d0ec68d4SJustin Hibbits }; 76d0ec68d4SJustin Hibbits 77d0ec68d4SJustin Hibbits static driver_t nvbl_driver = { 78d0ec68d4SJustin Hibbits "backlight", 79d0ec68d4SJustin Hibbits nvbl_methods, 80d0ec68d4SJustin Hibbits sizeof(struct nvbl_softc) 81d0ec68d4SJustin Hibbits }; 82d0ec68d4SJustin Hibbits 83d0ec68d4SJustin Hibbits static devclass_t nvbl_devclass; 84d0ec68d4SJustin Hibbits 85d0ec68d4SJustin Hibbits DRIVER_MODULE(nvbl, vgapci, nvbl_driver, nvbl_devclass, 0, 0); 86d0ec68d4SJustin Hibbits 87d0ec68d4SJustin Hibbits static void 88d0ec68d4SJustin Hibbits nvbl_identify(driver_t *driver, device_t parent) 89d0ec68d4SJustin Hibbits { 90427467b3SJustin Hibbits if (OF_finddevice("mac-io/backlight") == -1) 91427467b3SJustin Hibbits return; 92d0ec68d4SJustin Hibbits if (device_find_child(parent, "backlight", -1) == NULL) 93d0ec68d4SJustin Hibbits device_add_child(parent, "backlight", -1); 94d0ec68d4SJustin Hibbits } 95d0ec68d4SJustin Hibbits 96d0ec68d4SJustin Hibbits static int 97d0ec68d4SJustin Hibbits nvbl_probe(device_t dev) 98d0ec68d4SJustin Hibbits { 99d0ec68d4SJustin Hibbits char control[8]; 100d0ec68d4SJustin Hibbits phandle_t handle; 101d0ec68d4SJustin Hibbits 102d0ec68d4SJustin Hibbits handle = OF_finddevice("mac-io/backlight"); 103d0ec68d4SJustin Hibbits 1041342232fSJustin Hibbits if (handle == -1) 105d0ec68d4SJustin Hibbits return (ENXIO); 106d0ec68d4SJustin Hibbits 107d0ec68d4SJustin Hibbits if (OF_getprop(handle, "backlight-control", &control, sizeof(control)) < 0) 108d0ec68d4SJustin Hibbits return (ENXIO); 109d0ec68d4SJustin Hibbits 1109fdc5d59SJustin Hibbits if ((strcmp(control, "mnca") != 0) || 1119fdc5d59SJustin Hibbits pci_get_vendor(device_get_parent(dev)) != PCI_VENDOR_ID_NVIDIA) 112d0ec68d4SJustin Hibbits return (ENXIO); 113d0ec68d4SJustin Hibbits 114d0ec68d4SJustin Hibbits device_set_desc(dev, "PowerBook backlight for nVidia graphics"); 115d0ec68d4SJustin Hibbits 116d0ec68d4SJustin Hibbits return (0); 117d0ec68d4SJustin Hibbits } 118d0ec68d4SJustin Hibbits 119d0ec68d4SJustin Hibbits static int 120d0ec68d4SJustin Hibbits nvbl_attach(device_t dev) 121d0ec68d4SJustin Hibbits { 122d0ec68d4SJustin Hibbits struct nvbl_softc *sc; 123d0ec68d4SJustin Hibbits struct sysctl_ctx_list *ctx; 124d0ec68d4SJustin Hibbits struct sysctl_oid *tree; 125d0ec68d4SJustin Hibbits int rid; 126d0ec68d4SJustin Hibbits 127d0ec68d4SJustin Hibbits sc = device_get_softc(dev); 128d0ec68d4SJustin Hibbits 129d0ec68d4SJustin Hibbits rid = 0x10; /* BAR[0], for the MMIO register */ 130d0ec68d4SJustin Hibbits sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 131d0ec68d4SJustin Hibbits RF_ACTIVE | RF_SHAREABLE); 132d0ec68d4SJustin Hibbits if (sc->sc_memr == NULL) { 133d0ec68d4SJustin Hibbits device_printf(dev, "Could not alloc mem resource!\n"); 134d0ec68d4SJustin Hibbits return (ENXIO); 135d0ec68d4SJustin Hibbits } 136d0ec68d4SJustin Hibbits 137d0ec68d4SJustin Hibbits /* Turn on big-endian mode */ 138d0ec68d4SJustin Hibbits if (!(bus_read_stream_4(sc->sc_memr, NVIDIA_MMIO_PMC + 4) & 0x01000001)) { 139d0ec68d4SJustin Hibbits bus_write_stream_4(sc->sc_memr, NVIDIA_MMIO_PMC + 4, 0x01000001); 140d0ec68d4SJustin Hibbits mb(); 141d0ec68d4SJustin Hibbits } 142d0ec68d4SJustin Hibbits 143d0ec68d4SJustin Hibbits ctx = device_get_sysctl_ctx(dev); 144d0ec68d4SJustin Hibbits tree = device_get_sysctl_tree(dev); 145d0ec68d4SJustin Hibbits 146d0ec68d4SJustin Hibbits SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 147*7029da5cSPawel Biernacki "level", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, 148d0ec68d4SJustin Hibbits nvbl_sysctl, "I", "Backlight level (0-100)"); 149d0ec68d4SJustin Hibbits 150d0ec68d4SJustin Hibbits return (0); 151d0ec68d4SJustin Hibbits } 152d0ec68d4SJustin Hibbits 153d0ec68d4SJustin Hibbits static int 154d0ec68d4SJustin Hibbits nvbl_setlevel(struct nvbl_softc *sc, int newlevel) 155d0ec68d4SJustin Hibbits { 156d0ec68d4SJustin Hibbits uint32_t pmc_reg; 157d0ec68d4SJustin Hibbits 158d0ec68d4SJustin Hibbits if (newlevel > 100) 159d0ec68d4SJustin Hibbits newlevel = 100; 160d0ec68d4SJustin Hibbits 161d0ec68d4SJustin Hibbits if (newlevel < 0) 162d0ec68d4SJustin Hibbits newlevel = 0; 163d0ec68d4SJustin Hibbits 164d0ec68d4SJustin Hibbits if (newlevel > 0) 165d0ec68d4SJustin Hibbits newlevel = (newlevel * NVIDIA_BRIGHT_SCALE) + NVIDIA_BRIGHT_MIN; 166d0ec68d4SJustin Hibbits 167d0ec68d4SJustin Hibbits pmc_reg = bus_read_stream_4(sc->sc_memr, NVIDIA_PMC_OFF) & 0xffff; 168d0ec68d4SJustin Hibbits pmc_reg |= NVIDIA_PMC_BL_EN | (newlevel << NVIDIA_PMC_BL_SHIFT); 169d0ec68d4SJustin Hibbits bus_write_stream_4(sc->sc_memr, NVIDIA_PMC_OFF, pmc_reg); 170d0ec68d4SJustin Hibbits 171d0ec68d4SJustin Hibbits return (0); 172d0ec68d4SJustin Hibbits } 173d0ec68d4SJustin Hibbits 174d0ec68d4SJustin Hibbits static int 175d0ec68d4SJustin Hibbits nvbl_getlevel(struct nvbl_softc *sc) 176d0ec68d4SJustin Hibbits { 177d0ec68d4SJustin Hibbits uint16_t level; 178d0ec68d4SJustin Hibbits 179d0ec68d4SJustin Hibbits level = bus_read_stream_2(sc->sc_memr, NVIDIA_PMC_OFF) & 0x7fff; 180d0ec68d4SJustin Hibbits 181d0ec68d4SJustin Hibbits if (level < NVIDIA_BRIGHT_MIN) 182d0ec68d4SJustin Hibbits return 0; 183d0ec68d4SJustin Hibbits 184d0ec68d4SJustin Hibbits level = (level - NVIDIA_BRIGHT_MIN) / NVIDIA_BRIGHT_SCALE; 185d0ec68d4SJustin Hibbits 186d0ec68d4SJustin Hibbits return (level); 187d0ec68d4SJustin Hibbits } 188d0ec68d4SJustin Hibbits 189d0ec68d4SJustin Hibbits static int 190d0ec68d4SJustin Hibbits nvbl_sysctl(SYSCTL_HANDLER_ARGS) 191d0ec68d4SJustin Hibbits { 192d0ec68d4SJustin Hibbits struct nvbl_softc *sc; 193d0ec68d4SJustin Hibbits int newlevel, error; 194d0ec68d4SJustin Hibbits 195d0ec68d4SJustin Hibbits sc = arg1; 196d0ec68d4SJustin Hibbits 197d0ec68d4SJustin Hibbits newlevel = nvbl_getlevel(sc); 198d0ec68d4SJustin Hibbits 199d0ec68d4SJustin Hibbits error = sysctl_handle_int(oidp, &newlevel, 0, req); 200d0ec68d4SJustin Hibbits 201d0ec68d4SJustin Hibbits if (error || !req->newptr) 202d0ec68d4SJustin Hibbits return (error); 203d0ec68d4SJustin Hibbits 204d0ec68d4SJustin Hibbits return (nvbl_setlevel(sc, newlevel)); 205d0ec68d4SJustin Hibbits } 206