nvbl.c revision 239027
1169691Skan/*- 2169691Skan * Copyright (c) 2012 Justin Hibbits 3169691Skan * All rights reserved. 4169691Skan * 5169691Skan * Redistribution and use in source and binary forms, with or without 6169691Skan * modification, are permitted provided that the following conditions 7169691Skan * are met: 8169691Skan * 1. Redistributions of source code must retain the above copyright 9169691Skan * notice, this list of conditions and the following disclaimer. 10169691Skan * 2. Redistributions in binary form must reproduce the above copyright 11169691Skan * notice, this list of conditions and the following disclaimer in the 12169691Skan * documentation and/or other materials provided with the distribution. 13169691Skan * 14169691Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15169691Skan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16169691Skan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17169691Skan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18169691Skan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19169691Skan * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20169691Skan * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21169691Skan * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22169691Skan * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23169691Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24169691Skan * SUCH DAMAGE. 25169691Skan */ 26169691Skan 27169691Skan#include <sys/cdefs.h> 28169691Skan__FBSDID("$FreeBSD: head/sys/powerpc/powermac/nvbl.c 239027 2012-08-04 03:05:01Z jhibbits $"); 29169691Skan 30169691Skan#include <sys/param.h> 31169691Skan#include <sys/bus.h> 32169691Skan#include <sys/systm.h> 33169691Skan#include <sys/module.h> 34169691Skan#include <sys/kernel.h> 35169691Skan#include <sys/rman.h> 36169691Skan#include <sys/sysctl.h> 37169691Skan 38169691Skan#include <machine/bus.h> 39169691Skan 40169691Skan#include <dev/ofw/openfirm.h> 41169691Skan 42169691Skan#define NVIDIA_BRIGHT_MIN (0x0ec) 43169691Skan#define NVIDIA_BRIGHT_MAX (0x538) 44169691Skan#define NVIDIA_BRIGHT_SCALE ((NVIDIA_BRIGHT_MAX - NVIDIA_BRIGHT_MIN)/100) 45169691Skan/* nVidia's MMIO registers are at PCI BAR[0] */ 46169691Skan#define NVIDIA_MMIO_PMC (0x0) 47169691Skan#define NVIDIA_PMC_OFF (NVIDIA_MMIO_PMC + 0x10f0) 48169691Skan#define NVIDIA_PMC_BL_SHIFT (16) 49169691Skan#define NVIDIA_PMC_BL_EN (1 << 31) 50169691Skan 51169691Skan 52169691Skanstruct nvbl_softc { 53169691Skan device_t dev; 54169691Skan struct resource *sc_memr; 55169691Skan}; 56169691Skan 57169691Skanstatic void nvbl_identify(driver_t *driver, device_t parent); 58169691Skanstatic int nvbl_probe(device_t dev); 59169691Skanstatic int nvbl_attach(device_t dev); 60169691Skanstatic int nvbl_setlevel(struct nvbl_softc *sc, int newlevel); 61169691Skanstatic int nvbl_getlevel(struct nvbl_softc *sc); 62169691Skanstatic int nvbl_sysctl(SYSCTL_HANDLER_ARGS); 63169691Skan 64169691Skanstatic device_method_t nvbl_methods[] = { 65169691Skan /* Device interface */ 66169691Skan DEVMETHOD(device_identify, nvbl_identify), 67169691Skan DEVMETHOD(device_probe, nvbl_probe), 68169691Skan DEVMETHOD(device_attach, nvbl_attach), 69169691Skan {0, 0}, 70169691Skan}; 71169691Skan 72169691Skanstatic driver_t nvbl_driver = { 73169691Skan "backlight", 74169691Skan nvbl_methods, 75169691Skan sizeof(struct nvbl_softc) 76169691Skan}; 77169691Skan 78169691Skanstatic devclass_t nvbl_devclass; 79169691Skan 80169691SkanDRIVER_MODULE(nvbl, vgapci, nvbl_driver, nvbl_devclass, 0, 0); 81169691Skan 82169691Skanstatic void 83169691Skannvbl_identify(driver_t *driver, device_t parent) 84169691Skan{ 85169691Skan if (device_find_child(parent, "backlight", -1) == NULL) 86169691Skan device_add_child(parent, "backlight", -1); 87169691Skan} 88169691Skan 89169691Skanstatic int 90169691Skannvbl_probe(device_t dev) 91169691Skan{ 92169691Skan char control[8]; 93169691Skan phandle_t handle; 94169691Skan 95169691Skan handle = OF_finddevice("mac-io/backlight"); 96169691Skan 97169691Skan if (handle <= 0) 98169691Skan return (ENXIO); 99169691Skan 100169691Skan if (OF_getprop(handle, "backlight-control", &control, sizeof(control)) < 0) 101169691Skan return (ENXIO); 102169691Skan 103169691Skan if (strcmp(control, "mnca") != 0) 104169691Skan return (ENXIO); 105169691Skan 106169691Skan device_set_desc(dev, "PowerBook backlight for nVidia graphics"); 107169691Skan 108169691Skan return (0); 109169691Skan} 110169691Skan 111169691Skanstatic int 112169691Skannvbl_attach(device_t dev) 113169691Skan{ 114169691Skan struct nvbl_softc *sc; 115169691Skan struct sysctl_ctx_list *ctx; 116169691Skan struct sysctl_oid *tree; 117169691Skan int rid; 118169691Skan 119169691Skan sc = device_get_softc(dev); 120169691Skan 121169691Skan rid = 0x10; /* BAR[0], for the MMIO register */ 122169691Skan sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 123169691Skan RF_ACTIVE | RF_SHAREABLE); 124169691Skan if (sc->sc_memr == NULL) { 125169691Skan device_printf(dev, "Could not alloc mem resource!\n"); 126169691Skan return (ENXIO); 127169691Skan } 128169691Skan 129169691Skan /* Turn on big-endian mode */ 130169691Skan if (!(bus_read_stream_4(sc->sc_memr, NVIDIA_MMIO_PMC + 4) & 0x01000001)) { 131169691Skan bus_write_stream_4(sc->sc_memr, NVIDIA_MMIO_PMC + 4, 0x01000001); 132169691Skan mb(); 133169691Skan } 134169691Skan 135169691Skan ctx = device_get_sysctl_ctx(dev); 136169691Skan tree = device_get_sysctl_tree(dev); 137169691Skan 138169691Skan SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 139169691Skan "level", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 140169691Skan nvbl_sysctl, "I", "Backlight level (0-100)"); 141169691Skan 142169691Skan return (0); 143169691Skan} 144169691Skan 145169691Skanstatic int 146169691Skannvbl_setlevel(struct nvbl_softc *sc, int newlevel) 147169691Skan{ 148169691Skan uint32_t pmc_reg; 149169691Skan 150169691Skan if (newlevel > 100) 151169691Skan newlevel = 100; 152169691Skan 153169691Skan if (newlevel < 0) 154169691Skan newlevel = 0; 155169691Skan 156169691Skan if (newlevel > 0) 157169691Skan newlevel = (newlevel * NVIDIA_BRIGHT_SCALE) + NVIDIA_BRIGHT_MIN; 158169691Skan 159169691Skan pmc_reg = bus_read_stream_4(sc->sc_memr, NVIDIA_PMC_OFF) & 0xffff; 160169691Skan pmc_reg |= NVIDIA_PMC_BL_EN | (newlevel << NVIDIA_PMC_BL_SHIFT); 161169691Skan bus_write_stream_4(sc->sc_memr, NVIDIA_PMC_OFF, pmc_reg); 162169691Skan 163169691Skan return (0); 164169691Skan} 165169691Skan 166169691Skanstatic int 167169691Skannvbl_getlevel(struct nvbl_softc *sc) 168169691Skan{ 169169691Skan uint16_t level; 170169691Skan 171169691Skan level = bus_read_stream_2(sc->sc_memr, NVIDIA_PMC_OFF) & 0x7fff; 172169691Skan 173169691Skan if (level < NVIDIA_BRIGHT_MIN) 174169691Skan return 0; 175169691Skan 176169691Skan level = (level - NVIDIA_BRIGHT_MIN) / NVIDIA_BRIGHT_SCALE; 177169691Skan 178169691Skan return (level); 179169691Skan} 180169691Skan 181169691Skanstatic int 182169691Skannvbl_sysctl(SYSCTL_HANDLER_ARGS) 183169691Skan{ 184169691Skan struct nvbl_softc *sc; 185169691Skan int newlevel, error; 186169691Skan 187169691Skan sc = arg1; 188169691Skan 189169691Skan newlevel = nvbl_getlevel(sc); 190169691Skan 191169691Skan error = sysctl_handle_int(oidp, &newlevel, 0, req); 192169691Skan 193169691Skan if (error || !req->newptr) 194169691Skan return (error); 195169691Skan 196169691Skan return (nvbl_setlevel(sc, newlevel)); 197169691Skan} 198169691Skan