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#include <dev/pci/pcivar.h> 42 43#ifndef PCI_VENDOR_ID_ATI 44#define PCI_VENDOR_ID_ATI 0x1002 45#endif 46 47/* From the xf86-video-ati driver's radeon_reg.h */ 48#define RADEON_LVDS_GEN_CNTL 0x02d0 49#define RADEON_LVDS_ON (1 << 0) 50#define RADEON_LVDS_DISPLAY_DIS (1 << 1) 51#define RADEON_LVDS_PANEL_TYPE (1 << 2) 52#define RADEON_LVDS_PANEL_FORMAT (1 << 3) 53#define RADEON_LVDS_RST_FM (1 << 6) 54#define RADEON_LVDS_EN (1 << 7) 55#define RADEON_LVDS_BL_MOD_LEVEL_SHIFT 8 56#define RADEON_LVDS_BL_MOD_LEVEL_MASK (0xff << 8) 57#define RADEON_LVDS_BL_MOD_EN (1 << 16) 58#define RADEON_LVDS_DIGON (1 << 18) 59#define RADEON_LVDS_BLON (1 << 19) 60#define RADEON_LVDS_PLL_CNTL 0x02d4 61#define RADEON_LVDS_PLL_EN (1 << 16) 62#define RADEON_LVDS_PLL_RESET (1 << 17) 63#define RADEON_PIXCLKS_CNTL 0x002d 64#define RADEON_PIXCLK_LVDS_ALWAYS_ONb (1 << 14) 65#define RADEON_DISP_PWR_MAN 0x0d08 66#define RADEON_AUTO_PWRUP_EN (1 << 26) 67#define RADEON_CLOCK_CNTL_DATA 0x000c 68#define RADEON_CLOCK_CNTL_INDEX 0x0008 69#define RADEON_PLL_WR_EN (1 << 7) 70#define RADEON_CRTC_GEN_CNTL 0x0050 71 72struct atibl_softc { 73 struct resource *sc_memr; 74 int sc_level; 75}; 76 77static void atibl_identify(driver_t *driver, device_t parent); 78static int atibl_probe(device_t dev); 79static int atibl_attach(device_t dev); 80static int atibl_setlevel(struct atibl_softc *sc, int newlevel); 81static int atibl_getlevel(struct atibl_softc *sc); 82static int atibl_resume(device_t dev); 83static int atibl_suspend(device_t dev); 84static int atibl_sysctl(SYSCTL_HANDLER_ARGS); 85 86static device_method_t atibl_methods[] = { 87 /* Device interface */ 88 DEVMETHOD(device_identify, atibl_identify), 89 DEVMETHOD(device_probe, atibl_probe), 90 DEVMETHOD(device_attach, atibl_attach), 91 DEVMETHOD(device_suspend, atibl_suspend), 92 DEVMETHOD(device_resume, atibl_resume), 93 {0, 0}, 94}; 95 96static driver_t atibl_driver = { 97 "backlight", 98 atibl_methods, 99 sizeof(struct atibl_softc) 100}; 101 102static devclass_t atibl_devclass; 103 104DRIVER_MODULE(atibl, vgapci, atibl_driver, atibl_devclass, 0, 0); 105 106static void 107atibl_identify(driver_t *driver, device_t parent) 108{ 109 if (OF_finddevice("mac-io/backlight") == -1) 110 return; 111 if (device_find_child(parent, "backlight", -1) == NULL) 112 device_add_child(parent, "backlight", -1); 113} 114 115static int 116atibl_probe(device_t dev) 117{ 118 char control[8]; 119 phandle_t handle; 120 121 handle = OF_finddevice("mac-io/backlight"); 122 123 if (handle == -1) 124 return (ENXIO); 125 126 if (OF_getprop(handle, "backlight-control", &control, sizeof(control)) < 0) 127 return (ENXIO); 128 129 if (strcmp(control, "ati") != 0 && 130 (strcmp(control, "mnca") != 0 || 131 pci_get_vendor(device_get_parent(dev)) != 0x1002)) 132 return (ENXIO); 133 134 device_set_desc(dev, "PowerBook backlight for ATI graphics"); 135 136 return (0); 137} 138 139static int 140atibl_attach(device_t dev) 141{ 142 struct atibl_softc *sc; 143 struct sysctl_ctx_list *ctx; 144 struct sysctl_oid *tree; 145 int rid; 146 147 sc = device_get_softc(dev); 148 149 rid = 0x18; /* BAR[2], for the MMIO register */ 150 sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 151 RF_ACTIVE | RF_SHAREABLE); 152 if (sc->sc_memr == NULL) { 153 device_printf(dev, "Could not alloc mem resource!\n"); 154 return (ENXIO); 155 } 156 157 ctx = device_get_sysctl_ctx(dev); 158 tree = device_get_sysctl_tree(dev); 159 160 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 161 "level", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 162 atibl_sysctl, "I", "Backlight level (0-100)"); 163 164 return (0); 165} 166 167static uint32_t __inline 168atibl_pll_rreg(struct atibl_softc *sc, uint32_t reg) 169{ 170 uint32_t data, save, tmp; 171 172 bus_write_1(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, (reg & 0x3f)); 173 (void)bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA); 174 (void)bus_read_4(sc->sc_memr, RADEON_CRTC_GEN_CNTL); 175 176 data = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA); 177 178 /* Only necessary on R300, but won't hurt others. */ 179 save = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX); 180 tmp = save & (~0x3f | RADEON_PLL_WR_EN); 181 bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, tmp); 182 tmp = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA); 183 bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, save); 184 185 return data; 186} 187 188static void __inline 189atibl_pll_wreg(struct atibl_softc *sc, uint32_t reg, uint32_t val) 190{ 191 uint32_t save, tmp; 192 193 bus_write_1(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, 194 ((reg & 0x3f) | RADEON_PLL_WR_EN)); 195 (void)bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA); 196 (void)bus_read_4(sc->sc_memr, RADEON_CRTC_GEN_CNTL); 197 198 bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA, val); 199 DELAY(5000); 200 201 /* Only necessary on R300, but won't hurt others. */ 202 save = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX); 203 tmp = save & (~0x3f | RADEON_PLL_WR_EN); 204 bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, tmp); 205 tmp = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA); 206 bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, save); 207} 208 209static int 210atibl_setlevel(struct atibl_softc *sc, int newlevel) 211{ 212 uint32_t lvds_gen_cntl; 213 uint32_t lvds_pll_cntl; 214 uint32_t pixclks_cntl; 215 uint32_t disp_pwr_reg; 216 217 if (newlevel > 100) 218 newlevel = 100; 219 220 if (newlevel < 0) 221 newlevel = 0; 222 223 lvds_gen_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL); 224 225 if (newlevel > 0) { 226 newlevel = (newlevel * 5) / 2 + 5; 227 disp_pwr_reg = bus_read_4(sc->sc_memr, RADEON_DISP_PWR_MAN); 228 disp_pwr_reg |= RADEON_AUTO_PWRUP_EN; 229 bus_write_4(sc->sc_memr, RADEON_DISP_PWR_MAN, disp_pwr_reg); 230 lvds_pll_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL); 231 lvds_pll_cntl |= RADEON_LVDS_PLL_EN; 232 bus_write_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); 233 lvds_pll_cntl &= ~RADEON_LVDS_PLL_RESET; 234 bus_write_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); 235 DELAY(1000); 236 237 lvds_gen_cntl &= ~(RADEON_LVDS_DISPLAY_DIS | 238 RADEON_LVDS_BL_MOD_LEVEL_MASK); 239 lvds_gen_cntl |= RADEON_LVDS_ON | RADEON_LVDS_EN | 240 RADEON_LVDS_DIGON | RADEON_LVDS_BLON; 241 lvds_gen_cntl |= (newlevel << RADEON_LVDS_BL_MOD_LEVEL_SHIFT) & 242 RADEON_LVDS_BL_MOD_LEVEL_MASK; 243 lvds_gen_cntl |= RADEON_LVDS_BL_MOD_EN; 244 DELAY(200000); 245 bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); 246 } else { 247 pixclks_cntl = atibl_pll_rreg(sc, RADEON_PIXCLKS_CNTL); 248 atibl_pll_wreg(sc, RADEON_PIXCLKS_CNTL, 249 pixclks_cntl & ~RADEON_PIXCLK_LVDS_ALWAYS_ONb); 250 lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS; 251 lvds_gen_cntl &= ~(RADEON_LVDS_BL_MOD_EN | RADEON_LVDS_BL_MOD_LEVEL_MASK); 252 bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); 253 lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_EN); 254 DELAY(200000); 255 bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); 256 257 atibl_pll_wreg(sc, RADEON_PIXCLKS_CNTL, pixclks_cntl); 258 DELAY(200000); 259 } 260 261 return (0); 262} 263 264static int 265atibl_getlevel(struct atibl_softc *sc) 266{ 267 uint32_t lvds_gen_cntl; 268 int level; 269 270 lvds_gen_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL); 271 272 level = ((lvds_gen_cntl & RADEON_LVDS_BL_MOD_LEVEL_MASK) >> 273 RADEON_LVDS_BL_MOD_LEVEL_SHIFT); 274 if (level != 0) 275 level = ((level - 5) * 2) / 5; 276 277 return (level); 278} 279 280static int 281atibl_suspend(device_t dev) 282{ 283 struct atibl_softc *sc; 284 285 sc = device_get_softc(dev); 286 287 sc->sc_level = atibl_getlevel(sc); 288 atibl_setlevel(sc, 0); 289 290 return (0); 291} 292 293static int 294atibl_resume(device_t dev) 295{ 296 struct atibl_softc *sc; 297 298 sc = device_get_softc(dev); 299 300 atibl_setlevel(sc, sc->sc_level); 301 302 return (0); 303} 304 305static int 306atibl_sysctl(SYSCTL_HANDLER_ARGS) 307{ 308 struct atibl_softc *sc; 309 int newlevel, error; 310 311 sc = arg1; 312 313 newlevel = atibl_getlevel(sc); 314 315 error = sysctl_handle_int(oidp, &newlevel, 0, req); 316 317 if (error || !req->newptr) 318 return (error); 319 320 return (atibl_setlevel(sc, newlevel)); 321} 322