1 /*- 2 * Copyright (c) 2012 Justin Hibbits 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/bus.h> 32 #include <sys/systm.h> 33 #include <sys/module.h> 34 #include <sys/kernel.h> 35 #include <sys/rman.h> 36 #include <sys/sysctl.h> 37 38 #include <machine/bus.h> 39 40 #include <dev/ofw/openfirm.h> 41 42 /* From the xf86-video-ati driver's radeon_reg.h */ 43 #define RADEON_LVDS_GEN_CNTL 0x02d0 44 #define RADEON_LVDS_ON (1 << 0) 45 #define RADEON_LVDS_DISPLAY_DIS (1 << 1) 46 #define RADEON_LVDS_PANEL_TYPE (1 << 2) 47 #define RADEON_LVDS_PANEL_FORMAT (1 << 3) 48 #define RADEON_LVDS_RST_FM (1 << 6) 49 #define RADEON_LVDS_EN (1 << 7) 50 #define RADEON_LVDS_BL_MOD_LEVEL_SHIFT 8 51 #define RADEON_LVDS_BL_MOD_LEVEL_MASK (0xff << 8) 52 #define RADEON_LVDS_BL_MOD_EN (1 << 16) 53 #define RADEON_LVDS_DIGON (1 << 18) 54 #define RADEON_LVDS_BLON (1 << 19) 55 #define RADEON_LVDS_PLL_CNTL 0x02d4 56 #define RADEON_LVDS_PLL_EN (1 << 16) 57 #define RADEON_LVDS_PLL_RESET (1 << 17) 58 #define RADEON_PIXCLKS_CNTL 0x002d 59 #define RADEON_PIXCLK_LVDS_ALWAYS_ONb (1 << 14) 60 61 struct atibl_softc { 62 struct resource *sc_memr; 63 int sc_level; 64 }; 65 66 static void atibl_identify(driver_t *driver, device_t parent); 67 static int atibl_probe(device_t dev); 68 static int atibl_attach(device_t dev); 69 static int atibl_setlevel(struct atibl_softc *sc, int newlevel); 70 static int atibl_getlevel(struct atibl_softc *sc); 71 static int atibl_resume(device_t dev); 72 static int atibl_suspend(device_t dev); 73 static int atibl_sysctl(SYSCTL_HANDLER_ARGS); 74 75 static device_method_t atibl_methods[] = { 76 /* Device interface */ 77 DEVMETHOD(device_identify, atibl_identify), 78 DEVMETHOD(device_probe, atibl_probe), 79 DEVMETHOD(device_attach, atibl_attach), 80 DEVMETHOD(device_suspend, atibl_suspend), 81 DEVMETHOD(device_resume, atibl_resume), 82 {0, 0}, 83 }; 84 85 static driver_t atibl_driver = { 86 "backlight", 87 atibl_methods, 88 sizeof(struct atibl_softc) 89 }; 90 91 static devclass_t atibl_devclass; 92 93 DRIVER_MODULE(atibl, vgapci, atibl_driver, atibl_devclass, 0, 0); 94 95 static void 96 atibl_identify(driver_t *driver, device_t parent) 97 { 98 if (OF_finddevice("mac-io/backlight") == -1) 99 return; 100 if (device_find_child(parent, "backlight", -1) == NULL) 101 device_add_child(parent, "backlight", -1); 102 } 103 104 static int 105 atibl_probe(device_t dev) 106 { 107 char control[8]; 108 phandle_t handle; 109 110 handle = OF_finddevice("mac-io/backlight"); 111 112 if (handle == -1) 113 return (ENXIO); 114 115 if (OF_getprop(handle, "backlight-control", &control, sizeof(control)) < 0) 116 return (ENXIO); 117 118 if (strcmp(control, "ati") != 0) 119 return (ENXIO); 120 121 device_set_desc(dev, "PowerBook backlight for ATI graphics"); 122 123 return (0); 124 } 125 126 static int 127 atibl_attach(device_t dev) 128 { 129 struct atibl_softc *sc; 130 struct sysctl_ctx_list *ctx; 131 struct sysctl_oid *tree; 132 int rid; 133 134 sc = device_get_softc(dev); 135 136 rid = 0x18; /* BAR[2], for the MMIO register */ 137 sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 138 RF_ACTIVE | RF_SHAREABLE); 139 if (sc->sc_memr == NULL) { 140 device_printf(dev, "Could not alloc mem resource!\n"); 141 return (ENXIO); 142 } 143 144 ctx = device_get_sysctl_ctx(dev); 145 tree = device_get_sysctl_tree(dev); 146 147 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 148 "level", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 149 atibl_sysctl, "I", "Backlight level (0-100)"); 150 151 return (0); 152 } 153 154 static int 155 atibl_setlevel(struct atibl_softc *sc, int newlevel) 156 { 157 uint32_t lvds_gen_cntl; 158 uint32_t lvds_pll_cntl; 159 uint32_t pixclks_cntl; 160 161 if (newlevel > 100) 162 newlevel = 100; 163 164 if (newlevel < 0) 165 newlevel = 0; 166 167 lvds_gen_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL); 168 169 if (newlevel > 0) { 170 newlevel = (newlevel * 5) / 2 + 5; 171 lvds_pll_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL); 172 lvds_pll_cntl |= RADEON_LVDS_PLL_EN; 173 bus_write_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); 174 lvds_pll_cntl &= ~RADEON_LVDS_PLL_RESET; 175 bus_write_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); 176 177 lvds_gen_cntl &= ~(RADEON_LVDS_DISPLAY_DIS | 178 RADEON_LVDS_BL_MOD_LEVEL_MASK); 179 lvds_gen_cntl |= RADEON_LVDS_ON | RADEON_LVDS_EN | 180 RADEON_LVDS_DIGON | RADEON_LVDS_BLON; 181 lvds_gen_cntl |= (newlevel << RADEON_LVDS_BL_MOD_LEVEL_SHIFT) & 182 RADEON_LVDS_BL_MOD_LEVEL_MASK; 183 lvds_gen_cntl |= RADEON_LVDS_BL_MOD_EN; 184 DELAY(2000); 185 bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); 186 } else { 187 pixclks_cntl = bus_read_4(sc->sc_memr, RADEON_PIXCLKS_CNTL); 188 bus_write_4(sc->sc_memr, RADEON_PIXCLKS_CNTL, 189 pixclks_cntl & ~RADEON_PIXCLK_LVDS_ALWAYS_ONb); 190 lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS; 191 lvds_gen_cntl &= RADEON_LVDS_BL_MOD_EN; 192 bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); 193 lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_EN); 194 DELAY(2000); 195 bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); 196 197 bus_write_4(sc->sc_memr, RADEON_PIXCLKS_CNTL, pixclks_cntl); 198 } 199 200 return (0); 201 } 202 203 static int 204 atibl_getlevel(struct atibl_softc *sc) 205 { 206 uint32_t lvds_gen_cntl; 207 int level; 208 209 lvds_gen_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL); 210 211 level = ((lvds_gen_cntl & RADEON_LVDS_BL_MOD_LEVEL_MASK) >> 212 RADEON_LVDS_BL_MOD_LEVEL_SHIFT); 213 if (level != 0) 214 level = ((level - 5) * 2) / 5; 215 216 return (level); 217 } 218 219 static int 220 atibl_suspend(device_t dev) 221 { 222 struct atibl_softc *sc; 223 224 sc = device_get_softc(dev); 225 226 sc->sc_level = atibl_getlevel(sc); 227 atibl_setlevel(sc, 0); 228 229 return (0); 230 } 231 232 static int 233 atibl_resume(device_t dev) 234 { 235 struct atibl_softc *sc; 236 237 sc = device_get_softc(dev); 238 239 atibl_setlevel(sc, sc->sc_level); 240 241 return (0); 242 } 243 244 static int 245 atibl_sysctl(SYSCTL_HANDLER_ARGS) 246 { 247 struct atibl_softc *sc; 248 int newlevel, error; 249 250 sc = arg1; 251 252 newlevel = atibl_getlevel(sc); 253 254 error = sysctl_handle_int(oidp, &newlevel, 0, req); 255 256 if (error || !req->newptr) 257 return (error); 258 259 return (atibl_setlevel(sc, newlevel)); 260 } 261