lpc_fb.c revision 303975
1/*- 2 * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org> 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 AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, 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: releng/11.0/sys/arm/lpc/lpc_fb.c 261410 2014-02-02 19:17:28Z ian $"); 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/bio.h> 33#include <sys/bus.h> 34#include <sys/conf.h> 35#include <sys/endian.h> 36#include <sys/kernel.h> 37#include <sys/kthread.h> 38#include <sys/lock.h> 39#include <sys/malloc.h> 40#include <sys/module.h> 41#include <sys/mutex.h> 42#include <sys/queue.h> 43#include <sys/resource.h> 44#include <sys/rman.h> 45#include <sys/time.h> 46#include <sys/timetc.h> 47#include <sys/watchdog.h> 48 49#include <sys/kdb.h> 50 51#include <machine/bus.h> 52#include <machine/cpu.h> 53#include <machine/cpufunc.h> 54#include <machine/resource.h> 55#include <machine/intr.h> 56 57#include <dev/fdt/fdt_common.h> 58#include <dev/ofw/ofw_bus.h> 59#include <dev/ofw/ofw_bus_subr.h> 60 61#include <arm/lpc/lpcreg.h> 62#include <arm/lpc/lpcvar.h> 63 64 65struct lpc_fb_dmamap_arg { 66 bus_addr_t lf_dma_busaddr; 67}; 68 69struct lpc_lcd_config { 70 int lc_xres; 71 int lc_yres; 72 int lc_bpp; 73 uint32_t lc_pixelclock; 74 int lc_left_margin; 75 int lc_right_margin; 76 int lc_upper_margin; 77 int lc_lower_margin; 78 int lc_hsync_len; 79 int lc_vsync_len; 80}; 81 82struct lpc_fb_softc { 83 device_t lf_dev; 84 struct cdev * lf_cdev; 85 struct mtx lf_mtx; 86 struct resource * lf_mem_res; 87 struct resource * lf_irq_res; 88 bus_space_tag_t lf_bst; 89 bus_space_handle_t lf_bsh; 90 void * lf_intrhand; 91 bus_dma_tag_t lf_dma_tag; 92 bus_dmamap_t lf_dma_map; 93 void * lf_buffer; 94 bus_addr_t lf_buffer_phys; 95 bus_size_t lf_buffer_size; 96 struct lpc_lcd_config lf_lcd_config; 97 int lf_initialized; 98 int lf_opened; 99}; 100 101extern void ssd1289_configure(void); 102 103#define lpc_fb_lock(_sc) mtx_lock(&(_sc)->lf_mtx) 104#define lpc_fb_unlock(_sc) mtx_unlock(&(_sc)->lf_mtx) 105#define lpc_fb_lock_assert(sc) mtx_assert(&(_sc)->lf_mtx, MA_OWNED) 106 107#define lpc_fb_read_4(_sc, _reg) \ 108 bus_space_read_4((_sc)->lf_bst, (_sc)->lf_bsh, (_reg)) 109#define lpc_fb_write_4(_sc, _reg, _val) \ 110 bus_space_write_4((_sc)->lf_bst, (_sc)->lf_bsh, (_reg), (_val)) 111 112 113 114static int lpc_fb_probe(device_t); 115static int lpc_fb_attach(device_t); 116static void lpc_fb_intr(void *); 117static void lpc_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err); 118 119static int lpc_fb_fdt_read(phandle_t, const char *, uint32_t *); 120static int lpc_fb_read_lcd_config(phandle_t, struct lpc_lcd_config *); 121 122static int lpc_fb_open(struct cdev *, int, int, struct thread *); 123static int lpc_fb_close(struct cdev *, int, int, struct thread *); 124static int lpc_fb_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *); 125static int lpc_fb_mmap(struct cdev *, vm_ooffset_t, vm_paddr_t *, int, vm_memattr_t *); 126 127static void lpc_fb_blank(struct lpc_fb_softc *); 128 129static struct cdevsw lpc_fb_cdevsw = { 130 .d_open = lpc_fb_open, 131 .d_close = lpc_fb_close, 132 .d_ioctl = lpc_fb_ioctl, 133 .d_mmap = lpc_fb_mmap, 134 .d_name = "lpcfb", 135 .d_version = D_VERSION, 136}; 137 138static int 139lpc_fb_probe(device_t dev) 140{ 141 142 if (!ofw_bus_status_okay(dev)) 143 return (ENXIO); 144 145 if (!ofw_bus_is_compatible(dev, "lpc,fb")) 146 return (ENXIO); 147 148 device_set_desc(dev, "LPC32x0 framebuffer device"); 149 return (BUS_PROBE_DEFAULT); 150} 151 152static int 153lpc_fb_attach(device_t dev) 154{ 155 struct lpc_fb_softc *sc = device_get_softc(dev); 156 struct lpc_fb_dmamap_arg ctx; 157 phandle_t node; 158 int mode, rid, err = 0; 159 160 sc->lf_dev = dev; 161 mtx_init(&sc->lf_mtx, "lpcfb", "fb", MTX_DEF); 162 163 rid = 0; 164 sc->lf_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 165 RF_ACTIVE); 166 if (!sc->lf_mem_res) { 167 device_printf(dev, "cannot allocate memory window\n"); 168 return (ENXIO); 169 } 170 171 sc->lf_bst = rman_get_bustag(sc->lf_mem_res); 172 sc->lf_bsh = rman_get_bushandle(sc->lf_mem_res); 173 174 rid = 0; 175 sc->lf_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 176 RF_ACTIVE); 177 if (!sc->lf_irq_res) { 178 device_printf(dev, "cannot allocate interrupt\n"); 179 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lf_mem_res); 180 return (ENXIO); 181 } 182 183 if (bus_setup_intr(dev, sc->lf_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 184 NULL, lpc_fb_intr, sc, &sc->lf_intrhand)) 185 { 186 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lf_mem_res); 187 bus_release_resource(dev, SYS_RES_IRQ, 1, sc->lf_irq_res); 188 device_printf(dev, "cannot setup interrupt handler\n"); 189 return (ENXIO); 190 } 191 192 node = ofw_bus_get_node(dev); 193 194 err = lpc_fb_read_lcd_config(node, &sc->lf_lcd_config); 195 if (err) { 196 device_printf(dev, "cannot read LCD configuration\n"); 197 goto fail; 198 } 199 200 sc->lf_buffer_size = sc->lf_lcd_config.lc_xres * 201 sc->lf_lcd_config.lc_yres * 202 (sc->lf_lcd_config.lc_bpp == 24 ? 3 : 2); 203 204 device_printf(dev, "%dx%d LCD, %d bits per pixel, %dkHz pixel clock\n", 205 sc->lf_lcd_config.lc_xres, sc->lf_lcd_config.lc_yres, 206 sc->lf_lcd_config.lc_bpp, sc->lf_lcd_config.lc_pixelclock / 1000); 207 208 err = bus_dma_tag_create( 209 bus_get_dma_tag(sc->lf_dev), 210 4, 0, /* alignment, boundary */ 211 BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 212 BUS_SPACE_MAXADDR, /* highaddr */ 213 NULL, NULL, /* filter, filterarg */ 214 sc->lf_buffer_size, 1, /* maxsize, nsegments */ 215 sc->lf_buffer_size, 0, /* maxsegsize, flags */ 216 NULL, NULL, /* lockfunc, lockarg */ 217 &sc->lf_dma_tag); 218 219 err = bus_dmamem_alloc(sc->lf_dma_tag, (void **)&sc->lf_buffer, 220 0, &sc->lf_dma_map); 221 if (err) { 222 device_printf(dev, "cannot allocate framebuffer\n"); 223 goto fail; 224 } 225 226 err = bus_dmamap_load(sc->lf_dma_tag, sc->lf_dma_map, sc->lf_buffer, 227 sc->lf_buffer_size, lpc_fb_dmamap_cb, &ctx, BUS_DMA_NOWAIT); 228 if (err) { 229 device_printf(dev, "cannot load DMA map\n"); 230 goto fail; 231 } 232 233 switch (sc->lf_lcd_config.lc_bpp) { 234 case 12: 235 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_12; 236 break; 237 case 15: 238 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_15; 239 break; 240 case 16: 241 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_16; 242 break; 243 case 24: 244 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_24; 245 break; 246 default: 247 panic("unsupported bpp"); 248 } 249 250 lpc_pwr_write(sc->lf_dev, LPC_CLKPWR_LCDCLK_CTRL, 251 LPC_CLKPWR_LCDCLK_CTRL_MODE(mode) | 252 LPC_CLKPWR_LCDCLK_CTRL_HCLKEN); 253 254 sc->lf_buffer_phys = ctx.lf_dma_busaddr; 255 sc->lf_cdev = make_dev(&lpc_fb_cdevsw, 0, UID_ROOT, GID_WHEEL, 256 0600, "lpcfb"); 257 258 sc->lf_cdev->si_drv1 = sc; 259 260 return (0); 261fail: 262 return (ENXIO); 263} 264 265static void 266lpc_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) 267{ 268 struct lpc_fb_dmamap_arg *ctx; 269 270 if (err) 271 return; 272 273 ctx = (struct lpc_fb_dmamap_arg *)arg; 274 ctx->lf_dma_busaddr = segs[0].ds_addr; 275} 276 277static void 278lpc_fb_intr(void *arg) 279{ 280} 281 282static int 283lpc_fb_fdt_read(phandle_t node, const char *name, uint32_t *ret) 284{ 285 if (OF_getprop(node, name, ret, sizeof(uint32_t)) <= 0) 286 return (ENOENT); 287 288 *ret = fdt32_to_cpu(*ret); 289 return (0); 290} 291 292static int 293lpc_fb_read_lcd_config(phandle_t node, struct lpc_lcd_config *cfg) 294{ 295 if (lpc_fb_fdt_read(node, "horizontal-resolution", &cfg->lc_xres)) 296 return (ENXIO); 297 298 if (lpc_fb_fdt_read(node, "vertical-resolution", &cfg->lc_yres)) 299 return (ENXIO); 300 301 if (lpc_fb_fdt_read(node, "bits-per-pixel", &cfg->lc_bpp)) 302 return (ENXIO); 303 304 if (lpc_fb_fdt_read(node, "pixel-clock", &cfg->lc_pixelclock)) 305 return (ENXIO); 306 307 if (lpc_fb_fdt_read(node, "left-margin", &cfg->lc_left_margin)) 308 return (ENXIO); 309 310 if (lpc_fb_fdt_read(node, "right-margin", &cfg->lc_right_margin)) 311 return (ENXIO); 312 313 if (lpc_fb_fdt_read(node, "upper-margin", &cfg->lc_upper_margin)) 314 return (ENXIO); 315 316 if (lpc_fb_fdt_read(node, "lower-margin", &cfg->lc_lower_margin)) 317 return (ENXIO); 318 319 if (lpc_fb_fdt_read(node, "hsync-len", &cfg->lc_hsync_len)) 320 return (ENXIO); 321 322 if (lpc_fb_fdt_read(node, "vsync-len", &cfg->lc_vsync_len)) 323 return (ENXIO); 324 325 return (0); 326} 327 328static void 329lpc_fb_setup(struct lpc_fb_softc *sc) 330{ 331 struct lpc_lcd_config *cfg = &sc->lf_lcd_config; 332 uint32_t bpp; 333 334 lpc_fb_write_4(sc, LPC_LCD_TIMH, 335 LPC_LCD_TIMH_PPL(cfg->lc_xres) | 336 LPC_LCD_TIMH_HSW(cfg->lc_hsync_len - 1) | 337 LPC_LCD_TIMH_HFP(cfg->lc_right_margin - 1) | 338 LPC_LCD_TIMH_HBP(cfg->lc_left_margin - 1)); 339 340 lpc_fb_write_4(sc, LPC_LCD_TIMV, 341 LPC_LCD_TIMV_LPP(cfg->lc_yres - 1) | 342 LPC_LCD_TIMV_VSW(cfg->lc_vsync_len - 1) | 343 LPC_LCD_TIMV_VFP(cfg->lc_lower_margin) | 344 LPC_LCD_TIMV_VBP(cfg->lc_upper_margin)); 345 346 /* XXX LPC_LCD_POL_PCD_LO */ 347 lpc_fb_write_4(sc, LPC_LCD_POL, 348 LPC_LCD_POL_IHS | LPC_LCD_POL_IVS | 349 LPC_LCD_POL_CPL(cfg->lc_xres - 1) | 350 LPC_LCD_POL_PCD_LO(4)); 351 352 lpc_fb_write_4(sc, LPC_LCD_UPBASE, sc->lf_buffer_phys); 353 354 switch (cfg->lc_bpp) { 355 case 1: 356 bpp = LPC_LCD_CTRL_BPP1; 357 break; 358 case 2: 359 bpp = LPC_LCD_CTRL_BPP2; 360 break; 361 case 4: 362 bpp = LPC_LCD_CTRL_BPP4; 363 break; 364 case 8: 365 bpp = LPC_LCD_CTRL_BPP8; 366 break; 367 case 12: 368 bpp = LPC_LCD_CTRL_BPP12_444; 369 break; 370 case 15: 371 bpp = LPC_LCD_CTRL_BPP16; 372 break; 373 case 16: 374 bpp = LPC_LCD_CTRL_BPP16_565; 375 break; 376 case 24: 377 bpp = LPC_LCD_CTRL_BPP24; 378 break; 379 default: 380 panic("LCD unknown bpp: %d", cfg->lc_bpp); 381 } 382 383 lpc_fb_write_4(sc, LPC_LCD_CTRL, 384 LPC_LCD_CTRL_LCDVCOMP(1) | 385 LPC_LCD_CTRL_LCDPWR | 386 LPC_LCD_CTRL_BGR | 387 LPC_LCD_CTRL_LCDTFT | 388 LPC_LCD_CTRL_LCDBPP(bpp) | 389 LPC_LCD_CTRL_LCDEN); 390} 391 392 393static int 394lpc_fb_open(struct cdev *cdev, int oflags, int devtype, struct thread *td) 395{ 396 struct lpc_fb_softc *sc = cdev->si_drv1; 397 398 lpc_fb_lock(sc); 399 400 if (sc->lf_opened) 401 return (EBUSY); 402 403 sc->lf_opened = 1; 404 405 lpc_fb_unlock(sc); 406 407 if (!sc->lf_initialized) { 408 ssd1289_configure(); 409 lpc_fb_setup(sc); 410 lpc_fb_blank(sc); 411 sc->lf_initialized = 1; 412 } 413 414 return (0); 415} 416 417static int 418lpc_fb_close(struct cdev *cdev, int fflag, int devtype, struct thread *td) 419{ 420 struct lpc_fb_softc *sc = cdev->si_drv1; 421 422 lpc_fb_lock(sc); 423 sc->lf_opened = 0; 424 lpc_fb_unlock(sc); 425 426 return (0); 427} 428 429static int 430lpc_fb_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int x, 431 struct thread *td) 432{ 433 434 return (EINVAL); 435} 436 437static int 438lpc_fb_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr, 439 int nprot, vm_memattr_t *memattr) 440{ 441 struct lpc_fb_softc *sc = cdev->si_drv1; 442 443 *paddr = (vm_paddr_t)(sc->lf_buffer_phys + offset); 444 return (0); 445} 446 447static void 448lpc_fb_blank(struct lpc_fb_softc *sc) 449{ 450 memset(sc->lf_buffer, 0xffff, sc->lf_buffer_size); 451} 452 453static device_method_t lpc_fb_methods[] = { 454 /* Device interface */ 455 DEVMETHOD(device_probe, lpc_fb_probe), 456 DEVMETHOD(device_attach, lpc_fb_attach), 457 458 { 0, 0 } 459}; 460 461static devclass_t lpc_fb_devclass; 462 463static driver_t lpc_fb_driver = { 464 "lpcfb", 465 lpc_fb_methods, 466 sizeof(struct lpc_fb_softc), 467}; 468 469DRIVER_MODULE(lpcfb, simplebus, lpc_fb_driver, lpc_fb_devclass, 0, 0); 470