1239027Sjhibbits/*- 2239027Sjhibbits * Copyright (c) 2012 Justin Hibbits 3239027Sjhibbits * All rights reserved. 4239027Sjhibbits * 5239027Sjhibbits * Redistribution and use in source and binary forms, with or without 6239027Sjhibbits * modification, are permitted provided that the following conditions 7239027Sjhibbits * are met: 8239027Sjhibbits * 1. Redistributions of source code must retain the above copyright 9239027Sjhibbits * notice, this list of conditions and the following disclaimer. 10239027Sjhibbits * 2. Redistributions in binary form must reproduce the above copyright 11239027Sjhibbits * notice, this list of conditions and the following disclaimer in the 12239027Sjhibbits * documentation and/or other materials provided with the distribution. 13239027Sjhibbits * 14239027Sjhibbits * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15239027Sjhibbits * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16239027Sjhibbits * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17239027Sjhibbits * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18239027Sjhibbits * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19239027Sjhibbits * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20239027Sjhibbits * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21239027Sjhibbits * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22239027Sjhibbits * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23239027Sjhibbits * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24239027Sjhibbits * SUCH DAMAGE. 25239027Sjhibbits */ 26239027Sjhibbits 27239027Sjhibbits#include <sys/cdefs.h> 28239027Sjhibbits__FBSDID("$FreeBSD$"); 29239027Sjhibbits 30239027Sjhibbits#include <sys/param.h> 31239027Sjhibbits#include <sys/bus.h> 32239027Sjhibbits#include <sys/systm.h> 33239027Sjhibbits#include <sys/module.h> 34239027Sjhibbits#include <sys/kernel.h> 35239027Sjhibbits#include <sys/rman.h> 36239027Sjhibbits#include <sys/sysctl.h> 37239027Sjhibbits 38239027Sjhibbits#include <machine/bus.h> 39239027Sjhibbits 40239027Sjhibbits#include <dev/ofw/openfirm.h> 41239027Sjhibbits 42239027Sjhibbits#define NVIDIA_BRIGHT_MIN (0x0ec) 43239027Sjhibbits#define NVIDIA_BRIGHT_MAX (0x538) 44239027Sjhibbits#define NVIDIA_BRIGHT_SCALE ((NVIDIA_BRIGHT_MAX - NVIDIA_BRIGHT_MIN)/100) 45239027Sjhibbits/* nVidia's MMIO registers are at PCI BAR[0] */ 46239027Sjhibbits#define NVIDIA_MMIO_PMC (0x0) 47239027Sjhibbits#define NVIDIA_PMC_OFF (NVIDIA_MMIO_PMC + 0x10f0) 48239027Sjhibbits#define NVIDIA_PMC_BL_SHIFT (16) 49261455Seadler#define NVIDIA_PMC_BL_EN (1U << 31) 50239027Sjhibbits 51239027Sjhibbits 52239027Sjhibbitsstruct nvbl_softc { 53239027Sjhibbits device_t dev; 54239027Sjhibbits struct resource *sc_memr; 55239027Sjhibbits}; 56239027Sjhibbits 57239027Sjhibbitsstatic void nvbl_identify(driver_t *driver, device_t parent); 58239027Sjhibbitsstatic int nvbl_probe(device_t dev); 59239027Sjhibbitsstatic int nvbl_attach(device_t dev); 60239027Sjhibbitsstatic int nvbl_setlevel(struct nvbl_softc *sc, int newlevel); 61239027Sjhibbitsstatic int nvbl_getlevel(struct nvbl_softc *sc); 62239027Sjhibbitsstatic int nvbl_sysctl(SYSCTL_HANDLER_ARGS); 63239027Sjhibbits 64239027Sjhibbitsstatic device_method_t nvbl_methods[] = { 65239027Sjhibbits /* Device interface */ 66239027Sjhibbits DEVMETHOD(device_identify, nvbl_identify), 67239027Sjhibbits DEVMETHOD(device_probe, nvbl_probe), 68239027Sjhibbits DEVMETHOD(device_attach, nvbl_attach), 69239027Sjhibbits {0, 0}, 70239027Sjhibbits}; 71239027Sjhibbits 72239027Sjhibbitsstatic driver_t nvbl_driver = { 73239027Sjhibbits "backlight", 74239027Sjhibbits nvbl_methods, 75239027Sjhibbits sizeof(struct nvbl_softc) 76239027Sjhibbits}; 77239027Sjhibbits 78239027Sjhibbitsstatic devclass_t nvbl_devclass; 79239027Sjhibbits 80239027SjhibbitsDRIVER_MODULE(nvbl, vgapci, nvbl_driver, nvbl_devclass, 0, 0); 81239027Sjhibbits 82239027Sjhibbitsstatic void 83239027Sjhibbitsnvbl_identify(driver_t *driver, device_t parent) 84239027Sjhibbits{ 85255100Sjhibbits if (OF_finddevice("mac-io/backlight") == -1) 86255100Sjhibbits return; 87239027Sjhibbits if (device_find_child(parent, "backlight", -1) == NULL) 88239027Sjhibbits device_add_child(parent, "backlight", -1); 89239027Sjhibbits} 90239027Sjhibbits 91239027Sjhibbitsstatic int 92239027Sjhibbitsnvbl_probe(device_t dev) 93239027Sjhibbits{ 94239027Sjhibbits char control[8]; 95239027Sjhibbits phandle_t handle; 96239027Sjhibbits 97239027Sjhibbits handle = OF_finddevice("mac-io/backlight"); 98239027Sjhibbits 99239548Sjhibbits if (handle == -1) 100239027Sjhibbits return (ENXIO); 101239027Sjhibbits 102239027Sjhibbits if (OF_getprop(handle, "backlight-control", &control, sizeof(control)) < 0) 103239027Sjhibbits return (ENXIO); 104239027Sjhibbits 105239027Sjhibbits if (strcmp(control, "mnca") != 0) 106239027Sjhibbits return (ENXIO); 107239027Sjhibbits 108239027Sjhibbits device_set_desc(dev, "PowerBook backlight for nVidia graphics"); 109239027Sjhibbits 110239027Sjhibbits return (0); 111239027Sjhibbits} 112239027Sjhibbits 113239027Sjhibbitsstatic int 114239027Sjhibbitsnvbl_attach(device_t dev) 115239027Sjhibbits{ 116239027Sjhibbits struct nvbl_softc *sc; 117239027Sjhibbits struct sysctl_ctx_list *ctx; 118239027Sjhibbits struct sysctl_oid *tree; 119239027Sjhibbits int rid; 120239027Sjhibbits 121239027Sjhibbits sc = device_get_softc(dev); 122239027Sjhibbits 123239027Sjhibbits rid = 0x10; /* BAR[0], for the MMIO register */ 124239027Sjhibbits sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 125239027Sjhibbits RF_ACTIVE | RF_SHAREABLE); 126239027Sjhibbits if (sc->sc_memr == NULL) { 127239027Sjhibbits device_printf(dev, "Could not alloc mem resource!\n"); 128239027Sjhibbits return (ENXIO); 129239027Sjhibbits } 130239027Sjhibbits 131239027Sjhibbits /* Turn on big-endian mode */ 132239027Sjhibbits if (!(bus_read_stream_4(sc->sc_memr, NVIDIA_MMIO_PMC + 4) & 0x01000001)) { 133239027Sjhibbits bus_write_stream_4(sc->sc_memr, NVIDIA_MMIO_PMC + 4, 0x01000001); 134239027Sjhibbits mb(); 135239027Sjhibbits } 136239027Sjhibbits 137239027Sjhibbits ctx = device_get_sysctl_ctx(dev); 138239027Sjhibbits tree = device_get_sysctl_tree(dev); 139239027Sjhibbits 140239027Sjhibbits SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 141239027Sjhibbits "level", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 142239027Sjhibbits nvbl_sysctl, "I", "Backlight level (0-100)"); 143239027Sjhibbits 144239027Sjhibbits return (0); 145239027Sjhibbits} 146239027Sjhibbits 147239027Sjhibbitsstatic int 148239027Sjhibbitsnvbl_setlevel(struct nvbl_softc *sc, int newlevel) 149239027Sjhibbits{ 150239027Sjhibbits uint32_t pmc_reg; 151239027Sjhibbits 152239027Sjhibbits if (newlevel > 100) 153239027Sjhibbits newlevel = 100; 154239027Sjhibbits 155239027Sjhibbits if (newlevel < 0) 156239027Sjhibbits newlevel = 0; 157239027Sjhibbits 158239027Sjhibbits if (newlevel > 0) 159239027Sjhibbits newlevel = (newlevel * NVIDIA_BRIGHT_SCALE) + NVIDIA_BRIGHT_MIN; 160239027Sjhibbits 161239027Sjhibbits pmc_reg = bus_read_stream_4(sc->sc_memr, NVIDIA_PMC_OFF) & 0xffff; 162239027Sjhibbits pmc_reg |= NVIDIA_PMC_BL_EN | (newlevel << NVIDIA_PMC_BL_SHIFT); 163239027Sjhibbits bus_write_stream_4(sc->sc_memr, NVIDIA_PMC_OFF, pmc_reg); 164239027Sjhibbits 165239027Sjhibbits return (0); 166239027Sjhibbits} 167239027Sjhibbits 168239027Sjhibbitsstatic int 169239027Sjhibbitsnvbl_getlevel(struct nvbl_softc *sc) 170239027Sjhibbits{ 171239027Sjhibbits uint16_t level; 172239027Sjhibbits 173239027Sjhibbits level = bus_read_stream_2(sc->sc_memr, NVIDIA_PMC_OFF) & 0x7fff; 174239027Sjhibbits 175239027Sjhibbits if (level < NVIDIA_BRIGHT_MIN) 176239027Sjhibbits return 0; 177239027Sjhibbits 178239027Sjhibbits level = (level - NVIDIA_BRIGHT_MIN) / NVIDIA_BRIGHT_SCALE; 179239027Sjhibbits 180239027Sjhibbits return (level); 181239027Sjhibbits} 182239027Sjhibbits 183239027Sjhibbitsstatic int 184239027Sjhibbitsnvbl_sysctl(SYSCTL_HANDLER_ARGS) 185239027Sjhibbits{ 186239027Sjhibbits struct nvbl_softc *sc; 187239027Sjhibbits int newlevel, error; 188239027Sjhibbits 189239027Sjhibbits sc = arg1; 190239027Sjhibbits 191239027Sjhibbits newlevel = nvbl_getlevel(sc); 192239027Sjhibbits 193239027Sjhibbits error = sysctl_handle_int(oidp, &newlevel, 0, req); 194239027Sjhibbits 195239027Sjhibbits if (error || !req->newptr) 196239027Sjhibbits return (error); 197239027Sjhibbits 198239027Sjhibbits return (nvbl_setlevel(sc, newlevel)); 199239027Sjhibbits} 200