1d0ec68d4SJustin Hibbits /*- 2d0ec68d4SJustin Hibbits * Copyright (c) 2012 Justin Hibbits 3d0ec68d4SJustin Hibbits * All rights reserved. 4d0ec68d4SJustin Hibbits * 5d0ec68d4SJustin Hibbits * Redistribution and use in source and binary forms, with or without 6d0ec68d4SJustin Hibbits * modification, are permitted provided that the following conditions 7d0ec68d4SJustin Hibbits * are met: 8d0ec68d4SJustin Hibbits * 1. Redistributions of source code must retain the above copyright 9d0ec68d4SJustin Hibbits * notice, this list of conditions and the following disclaimer. 10d0ec68d4SJustin Hibbits * 2. Redistributions in binary form must reproduce the above copyright 11d0ec68d4SJustin Hibbits * notice, this list of conditions and the following disclaimer in the 12d0ec68d4SJustin Hibbits * documentation and/or other materials provided with the distribution. 13d0ec68d4SJustin Hibbits * 14d0ec68d4SJustin Hibbits * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15d0ec68d4SJustin Hibbits * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16d0ec68d4SJustin Hibbits * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17d0ec68d4SJustin Hibbits * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18d0ec68d4SJustin Hibbits * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19d0ec68d4SJustin Hibbits * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20d0ec68d4SJustin Hibbits * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21d0ec68d4SJustin Hibbits * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22d0ec68d4SJustin Hibbits * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23d0ec68d4SJustin Hibbits * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24d0ec68d4SJustin Hibbits * SUCH DAMAGE. 25d0ec68d4SJustin Hibbits */ 26d0ec68d4SJustin Hibbits 27d0ec68d4SJustin Hibbits #include <sys/cdefs.h> 28d0ec68d4SJustin Hibbits __FBSDID("$FreeBSD$"); 29d0ec68d4SJustin Hibbits 30d0ec68d4SJustin Hibbits #include <sys/param.h> 31d0ec68d4SJustin Hibbits #include <sys/bus.h> 32d0ec68d4SJustin Hibbits #include <sys/systm.h> 33d0ec68d4SJustin Hibbits #include <sys/module.h> 34d0ec68d4SJustin Hibbits #include <sys/kernel.h> 35d0ec68d4SJustin Hibbits #include <sys/rman.h> 36d0ec68d4SJustin Hibbits #include <sys/sysctl.h> 37d0ec68d4SJustin Hibbits 38d0ec68d4SJustin Hibbits #include <machine/bus.h> 39d0ec68d4SJustin Hibbits 40d0ec68d4SJustin Hibbits #include <dev/ofw/openfirm.h> 41d0ec68d4SJustin Hibbits 42d0ec68d4SJustin Hibbits #define NVIDIA_BRIGHT_MIN (0x0ec) 43d0ec68d4SJustin Hibbits #define NVIDIA_BRIGHT_MAX (0x538) 44d0ec68d4SJustin Hibbits #define NVIDIA_BRIGHT_SCALE ((NVIDIA_BRIGHT_MAX - NVIDIA_BRIGHT_MIN)/100) 45d0ec68d4SJustin Hibbits /* nVidia's MMIO registers are at PCI BAR[0] */ 46d0ec68d4SJustin Hibbits #define NVIDIA_MMIO_PMC (0x0) 47d0ec68d4SJustin Hibbits #define NVIDIA_PMC_OFF (NVIDIA_MMIO_PMC + 0x10f0) 48d0ec68d4SJustin Hibbits #define NVIDIA_PMC_BL_SHIFT (16) 49*7a22215cSEitan Adler #define NVIDIA_PMC_BL_EN (1U << 31) 50d0ec68d4SJustin Hibbits 51d0ec68d4SJustin Hibbits 52d0ec68d4SJustin Hibbits struct nvbl_softc { 53d0ec68d4SJustin Hibbits device_t dev; 54d0ec68d4SJustin Hibbits struct resource *sc_memr; 55d0ec68d4SJustin Hibbits }; 56d0ec68d4SJustin Hibbits 57d0ec68d4SJustin Hibbits static void nvbl_identify(driver_t *driver, device_t parent); 58d0ec68d4SJustin Hibbits static int nvbl_probe(device_t dev); 59d0ec68d4SJustin Hibbits static int nvbl_attach(device_t dev); 60d0ec68d4SJustin Hibbits static int nvbl_setlevel(struct nvbl_softc *sc, int newlevel); 61d0ec68d4SJustin Hibbits static int nvbl_getlevel(struct nvbl_softc *sc); 62d0ec68d4SJustin Hibbits static int nvbl_sysctl(SYSCTL_HANDLER_ARGS); 63d0ec68d4SJustin Hibbits 64d0ec68d4SJustin Hibbits static device_method_t nvbl_methods[] = { 65d0ec68d4SJustin Hibbits /* Device interface */ 66d0ec68d4SJustin Hibbits DEVMETHOD(device_identify, nvbl_identify), 67d0ec68d4SJustin Hibbits DEVMETHOD(device_probe, nvbl_probe), 68d0ec68d4SJustin Hibbits DEVMETHOD(device_attach, nvbl_attach), 69d0ec68d4SJustin Hibbits {0, 0}, 70d0ec68d4SJustin Hibbits }; 71d0ec68d4SJustin Hibbits 72d0ec68d4SJustin Hibbits static driver_t nvbl_driver = { 73d0ec68d4SJustin Hibbits "backlight", 74d0ec68d4SJustin Hibbits nvbl_methods, 75d0ec68d4SJustin Hibbits sizeof(struct nvbl_softc) 76d0ec68d4SJustin Hibbits }; 77d0ec68d4SJustin Hibbits 78d0ec68d4SJustin Hibbits static devclass_t nvbl_devclass; 79d0ec68d4SJustin Hibbits 80d0ec68d4SJustin Hibbits DRIVER_MODULE(nvbl, vgapci, nvbl_driver, nvbl_devclass, 0, 0); 81d0ec68d4SJustin Hibbits 82d0ec68d4SJustin Hibbits static void 83d0ec68d4SJustin Hibbits nvbl_identify(driver_t *driver, device_t parent) 84d0ec68d4SJustin Hibbits { 85427467b3SJustin Hibbits if (OF_finddevice("mac-io/backlight") == -1) 86427467b3SJustin Hibbits return; 87d0ec68d4SJustin Hibbits if (device_find_child(parent, "backlight", -1) == NULL) 88d0ec68d4SJustin Hibbits device_add_child(parent, "backlight", -1); 89d0ec68d4SJustin Hibbits } 90d0ec68d4SJustin Hibbits 91d0ec68d4SJustin Hibbits static int 92d0ec68d4SJustin Hibbits nvbl_probe(device_t dev) 93d0ec68d4SJustin Hibbits { 94d0ec68d4SJustin Hibbits char control[8]; 95d0ec68d4SJustin Hibbits phandle_t handle; 96d0ec68d4SJustin Hibbits 97d0ec68d4SJustin Hibbits handle = OF_finddevice("mac-io/backlight"); 98d0ec68d4SJustin Hibbits 991342232fSJustin Hibbits if (handle == -1) 100d0ec68d4SJustin Hibbits return (ENXIO); 101d0ec68d4SJustin Hibbits 102d0ec68d4SJustin Hibbits if (OF_getprop(handle, "backlight-control", &control, sizeof(control)) < 0) 103d0ec68d4SJustin Hibbits return (ENXIO); 104d0ec68d4SJustin Hibbits 105d0ec68d4SJustin Hibbits if (strcmp(control, "mnca") != 0) 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, 141d0ec68d4SJustin Hibbits "level", CTLTYPE_INT | CTLFLAG_RW, 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