lpc_fb.c revision 314503
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/11/sys/arm/lpc/lpc_fb.c 314503 2017-03-01 18:53:05Z 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/resource.h> 53#include <machine/intr.h> 54 55#include <dev/ofw/ofw_bus.h> 56#include <dev/ofw/ofw_bus_subr.h> 57 58#include <arm/lpc/lpcreg.h> 59#include <arm/lpc/lpcvar.h> 60 61 62struct lpc_fb_dmamap_arg { 63 bus_addr_t lf_dma_busaddr; 64}; 65 66struct lpc_lcd_config { 67 int lc_xres; 68 int lc_yres; 69 int lc_bpp; 70 uint32_t lc_pixelclock; 71 int lc_left_margin; 72 int lc_right_margin; 73 int lc_upper_margin; 74 int lc_lower_margin; 75 int lc_hsync_len; 76 int lc_vsync_len; 77}; 78 79struct lpc_fb_softc { 80 device_t lf_dev; 81 struct cdev * lf_cdev; 82 struct mtx lf_mtx; 83 struct resource * lf_mem_res; 84 struct resource * lf_irq_res; 85 bus_space_tag_t lf_bst; 86 bus_space_handle_t lf_bsh; 87 void * lf_intrhand; 88 bus_dma_tag_t lf_dma_tag; 89 bus_dmamap_t lf_dma_map; 90 void * lf_buffer; 91 bus_addr_t lf_buffer_phys; 92 bus_size_t lf_buffer_size; 93 struct lpc_lcd_config lf_lcd_config; 94 int lf_initialized; 95 int lf_opened; 96}; 97 98extern void ssd1289_configure(void); 99 100#define lpc_fb_lock(_sc) mtx_lock(&(_sc)->lf_mtx) 101#define lpc_fb_unlock(_sc) mtx_unlock(&(_sc)->lf_mtx) 102#define lpc_fb_lock_assert(sc) mtx_assert(&(_sc)->lf_mtx, MA_OWNED) 103 104#define lpc_fb_read_4(_sc, _reg) \ 105 bus_space_read_4((_sc)->lf_bst, (_sc)->lf_bsh, (_reg)) 106#define lpc_fb_write_4(_sc, _reg, _val) \ 107 bus_space_write_4((_sc)->lf_bst, (_sc)->lf_bsh, (_reg), (_val)) 108 109 110 111static int lpc_fb_probe(device_t); 112static int lpc_fb_attach(device_t); 113static void lpc_fb_intr(void *); 114static void lpc_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err); 115 116static int lpc_fb_fdt_read(phandle_t, const char *, uint32_t *); 117static int lpc_fb_read_lcd_config(phandle_t, struct lpc_lcd_config *); 118 119static int lpc_fb_open(struct cdev *, int, int, struct thread *); 120static int lpc_fb_close(struct cdev *, int, int, struct thread *); 121static int lpc_fb_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *); 122static int lpc_fb_mmap(struct cdev *, vm_ooffset_t, vm_paddr_t *, int, vm_memattr_t *); 123 124static void lpc_fb_blank(struct lpc_fb_softc *); 125 126static struct cdevsw lpc_fb_cdevsw = { 127 .d_open = lpc_fb_open, 128 .d_close = lpc_fb_close, 129 .d_ioctl = lpc_fb_ioctl, 130 .d_mmap = lpc_fb_mmap, 131 .d_name = "lpcfb", 132 .d_version = D_VERSION, 133}; 134 135static int 136lpc_fb_probe(device_t dev) 137{ 138 139 if (!ofw_bus_status_okay(dev)) 140 return (ENXIO); 141 142 if (!ofw_bus_is_compatible(dev, "lpc,fb")) 143 return (ENXIO); 144 145 device_set_desc(dev, "LPC32x0 framebuffer device"); 146 return (BUS_PROBE_DEFAULT); 147} 148 149static int 150lpc_fb_attach(device_t dev) 151{ 152 struct lpc_fb_softc *sc = device_get_softc(dev); 153 struct lpc_fb_dmamap_arg ctx; 154 phandle_t node; 155 int mode, rid, err = 0; 156 157 sc->lf_dev = dev; 158 mtx_init(&sc->lf_mtx, "lpcfb", "fb", MTX_DEF); 159 160 rid = 0; 161 sc->lf_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 162 RF_ACTIVE); 163 if (!sc->lf_mem_res) { 164 device_printf(dev, "cannot allocate memory window\n"); 165 return (ENXIO); 166 } 167 168 sc->lf_bst = rman_get_bustag(sc->lf_mem_res); 169 sc->lf_bsh = rman_get_bushandle(sc->lf_mem_res); 170 171 rid = 0; 172 sc->lf_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 173 RF_ACTIVE); 174 if (!sc->lf_irq_res) { 175 device_printf(dev, "cannot allocate interrupt\n"); 176 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lf_mem_res); 177 return (ENXIO); 178 } 179 180 if (bus_setup_intr(dev, sc->lf_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 181 NULL, lpc_fb_intr, sc, &sc->lf_intrhand)) 182 { 183 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lf_mem_res); 184 bus_release_resource(dev, SYS_RES_IRQ, 1, sc->lf_irq_res); 185 device_printf(dev, "cannot setup interrupt handler\n"); 186 return (ENXIO); 187 } 188 189 node = ofw_bus_get_node(dev); 190 191 err = lpc_fb_read_lcd_config(node, &sc->lf_lcd_config); 192 if (err) { 193 device_printf(dev, "cannot read LCD configuration\n"); 194 goto fail; 195 } 196 197 sc->lf_buffer_size = sc->lf_lcd_config.lc_xres * 198 sc->lf_lcd_config.lc_yres * 199 (sc->lf_lcd_config.lc_bpp == 24 ? 3 : 2); 200 201 device_printf(dev, "%dx%d LCD, %d bits per pixel, %dkHz pixel clock\n", 202 sc->lf_lcd_config.lc_xres, sc->lf_lcd_config.lc_yres, 203 sc->lf_lcd_config.lc_bpp, sc->lf_lcd_config.lc_pixelclock / 1000); 204 205 err = bus_dma_tag_create( 206 bus_get_dma_tag(sc->lf_dev), 207 4, 0, /* alignment, boundary */ 208 BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 209 BUS_SPACE_MAXADDR, /* highaddr */ 210 NULL, NULL, /* filter, filterarg */ 211 sc->lf_buffer_size, 1, /* maxsize, nsegments */ 212 sc->lf_buffer_size, 0, /* maxsegsize, flags */ 213 NULL, NULL, /* lockfunc, lockarg */ 214 &sc->lf_dma_tag); 215 216 err = bus_dmamem_alloc(sc->lf_dma_tag, (void **)&sc->lf_buffer, 217 0, &sc->lf_dma_map); 218 if (err) { 219 device_printf(dev, "cannot allocate framebuffer\n"); 220 goto fail; 221 } 222 223 err = bus_dmamap_load(sc->lf_dma_tag, sc->lf_dma_map, sc->lf_buffer, 224 sc->lf_buffer_size, lpc_fb_dmamap_cb, &ctx, BUS_DMA_NOWAIT); 225 if (err) { 226 device_printf(dev, "cannot load DMA map\n"); 227 goto fail; 228 } 229 230 switch (sc->lf_lcd_config.lc_bpp) { 231 case 12: 232 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_12; 233 break; 234 case 15: 235 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_15; 236 break; 237 case 16: 238 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_16; 239 break; 240 case 24: 241 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_24; 242 break; 243 default: 244 panic("unsupported bpp"); 245 } 246 247 lpc_pwr_write(sc->lf_dev, LPC_CLKPWR_LCDCLK_CTRL, 248 LPC_CLKPWR_LCDCLK_CTRL_MODE(mode) | 249 LPC_CLKPWR_LCDCLK_CTRL_HCLKEN); 250 251 sc->lf_buffer_phys = ctx.lf_dma_busaddr; 252 sc->lf_cdev = make_dev(&lpc_fb_cdevsw, 0, UID_ROOT, GID_WHEEL, 253 0600, "lpcfb"); 254 255 sc->lf_cdev->si_drv1 = sc; 256 257 return (0); 258fail: 259 return (ENXIO); 260} 261 262static void 263lpc_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) 264{ 265 struct lpc_fb_dmamap_arg *ctx; 266 267 if (err) 268 return; 269 270 ctx = (struct lpc_fb_dmamap_arg *)arg; 271 ctx->lf_dma_busaddr = segs[0].ds_addr; 272} 273 274static void 275lpc_fb_intr(void *arg) 276{ 277} 278 279static int 280lpc_fb_fdt_read(phandle_t node, const char *name, uint32_t *ret) 281{ 282 if (OF_getencprop(node, name, ret, sizeof(uint32_t)) <= 0) 283 return (ENOENT); 284 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