1/*- 2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 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 * $FreeBSD$ 27 */ 28 29/* 30 * Ingenic JZ4780 LCD Controller 31 */ 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD$"); 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/bus.h> 39#include <sys/rman.h> 40#include <sys/condvar.h> 41#include <sys/kernel.h> 42#include <sys/module.h> 43#include <sys/fbio.h> 44#include <vm/vm.h> 45#include <vm/vm_extern.h> 46#include <vm/vm_kern.h> 47#include <vm/pmap.h> 48 49#include <machine/bus.h> 50 51#include <dev/ofw/ofw_bus.h> 52#include <dev/ofw/ofw_bus_subr.h> 53 54#include <dev/videomode/videomode.h> 55#include <dev/videomode/edidvar.h> 56 57#include <dev/extres/clk/clk.h> 58 59#include <mips/ingenic/jz4780_lcd.h> 60 61#include "fb_if.h" 62#include "hdmi_if.h" 63 64#define FB_DEFAULT_W 800 65#define FB_DEFAULT_H 600 66#define FB_DEFAULT_REF 60 67#define FB_BPP 32 68#define FB_ALIGN (16 * 4) 69#define FB_MAX_BW (1920 * 1080 * 60) 70#define FB_MAX_W 2048 71#define FB_MAX_H 2048 72#define FB_DIVIDE(x, y) (((x) + ((y) / 2)) / (y)) 73 74#define PCFG_MAGIC 0xc7ff2100 75 76#define DOT_CLOCK_TO_HZ(c) ((c) * 1000) 77 78#ifndef VM_MEMATTR_WRITE_COMBINING 79#define VM_MEMATTR_WRITE_COMBINING VM_MEMATTR_UNCACHEABLE 80#endif 81 82struct jzlcd_softc { 83 device_t dev; 84 device_t fbdev; 85 struct resource *res[1]; 86 87 /* Clocks */ 88 clk_t clk; 89 clk_t clk_pix; 90 91 /* Framebuffer */ 92 struct fb_info info; 93 size_t fbsize; 94 bus_addr_t paddr; 95 vm_offset_t vaddr; 96 97 /* HDMI */ 98 eventhandler_tag hdmi_evh; 99 100 /* Frame descriptor DMA */ 101 bus_dma_tag_t fdesc_tag; 102 bus_dmamap_t fdesc_map; 103 bus_addr_t fdesc_paddr; 104 struct lcd_frame_descriptor *fdesc; 105}; 106 107static struct resource_spec jzlcd_spec[] = { 108 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 109 { -1, 0 } 110}; 111 112#define LCD_READ(sc, reg) bus_read_4((sc)->res[0], (reg)) 113#define LCD_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) 114 115static int 116jzlcd_allocfb(struct jzlcd_softc *sc) 117{ 118 sc->vaddr = kmem_alloc_contig(sc->fbsize, M_NOWAIT | M_ZERO, 0, ~0, 119 FB_ALIGN, 0, VM_MEMATTR_WRITE_COMBINING); 120 if (sc->vaddr == 0) { 121 device_printf(sc->dev, "failed to allocate FB memory\n"); 122 return (ENOMEM); 123 } 124 sc->paddr = pmap_kextract(sc->vaddr); 125 126 return (0); 127} 128 129static void 130jzlcd_freefb(struct jzlcd_softc *sc) 131{ 132 kmem_free(sc->vaddr, sc->fbsize); 133} 134 135static void 136jzlcd_start(struct jzlcd_softc *sc) 137{ 138 uint32_t ctrl; 139 140 /* Clear status registers */ 141 LCD_WRITE(sc, LCDSTATE, 0); 142 LCD_WRITE(sc, LCDOSDS, 0); 143 /* Enable the controller */ 144 ctrl = LCD_READ(sc, LCDCTRL); 145 ctrl |= LCDCTRL_ENA; 146 ctrl &= ~LCDCTRL_DIS; 147 LCD_WRITE(sc, LCDCTRL, ctrl); 148} 149 150static void 151jzlcd_stop(struct jzlcd_softc *sc) 152{ 153 uint32_t ctrl; 154 155 ctrl = LCD_READ(sc, LCDCTRL); 156 if ((ctrl & LCDCTRL_ENA) != 0) { 157 /* Disable the controller and wait for it to stop */ 158 ctrl |= LCDCTRL_DIS; 159 LCD_WRITE(sc, LCDCTRL, ctrl); 160 while ((LCD_READ(sc, LCDSTATE) & LCDSTATE_LDD) == 0) 161 DELAY(100); 162 } 163 /* Clear all status except for disable */ 164 LCD_WRITE(sc, LCDSTATE, LCD_READ(sc, LCDSTATE) & ~LCDSTATE_LDD); 165} 166 167static void 168jzlcd_setup_descriptor(struct jzlcd_softc *sc, const struct videomode *mode, 169 u_int desno) 170{ 171 struct lcd_frame_descriptor *fdesc; 172 int line_sz; 173 174 /* Frame size is specified in # words */ 175 line_sz = (mode->hdisplay * FB_BPP) >> 3; 176 line_sz = ((line_sz + 3) & ~3) / 4; 177 178 fdesc = sc->fdesc + desno; 179 180 if (desno == 0) 181 fdesc->next = sc->fdesc_paddr + 182 sizeof(struct lcd_frame_descriptor); 183 else 184 fdesc->next = sc->fdesc_paddr; 185 fdesc->physaddr = sc->paddr; 186 fdesc->id = desno; 187 fdesc->cmd = LCDCMD_FRM_EN | (line_sz * mode->vdisplay); 188 fdesc->offs = 0; 189 fdesc->pw = 0; 190 fdesc->cnum_pos = LCDPOS_BPP01_18_24 | 191 LCDPOS_PREMULTI01 | 192 (desno == 0 ? LCDPOS_COEF_BLE01_1 : LCDPOS_COEF_SLE01); 193 fdesc->dessize = LCDDESSIZE_ALPHA | 194 ((mode->vdisplay - 1) << LCDDESSIZE_HEIGHT_SHIFT) | 195 ((mode->hdisplay - 1) << LCDDESSIZE_WIDTH_SHIFT); 196} 197 198static int 199jzlcd_set_videomode(struct jzlcd_softc *sc, const struct videomode *mode) 200{ 201 u_int hbp, hfp, hsw, vbp, vfp, vsw; 202 u_int hds, hde, ht, vds, vde, vt; 203 uint32_t ctrl; 204 int error; 205 206 hbp = mode->htotal - mode->hsync_end; 207 hfp = mode->hsync_start - mode->hdisplay; 208 hsw = mode->hsync_end - mode->hsync_start; 209 vbp = mode->vtotal - mode->vsync_end; 210 vfp = mode->vsync_start - mode->vdisplay; 211 vsw = mode->vsync_end - mode->vsync_start; 212 213 hds = hsw + hbp; 214 hde = hds + mode->hdisplay; 215 ht = hde + hfp; 216 217 vds = vsw + vbp; 218 vde = vds + mode->vdisplay; 219 vt = vde + vfp; 220 221 /* Setup timings */ 222 LCD_WRITE(sc, LCDVAT, 223 (ht << LCDVAT_HT_SHIFT) | (vt << LCDVAT_VT_SHIFT)); 224 LCD_WRITE(sc, LCDDAH, 225 (hds << LCDDAH_HDS_SHIFT) | (hde << LCDDAH_HDE_SHIFT)); 226 LCD_WRITE(sc, LCDDAV, 227 (vds << LCDDAV_VDS_SHIFT) | (vde << LCDDAV_VDE_SHIFT)); 228 LCD_WRITE(sc, LCDHSYNC, hsw); 229 LCD_WRITE(sc, LCDVSYNC, vsw); 230 231 /* Set configuration */ 232 LCD_WRITE(sc, LCDCFG, LCDCFG_NEWDES | LCDCFG_RECOVER | LCDCFG_24 | 233 LCDCFG_PSM | LCDCFG_CLSM | LCDCFG_SPLM | LCDCFG_REVM | LCDCFG_PCP); 234 ctrl = LCD_READ(sc, LCDCTRL); 235 ctrl &= ~LCDCTRL_BST; 236 ctrl |= LCDCTRL_BST_64 | LCDCTRL_OFUM; 237 LCD_WRITE(sc, LCDCTRL, ctrl); 238 LCD_WRITE(sc, LCDPCFG, PCFG_MAGIC); 239 LCD_WRITE(sc, LCDRGBC, LCDRGBC_RGBFMT); 240 241 /* Update registers */ 242 LCD_WRITE(sc, LCDSTATE, 0); 243 244 /* Setup frame descriptors */ 245 jzlcd_setup_descriptor(sc, mode, 0); 246 jzlcd_setup_descriptor(sc, mode, 1); 247 bus_dmamap_sync(sc->fdesc_tag, sc->fdesc_map, BUS_DMASYNC_PREWRITE); 248 249 /* Setup DMA channels */ 250 LCD_WRITE(sc, LCDDA0, sc->fdesc_paddr 251 + sizeof(struct lcd_frame_descriptor)); 252 LCD_WRITE(sc, LCDDA1, sc->fdesc_paddr); 253 254 /* Set display clock */ 255 error = clk_set_freq(sc->clk_pix, DOT_CLOCK_TO_HZ(mode->dot_clock), 0); 256 if (error != 0) { 257 device_printf(sc->dev, "failed to set pixel clock to %u Hz\n", 258 DOT_CLOCK_TO_HZ(mode->dot_clock)); 259 return (error); 260 } 261 262 return (0); 263} 264 265static int 266jzlcd_configure(struct jzlcd_softc *sc, const struct videomode *mode) 267{ 268 size_t fbsize; 269 int error; 270 271 fbsize = round_page(mode->hdisplay * mode->vdisplay * (FB_BPP / NBBY)); 272 273 /* Detach the old FB device */ 274 if (sc->fbdev != NULL) { 275 device_delete_child(sc->dev, sc->fbdev); 276 sc->fbdev = NULL; 277 } 278 279 /* If the FB size has changed, free the old FB memory */ 280 if (sc->fbsize > 0 && sc->fbsize != fbsize) { 281 jzlcd_freefb(sc); 282 sc->vaddr = 0; 283 } 284 285 /* Allocate the FB if necessary */ 286 sc->fbsize = fbsize; 287 if (sc->vaddr == 0) { 288 error = jzlcd_allocfb(sc); 289 if (error != 0) { 290 device_printf(sc->dev, "failed to allocate FB memory\n"); 291 return (ENXIO); 292 } 293 } 294 295 /* Setup video mode */ 296 error = jzlcd_set_videomode(sc, mode); 297 if (error != 0) 298 return (error); 299 300 /* Attach framebuffer device */ 301 sc->info.fb_name = device_get_nameunit(sc->dev); 302 sc->info.fb_vbase = (intptr_t)sc->vaddr; 303 sc->info.fb_pbase = sc->paddr; 304 sc->info.fb_size = sc->fbsize; 305 sc->info.fb_bpp = sc->info.fb_depth = FB_BPP; 306 sc->info.fb_stride = mode->hdisplay * (FB_BPP / NBBY); 307 sc->info.fb_width = mode->hdisplay; 308 sc->info.fb_height = mode->vdisplay; 309#ifdef VM_MEMATTR_WRITE_COMBINING 310 sc->info.fb_flags = FB_FLAG_MEMATTR; 311 sc->info.fb_memattr = VM_MEMATTR_WRITE_COMBINING; 312#endif 313 sc->fbdev = device_add_child(sc->dev, "fbd", device_get_unit(sc->dev)); 314 if (sc->fbdev == NULL) { 315 device_printf(sc->dev, "failed to add fbd child\n"); 316 return (ENOENT); 317 } 318 319 error = device_probe_and_attach(sc->fbdev); 320 if (error != 0) { 321 device_printf(sc->dev, "failed to attach fbd device\n"); 322 return (error); 323 } 324 325 return (0); 326} 327 328static int 329jzlcd_get_bandwidth(const struct videomode *mode) 330{ 331 int refresh; 332 333 refresh = FB_DIVIDE(FB_DIVIDE(DOT_CLOCK_TO_HZ(mode->dot_clock), 334 mode->htotal), mode->vtotal); 335 336 return mode->hdisplay * mode->vdisplay * refresh; 337} 338 339static int 340jzlcd_mode_supported(const struct videomode *mode) 341{ 342 /* Width and height must be less than 2048 */ 343 if (mode->hdisplay > FB_MAX_W || mode->vdisplay > FB_MAX_H) 344 return (0); 345 346 /* Bandwidth check */ 347 if (jzlcd_get_bandwidth(mode) > FB_MAX_BW) 348 return (0); 349 350 /* Interlace modes not yet supported by the driver */ 351 if ((mode->flags & VID_INTERLACE) != 0) 352 return (0); 353 354 return (1); 355} 356 357static const struct videomode * 358jzlcd_find_mode(struct edid_info *ei) 359{ 360 const struct videomode *best; 361 int n, bw, best_bw; 362 363 /* If the preferred mode is OK, just use it */ 364 if (jzlcd_mode_supported(ei->edid_preferred_mode) != 0) 365 return ei->edid_preferred_mode; 366 367 /* Pick the mode with the highest bandwidth requirements */ 368 best = NULL; 369 best_bw = 0; 370 for (n = 0; n < ei->edid_nmodes; n++) { 371 if (jzlcd_mode_supported(&ei->edid_modes[n]) == 0) 372 continue; 373 bw = jzlcd_get_bandwidth(&ei->edid_modes[n]); 374 if (bw > FB_MAX_BW) 375 continue; 376 if (best == NULL || bw > best_bw) { 377 best = &ei->edid_modes[n]; 378 best_bw = bw; 379 } 380 } 381 382 return best; 383} 384 385static void 386jzlcd_hdmi_event(void *arg, device_t hdmi_dev) 387{ 388 const struct videomode *mode; 389 struct videomode hdmi_mode; 390 struct jzlcd_softc *sc; 391 struct edid_info ei; 392 uint8_t *edid; 393 uint32_t edid_len; 394 int error; 395 396 sc = arg; 397 edid = NULL; 398 edid_len = 0; 399 mode = NULL; 400 401 error = HDMI_GET_EDID(hdmi_dev, &edid, &edid_len); 402 if (error != 0) { 403 device_printf(sc->dev, "failed to get EDID: %d\n", error); 404 } else { 405 error = edid_parse(edid, &ei); 406 if (error != 0) { 407 device_printf(sc->dev, "failed to parse EDID: %d\n", 408 error); 409 } else { 410 if (bootverbose) 411 edid_print(&ei); 412 413 mode = jzlcd_find_mode(&ei); 414 } 415 } 416 417 /* If a suitable mode could not be found, try the default */ 418 if (mode == NULL) 419 mode = pick_mode_by_ref(FB_DEFAULT_W, FB_DEFAULT_H, 420 FB_DEFAULT_REF); 421 422 if (mode == NULL) { 423 device_printf(sc->dev, "failed to find usable video mode\n"); 424 return; 425 } 426 427 if (bootverbose) 428 device_printf(sc->dev, "using %dx%d\n", 429 mode->hdisplay, mode->vdisplay); 430 431 /* Stop the controller */ 432 jzlcd_stop(sc); 433 434 /* Configure LCD controller */ 435 error = jzlcd_configure(sc, mode); 436 if (error != 0) { 437 device_printf(sc->dev, "failed to configure FB: %d\n", error); 438 return; 439 } 440 441 /* Enable HDMI TX */ 442 hdmi_mode = *mode; 443 HDMI_SET_VIDEOMODE(hdmi_dev, &hdmi_mode); 444 445 /* Start the controller! */ 446 jzlcd_start(sc); 447} 448 449static void 450jzlcd_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 451{ 452 if (error != 0) 453 return; 454 *(bus_addr_t *)arg = segs[0].ds_addr; 455} 456 457static int 458jzlcd_probe(device_t dev) 459{ 460 if (!ofw_bus_status_okay(dev)) 461 return (ENXIO); 462 463 if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-lcd")) 464 return (ENXIO); 465 466 device_set_desc(dev, "Ingenic JZ4780 LCD Controller"); 467 return (BUS_PROBE_DEFAULT); 468} 469 470static int 471jzlcd_attach(device_t dev) 472{ 473 struct jzlcd_softc *sc; 474 int error; 475 476 sc = device_get_softc(dev); 477 478 sc->dev = dev; 479 480 if (bus_alloc_resources(dev, jzlcd_spec, sc->res)) { 481 device_printf(dev, "cannot allocate resources for device\n"); 482 goto failed; 483 } 484 485 if (clk_get_by_ofw_name(dev, 0, "lcd_clk", &sc->clk) != 0 || 486 clk_get_by_ofw_name(dev, 0, "lcd_pixclk", &sc->clk_pix) != 0) { 487 device_printf(dev, "cannot get clocks\n"); 488 goto failed; 489 } 490 if (clk_enable(sc->clk) != 0 || clk_enable(sc->clk_pix) != 0) { 491 device_printf(dev, "cannot enable clocks\n"); 492 goto failed; 493 } 494 495 error = bus_dma_tag_create( 496 bus_get_dma_tag(dev), 497 sizeof(struct lcd_frame_descriptor), 0, 498 BUS_SPACE_MAXADDR_32BIT, 499 BUS_SPACE_MAXADDR, 500 NULL, NULL, 501 sizeof(struct lcd_frame_descriptor) * 2, 1, 502 sizeof(struct lcd_frame_descriptor) * 2, 503 0, 504 NULL, NULL, 505 &sc->fdesc_tag); 506 if (error != 0) { 507 device_printf(dev, "cannot create bus dma tag\n"); 508 goto failed; 509 } 510 511 error = bus_dmamem_alloc(sc->fdesc_tag, (void **)&sc->fdesc, 512 BUS_DMA_NOCACHE | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->fdesc_map); 513 if (error != 0) { 514 device_printf(dev, "cannot allocate dma descriptor\n"); 515 goto dmaalloc_failed; 516 } 517 518 error = bus_dmamap_load(sc->fdesc_tag, sc->fdesc_map, sc->fdesc, 519 sizeof(struct lcd_frame_descriptor) * 2, jzlcd_dmamap_cb, 520 &sc->fdesc_paddr, 0); 521 if (error != 0) { 522 device_printf(dev, "cannot load dma map\n"); 523 goto dmaload_failed; 524 } 525 526 sc->hdmi_evh = EVENTHANDLER_REGISTER(hdmi_event, 527 jzlcd_hdmi_event, sc, 0); 528 529 return (0); 530 531dmaload_failed: 532 bus_dmamem_free(sc->fdesc_tag, sc->fdesc, sc->fdesc_map); 533dmaalloc_failed: 534 bus_dma_tag_destroy(sc->fdesc_tag); 535failed: 536 if (sc->clk_pix != NULL) 537 clk_release(sc->clk); 538 if (sc->clk != NULL) 539 clk_release(sc->clk); 540 if (sc->res != NULL) 541 bus_release_resources(dev, jzlcd_spec, sc->res); 542 543 return (ENXIO); 544} 545 546static struct fb_info * 547jzlcd_fb_getinfo(device_t dev) 548{ 549 struct jzlcd_softc *sc; 550 551 sc = device_get_softc(dev); 552 553 return (&sc->info); 554} 555 556static device_method_t jzlcd_methods[] = { 557 /* Device interface */ 558 DEVMETHOD(device_probe, jzlcd_probe), 559 DEVMETHOD(device_attach, jzlcd_attach), 560 561 /* FB interface */ 562 DEVMETHOD(fb_getinfo, jzlcd_fb_getinfo), 563 564 DEVMETHOD_END 565}; 566 567static driver_t jzlcd_driver = { 568 "fb", 569 jzlcd_methods, 570 sizeof(struct jzlcd_softc), 571}; 572 573static devclass_t jzlcd_devclass; 574 575DRIVER_MODULE(fb, simplebus, jzlcd_driver, jzlcd_devclass, 0, 0); 576