lpc_fb.c revision 259329
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: stable/10/sys/arm/lpc/lpc_fb.c 259329 2013-12-13 20:43:11Z 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 if (!ofw_bus_is_compatible(dev, "lpc,fb")) 142 return (ENXIO); 143 144 device_set_desc(dev, "LPC32x0 framebuffer device"); 145 return (BUS_PROBE_DEFAULT); 146} 147 148static int 149lpc_fb_attach(device_t dev) 150{ 151 struct lpc_fb_softc *sc = device_get_softc(dev); 152 struct lpc_fb_dmamap_arg ctx; 153 phandle_t node; 154 int mode, rid, err = 0; 155 156 sc->lf_dev = dev; 157 mtx_init(&sc->lf_mtx, "lpcfb", "fb", MTX_DEF); 158 159 rid = 0; 160 sc->lf_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 161 RF_ACTIVE); 162 if (!sc->lf_mem_res) { 163 device_printf(dev, "cannot allocate memory window\n"); 164 return (ENXIO); 165 } 166 167 sc->lf_bst = rman_get_bustag(sc->lf_mem_res); 168 sc->lf_bsh = rman_get_bushandle(sc->lf_mem_res); 169 170 rid = 0; 171 sc->lf_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 172 RF_ACTIVE); 173 if (!sc->lf_irq_res) { 174 device_printf(dev, "cannot allocate interrupt\n"); 175 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lf_mem_res); 176 return (ENXIO); 177 } 178 179 if (bus_setup_intr(dev, sc->lf_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 180 NULL, lpc_fb_intr, sc, &sc->lf_intrhand)) 181 { 182 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lf_mem_res); 183 bus_release_resource(dev, SYS_RES_IRQ, 1, sc->lf_irq_res); 184 device_printf(dev, "cannot setup interrupt handler\n"); 185 return (ENXIO); 186 } 187 188 node = ofw_bus_get_node(dev); 189 190 err = lpc_fb_read_lcd_config(node, &sc->lf_lcd_config); 191 if (err) { 192 device_printf(dev, "cannot read LCD configuration\n"); 193 goto fail; 194 } 195 196 sc->lf_buffer_size = sc->lf_lcd_config.lc_xres * 197 sc->lf_lcd_config.lc_yres * 198 (sc->lf_lcd_config.lc_bpp == 24 ? 3 : 2); 199 200 device_printf(dev, "%dx%d LCD, %d bits per pixel, %dkHz pixel clock\n", 201 sc->lf_lcd_config.lc_xres, sc->lf_lcd_config.lc_yres, 202 sc->lf_lcd_config.lc_bpp, sc->lf_lcd_config.lc_pixelclock / 1000); 203 204 err = bus_dma_tag_create( 205 bus_get_dma_tag(sc->lf_dev), 206 4, 0, /* alignment, boundary */ 207 BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 208 BUS_SPACE_MAXADDR, /* highaddr */ 209 NULL, NULL, /* filter, filterarg */ 210 sc->lf_buffer_size, 1, /* maxsize, nsegments */ 211 sc->lf_buffer_size, 0, /* maxsegsize, flags */ 212 NULL, NULL, /* lockfunc, lockarg */ 213 &sc->lf_dma_tag); 214 215 err = bus_dmamem_alloc(sc->lf_dma_tag, (void **)&sc->lf_buffer, 216 0, &sc->lf_dma_map); 217 if (err) { 218 device_printf(dev, "cannot allocate framebuffer\n"); 219 goto fail; 220 } 221 222 err = bus_dmamap_load(sc->lf_dma_tag, sc->lf_dma_map, sc->lf_buffer, 223 sc->lf_buffer_size, lpc_fb_dmamap_cb, &ctx, BUS_DMA_NOWAIT); 224 if (err) { 225 device_printf(dev, "cannot load DMA map\n"); 226 goto fail; 227 } 228 229 switch (sc->lf_lcd_config.lc_bpp) { 230 case 12: 231 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_12; 232 break; 233 case 15: 234 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_15; 235 break; 236 case 16: 237 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_16; 238 break; 239 case 24: 240 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_24; 241 break; 242 default: 243 panic("unsupported bpp"); 244 } 245 246 lpc_pwr_write(sc->lf_dev, LPC_CLKPWR_LCDCLK_CTRL, 247 LPC_CLKPWR_LCDCLK_CTRL_MODE(mode) | 248 LPC_CLKPWR_LCDCLK_CTRL_HCLKEN); 249 250 sc->lf_buffer_phys = ctx.lf_dma_busaddr; 251 sc->lf_cdev = make_dev(&lpc_fb_cdevsw, 0, UID_ROOT, GID_WHEEL, 252 0600, "lpcfb"); 253 254 sc->lf_cdev->si_drv1 = sc; 255 256 return (0); 257fail: 258 return (ENXIO); 259} 260 261static void 262lpc_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) 263{ 264 struct lpc_fb_dmamap_arg *ctx; 265 266 if (err) 267 return; 268 269 ctx = (struct lpc_fb_dmamap_arg *)arg; 270 ctx->lf_dma_busaddr = segs[0].ds_addr; 271} 272 273static void 274lpc_fb_intr(void *arg) 275{ 276} 277 278static int 279lpc_fb_fdt_read(phandle_t node, const char *name, uint32_t *ret) 280{ 281 if (OF_getprop(node, name, ret, sizeof(uint32_t)) <= 0) 282 return (ENOENT); 283 284 *ret = fdt32_to_cpu(*ret); 285 return (0); 286} 287 288static int 289lpc_fb_read_lcd_config(phandle_t node, struct lpc_lcd_config *cfg) 290{ 291 if (lpc_fb_fdt_read(node, "horizontal-resolution", &cfg->lc_xres)) 292 return (ENXIO); 293 294 if (lpc_fb_fdt_read(node, "vertical-resolution", &cfg->lc_yres)) 295 return (ENXIO); 296 297 if (lpc_fb_fdt_read(node, "bits-per-pixel", &cfg->lc_bpp)) 298 return (ENXIO); 299 300 if (lpc_fb_fdt_read(node, "pixel-clock", &cfg->lc_pixelclock)) 301 return (ENXIO); 302 303 if (lpc_fb_fdt_read(node, "left-margin", &cfg->lc_left_margin)) 304 return (ENXIO); 305 306 if (lpc_fb_fdt_read(node, "right-margin", &cfg->lc_right_margin)) 307 return (ENXIO); 308 309 if (lpc_fb_fdt_read(node, "upper-margin", &cfg->lc_upper_margin)) 310 return (ENXIO); 311 312 if (lpc_fb_fdt_read(node, "lower-margin", &cfg->lc_lower_margin)) 313 return (ENXIO); 314 315 if (lpc_fb_fdt_read(node, "hsync-len", &cfg->lc_hsync_len)) 316 return (ENXIO); 317 318 if (lpc_fb_fdt_read(node, "vsync-len", &cfg->lc_vsync_len)) 319 return (ENXIO); 320 321 return (0); 322} 323 324static void 325lpc_fb_setup(struct lpc_fb_softc *sc) 326{ 327 struct lpc_lcd_config *cfg = &sc->lf_lcd_config; 328 uint32_t bpp; 329 330 lpc_fb_write_4(sc, LPC_LCD_TIMH, 331 LPC_LCD_TIMH_PPL(cfg->lc_xres) | 332 LPC_LCD_TIMH_HSW(cfg->lc_hsync_len - 1) | 333 LPC_LCD_TIMH_HFP(cfg->lc_right_margin - 1) | 334 LPC_LCD_TIMH_HBP(cfg->lc_left_margin - 1)); 335 336 lpc_fb_write_4(sc, LPC_LCD_TIMV, 337 LPC_LCD_TIMV_LPP(cfg->lc_yres - 1) | 338 LPC_LCD_TIMV_VSW(cfg->lc_vsync_len - 1) | 339 LPC_LCD_TIMV_VFP(cfg->lc_lower_margin) | 340 LPC_LCD_TIMV_VBP(cfg->lc_upper_margin)); 341 342 /* XXX LPC_LCD_POL_PCD_LO */ 343 lpc_fb_write_4(sc, LPC_LCD_POL, 344 LPC_LCD_POL_IHS | LPC_LCD_POL_IVS | 345 LPC_LCD_POL_CPL(cfg->lc_xres - 1) | 346 LPC_LCD_POL_PCD_LO(4)); 347 348 lpc_fb_write_4(sc, LPC_LCD_UPBASE, sc->lf_buffer_phys); 349 350 switch (cfg->lc_bpp) { 351 case 1: 352 bpp = LPC_LCD_CTRL_BPP1; 353 break; 354 case 2: 355 bpp = LPC_LCD_CTRL_BPP2; 356 break; 357 case 4: 358 bpp = LPC_LCD_CTRL_BPP4; 359 break; 360 case 8: 361 bpp = LPC_LCD_CTRL_BPP8; 362 break; 363 case 12: 364 bpp = LPC_LCD_CTRL_BPP12_444; 365 break; 366 case 15: 367 bpp = LPC_LCD_CTRL_BPP16; 368 break; 369 case 16: 370 bpp = LPC_LCD_CTRL_BPP16_565; 371 break; 372 case 24: 373 bpp = LPC_LCD_CTRL_BPP24; 374 break; 375 default: 376 panic("LCD unknown bpp: %d", cfg->lc_bpp); 377 } 378 379 lpc_fb_write_4(sc, LPC_LCD_CTRL, 380 LPC_LCD_CTRL_LCDVCOMP(1) | 381 LPC_LCD_CTRL_LCDPWR | 382 LPC_LCD_CTRL_BGR | 383 LPC_LCD_CTRL_LCDTFT | 384 LPC_LCD_CTRL_LCDBPP(bpp) | 385 LPC_LCD_CTRL_LCDEN); 386} 387 388 389static int 390lpc_fb_open(struct cdev *cdev, int oflags, int devtype, struct thread *td) 391{ 392 struct lpc_fb_softc *sc = cdev->si_drv1; 393 394 lpc_fb_lock(sc); 395 396 if (sc->lf_opened) 397 return (EBUSY); 398 399 sc->lf_opened = 1; 400 401 lpc_fb_unlock(sc); 402 403 if (!sc->lf_initialized) { 404 ssd1289_configure(); 405 lpc_fb_setup(sc); 406 lpc_fb_blank(sc); 407 sc->lf_initialized = 1; 408 } 409 410 return (0); 411} 412 413static int 414lpc_fb_close(struct cdev *cdev, int fflag, int devtype, struct thread *td) 415{ 416 struct lpc_fb_softc *sc = cdev->si_drv1; 417 418 lpc_fb_lock(sc); 419 sc->lf_opened = 0; 420 lpc_fb_unlock(sc); 421 422 return (0); 423} 424 425static int 426lpc_fb_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int x, 427 struct thread *td) 428{ 429 430 return (EINVAL); 431} 432 433static int 434lpc_fb_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr, 435 int nprot, vm_memattr_t *memattr) 436{ 437 struct lpc_fb_softc *sc = cdev->si_drv1; 438 439 *paddr = (vm_paddr_t)(sc->lf_buffer_phys + offset); 440 return (0); 441} 442 443static void 444lpc_fb_blank(struct lpc_fb_softc *sc) 445{ 446 memset(sc->lf_buffer, 0xffff, sc->lf_buffer_size); 447} 448 449static device_method_t lpc_fb_methods[] = { 450 /* Device interface */ 451 DEVMETHOD(device_probe, lpc_fb_probe), 452 DEVMETHOD(device_attach, lpc_fb_attach), 453 454 { 0, 0 } 455}; 456 457static devclass_t lpc_fb_devclass; 458 459static driver_t lpc_fb_driver = { 460 "lpcfb", 461 lpc_fb_methods, 462 sizeof(struct lpc_fb_softc), 463}; 464 465DRIVER_MODULE(lpcfb, simplebus, lpc_fb_driver, lpc_fb_devclass, 0, 0); 466