a10_fb.c revision 296064
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: head/sys/arm/allwinner/a10_fb.c 296064 2016-02-25 20:17:18Z jmcneill $ 27 */ 28 29/* 30 * Allwinner A10/A20 Framebuffer 31 */ 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD: head/sys/arm/allwinner/a10_fb.c 296064 2016-02-25 20:17:18Z jmcneill $"); 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 <arm/allwinner/a10_clk.h> 58 59#include "fb_if.h" 60#include "hdmi_if.h" 61 62#define FB_DEFAULT_W 800 63#define FB_DEFAULT_H 600 64#define FB_DEFAULT_REF 60 65#define FB_BPP 32 66#define FB_ALIGN 0x1000 67 68#define HDMI_ENABLE_DELAY 20000 69 70#define DOT_CLOCK_TO_HZ(c) ((c) * 1000) 71 72/* Display backend */ 73#define DEBE_REG_START 0x800 74#define DEBE_REG_END 0x1000 75#define DEBE_REG_WIDTH 4 76#define DEBE_MODCTL 0x800 77#define MODCTL_ITLMOD_EN (1 << 28) 78#define MODCTL_OUT_SEL_MASK (0x7 << 20) 79#define MODCTL_OUT_SEL(sel) ((sel) << 20) 80#define OUT_SEL_LCD 0 81#define MODCTL_LAY0_EN (1 << 8) 82#define MODCTL_START_CTL (1 << 1) 83#define MODCTL_EN (1 << 0) 84#define DEBE_DISSIZE 0x808 85#define DIS_HEIGHT(h) (((h) - 1) << 16) 86#define DIS_WIDTH(w) (((w) - 1) << 0) 87#define DEBE_LAYSIZE0 0x810 88#define LAY_HEIGHT(h) (((h) - 1) << 16) 89#define LAY_WIDTH(w) (((w) - 1) << 0) 90#define DEBE_LAYCOOR0 0x820 91#define LAY_XCOOR(x) ((x) << 16) 92#define LAY_YCOOR(y) ((y) << 0) 93#define DEBE_LAYLINEWIDTH0 0x840 94#define DEBE_LAYFB_L32ADD0 0x850 95#define LAYFB_L32ADD(pa) ((pa) << 3) 96#define DEBE_LAYFB_H4ADD 0x860 97#define LAY0FB_H4ADD(pa) ((pa) >> 29) 98#define DEBE_REGBUFFCTL 0x870 99#define REGBUFFCTL_LOAD (1 << 0) 100#define DEBE_ATTCTL1 0x8a0 101#define ATTCTL1_FBFMT(fmt) ((fmt) << 8) 102#define FBFMT_XRGB8888 9 103#define ATTCTL1_FBPS(ps) ((ps) << 0) 104#define FBPS_32BPP_ARGB 0 105 106/* Timing controller */ 107#define TCON_GCTL 0x000 108#define GCTL_TCON_EN (1 << 31) 109#define GCTL_IO_MAP_SEL_TCON1 (1 << 0) 110#define TCON_GINT1 0x008 111#define GINT1_TCON1_LINENO(n) (((n) + 2) << 0) 112#define TCON0_DCLK 0x044 113#define DCLK_EN 0xf0000000 114#define TCON1_CTL 0x090 115#define TCON1_EN (1 << 31) 116#define INTERLACE_EN (1 << 20) 117#define TCON1_SRC_SEL(src) ((src) << 0) 118#define TCON1_SRC_CH1 0 119#define TCON1_SRC_CH2 1 120#define TCON1_SRC_BLUE 2 121#define TCON1_START_DELAY(sd) ((sd) << 4) 122#define TCON1_BASIC0 0x094 123#define TCON1_BASIC1 0x098 124#define TCON1_BASIC2 0x09c 125#define TCON1_BASIC3 0x0a0 126#define TCON1_BASIC4 0x0a4 127#define TCON1_BASIC5 0x0a8 128#define BASIC_X(x) (((x) - 1) << 16) 129#define BASIC_Y(y) (((y) - 1) << 0) 130#define BASIC3_HT(ht) (((ht) - 1) << 16) 131#define BASIC3_HBP(hbp) (((hbp) - 1) << 0) 132#define BASIC4_VT(vt) ((vt) << 16) 133#define BASIC4_VBP(vbp) (((vbp) - 1) << 0) 134#define BASIC5_HSPW(hspw) (((hspw) - 1) << 16) 135#define BASIC5_VSPW(vspw) (((vspw) - 1) << 0) 136#define TCON1_IO_POL 0x0f0 137#define IO_POL_IO2_INV (1 << 26) 138#define IO_POL_PHSYNC (1 << 25) 139#define IO_POL_PVSYNC (1 << 24) 140#define TCON1_IO_TRI 0x0f4 141#define IO0_OUTPUT_TRI_EN (1 << 24) 142#define IO1_OUTPUT_TRI_EN (1 << 25) 143#define IO_TRI_MASK 0xffffffff 144#define START_DELAY(vbl) (MIN(32, (vbl)) - 2) 145#define VBLANK_LEN(vt, vd, i) ((((vt) << (i)) >> 1) - (vd) - 2) 146#define VTOTAL(vt) ((vt) * 2) 147#define DIVIDE(x, y) (((x) + ((y) / 2)) / (y)) 148 149struct a10fb_softc { 150 device_t dev; 151 device_t fbdev; 152 struct resource *res[2]; 153 154 /* Framebuffer */ 155 struct fb_info info; 156 size_t fbsize; 157 bus_addr_t paddr; 158 vm_offset_t vaddr; 159 160 /* HDMI */ 161 eventhandler_tag hdmi_evh; 162}; 163 164static struct resource_spec a10fb_spec[] = { 165 { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* DEBE */ 166 { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* TCON */ 167 { -1, 0 } 168}; 169 170#define DEBE_READ(sc, reg) bus_read_4((sc)->res[0], (reg)) 171#define DEBE_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) 172 173#define TCON_READ(sc, reg) bus_read_4((sc)->res[1], (reg)) 174#define TCON_WRITE(sc, reg, val) bus_write_4((sc)->res[1], (reg), (val)) 175 176static int 177a10fb_allocfb(struct a10fb_softc *sc) 178{ 179 sc->vaddr = kmem_alloc_contig(kernel_arena, sc->fbsize, 180 M_NOWAIT | M_ZERO, 0, ~0, FB_ALIGN, 0, VM_MEMATTR_WRITE_COMBINING); 181 if (sc->vaddr == 0) { 182 device_printf(sc->dev, "failed to allocate FB memory\n"); 183 return (ENOMEM); 184 } 185 sc->paddr = pmap_kextract(sc->vaddr); 186 187 return (0); 188} 189 190static void 191a10fb_freefb(struct a10fb_softc *sc) 192{ 193 kmem_free(kernel_arena, sc->vaddr, sc->fbsize); 194} 195 196static void 197a10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode) 198{ 199 int width, height, interlace, reg; 200 uint32_t val; 201 202 interlace = !!(mode->flags & VID_INTERLACE); 203 width = mode->hdisplay; 204 height = mode->vdisplay << interlace; 205 206 /* Enable DEBE clocks */ 207 a10_clk_debe_activate(); 208 209 /* Initialize all registers to 0 */ 210 for (reg = DEBE_REG_START; reg < DEBE_REG_END; reg += DEBE_REG_WIDTH) 211 DEBE_WRITE(sc, reg, 0); 212 213 /* Enable display backend */ 214 DEBE_WRITE(sc, DEBE_MODCTL, MODCTL_EN); 215 216 /* Set display size */ 217 DEBE_WRITE(sc, DEBE_DISSIZE, DIS_HEIGHT(height) | DIS_WIDTH(width)); 218 219 /* Set layer 0 size, position, and stride */ 220 DEBE_WRITE(sc, DEBE_LAYSIZE0, LAY_HEIGHT(height) | LAY_WIDTH(width)); 221 DEBE_WRITE(sc, DEBE_LAYCOOR0, LAY_XCOOR(0) | LAY_YCOOR(0)); 222 DEBE_WRITE(sc, DEBE_LAYLINEWIDTH0, width * FB_BPP); 223 224 /* Point layer 0 to FB memory */ 225 DEBE_WRITE(sc, DEBE_LAYFB_L32ADD0, LAYFB_L32ADD(sc->paddr)); 226 DEBE_WRITE(sc, DEBE_LAYFB_H4ADD, LAY0FB_H4ADD(sc->paddr)); 227 228 /* Set backend format and pixel sequence */ 229 DEBE_WRITE(sc, DEBE_ATTCTL1, ATTCTL1_FBFMT(FBFMT_XRGB8888) | 230 ATTCTL1_FBPS(FBPS_32BPP_ARGB)); 231 232 /* Enable layer 0, output to LCD, setup interlace */ 233 val = DEBE_READ(sc, DEBE_MODCTL); 234 val |= MODCTL_LAY0_EN; 235 val &= ~MODCTL_OUT_SEL_MASK; 236 val |= MODCTL_OUT_SEL(OUT_SEL_LCD); 237 if (interlace) 238 val |= MODCTL_ITLMOD_EN; 239 else 240 val &= ~MODCTL_ITLMOD_EN; 241 DEBE_WRITE(sc, DEBE_MODCTL, val); 242 243 /* Commit settings */ 244 DEBE_WRITE(sc, DEBE_REGBUFFCTL, REGBUFFCTL_LOAD); 245 246 /* Start DEBE */ 247 val = DEBE_READ(sc, DEBE_MODCTL); 248 val |= MODCTL_START_CTL; 249 DEBE_WRITE(sc, DEBE_MODCTL, val); 250} 251 252static void 253a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode) 254{ 255 u_int interlace, hspw, hbp, vspw, vbp, vbl, width, height, start_delay; 256 u_int vtotal, framerate, clk; 257 uint32_t val; 258 259 interlace = !!(mode->flags & VID_INTERLACE); 260 width = mode->hdisplay; 261 height = mode->vdisplay; 262 hspw = mode->hsync_end - mode->hsync_start; 263 hbp = mode->htotal - mode->hsync_start; 264 vspw = mode->vsync_end - mode->vsync_start; 265 vbp = mode->vtotal - mode->vsync_start; 266 vbl = VBLANK_LEN(mode->vtotal, mode->vdisplay, interlace); 267 start_delay = START_DELAY(vbl); 268 269 /* Enable LCD clocks */ 270 a10_clk_lcd_activate(); 271 272 /* Disable TCON and TCON1 */ 273 TCON_WRITE(sc, TCON_GCTL, 0); 274 TCON_WRITE(sc, TCON1_CTL, 0); 275 276 /* Enable clocks */ 277 TCON_WRITE(sc, TCON0_DCLK, DCLK_EN); 278 279 /* Disable IO and data output ports */ 280 TCON_WRITE(sc, TCON1_IO_TRI, IO_TRI_MASK); 281 282 /* Disable TCON and select TCON1 */ 283 TCON_WRITE(sc, TCON_GCTL, GCTL_IO_MAP_SEL_TCON1); 284 285 /* Source width and height */ 286 TCON_WRITE(sc, TCON1_BASIC0, BASIC_X(width) | BASIC_Y(height)); 287 /* Scaler width and height */ 288 TCON_WRITE(sc, TCON1_BASIC1, BASIC_X(width) | BASIC_Y(height)); 289 /* Output width and height */ 290 TCON_WRITE(sc, TCON1_BASIC2, BASIC_X(width) | BASIC_Y(height)); 291 /* Horizontal total and back porch */ 292 TCON_WRITE(sc, TCON1_BASIC3, BASIC3_HT(mode->htotal) | BASIC3_HBP(hbp)); 293 /* Vertical total and back porch */ 294 vtotal = VTOTAL(mode->vtotal); 295 if (interlace) { 296 framerate = DIVIDE(DIVIDE(DOT_CLOCK_TO_HZ(mode->dot_clock), 297 mode->htotal), mode->vtotal); 298 clk = mode->htotal * (VTOTAL(mode->vtotal) + 1) * framerate; 299 if ((clk / 2) == DOT_CLOCK_TO_HZ(mode->dot_clock)) 300 vtotal += 1; 301 } 302 TCON_WRITE(sc, TCON1_BASIC4, BASIC4_VT(vtotal) | BASIC4_VBP(vbp)); 303 /* Horizontal and vertical sync */ 304 TCON_WRITE(sc, TCON1_BASIC5, BASIC5_HSPW(hspw) | BASIC5_VSPW(vspw)); 305 /* Polarity */ 306 val = IO_POL_IO2_INV; 307 if (mode->flags & VID_PHSYNC) 308 val |= IO_POL_PHSYNC; 309 if (mode->flags & VID_PVSYNC) 310 val |= IO_POL_PVSYNC; 311 TCON_WRITE(sc, TCON1_IO_POL, val); 312 313 /* Set scan line for TCON1 line trigger */ 314 TCON_WRITE(sc, TCON_GINT1, GINT1_TCON1_LINENO(start_delay)); 315 316 /* Enable TCON1 */ 317 val = TCON1_EN; 318 if (interlace) 319 val |= INTERLACE_EN; 320 val |= TCON1_START_DELAY(start_delay); 321 val |= TCON1_SRC_SEL(TCON1_SRC_CH1); 322 TCON_WRITE(sc, TCON1_CTL, val); 323 324 /* Setup PLL */ 325 a10_clk_tcon_activate(DOT_CLOCK_TO_HZ(mode->dot_clock)); 326} 327 328static void 329a10fb_enable_tcon(struct a10fb_softc *sc, int onoff) 330{ 331 uint32_t val; 332 333 /* Enable TCON */ 334 val = TCON_READ(sc, TCON_GCTL); 335 if (onoff) 336 val |= GCTL_TCON_EN; 337 else 338 val &= ~GCTL_TCON_EN; 339 TCON_WRITE(sc, TCON_GCTL, val); 340 341 /* Enable TCON1 IO0/IO1 outputs */ 342 val = TCON_READ(sc, TCON1_IO_TRI); 343 if (onoff) 344 val &= ~(IO0_OUTPUT_TRI_EN | IO1_OUTPUT_TRI_EN); 345 else 346 val |= (IO0_OUTPUT_TRI_EN | IO1_OUTPUT_TRI_EN); 347 TCON_WRITE(sc, TCON1_IO_TRI, val); 348} 349 350static int 351a10fb_configure(struct a10fb_softc *sc, const struct videomode *mode) 352{ 353 size_t fbsize; 354 int error; 355 356 fbsize = round_page(mode->hdisplay * mode->vdisplay * (FB_BPP / NBBY)); 357 358 /* Detach the old FB device */ 359 if (sc->fbdev != NULL) { 360 device_delete_child(sc->dev, sc->fbdev); 361 sc->fbdev = NULL; 362 } 363 364 /* If the FB size has changed, free the old FB memory */ 365 if (sc->fbsize > 0 && sc->fbsize != fbsize) { 366 a10fb_freefb(sc); 367 sc->vaddr = 0; 368 } 369 370 /* Allocate the FB if necessary */ 371 sc->fbsize = fbsize; 372 if (sc->vaddr == 0) { 373 error = a10fb_allocfb(sc); 374 if (error != 0) { 375 device_printf(sc->dev, "failed to allocate FB memory\n"); 376 return (ENXIO); 377 } 378 } 379 380 /* Setup display backend */ 381 a10fb_setup_debe(sc, mode); 382 383 /* Setup display timing controller */ 384 a10fb_setup_tcon(sc, mode); 385 386 /* Attach framebuffer device */ 387 sc->info.fb_name = device_get_nameunit(sc->dev); 388 sc->info.fb_vbase = (intptr_t)sc->vaddr; 389 sc->info.fb_pbase = sc->paddr; 390 sc->info.fb_size = sc->fbsize; 391 sc->info.fb_bpp = sc->info.fb_depth = FB_BPP; 392 sc->info.fb_stride = mode->hdisplay * (FB_BPP / NBBY); 393 sc->info.fb_width = mode->hdisplay; 394 sc->info.fb_height = mode->vdisplay; 395 396 sc->fbdev = device_add_child(sc->dev, "fbd", device_get_unit(sc->dev)); 397 if (sc->fbdev == NULL) { 398 device_printf(sc->dev, "failed to add fbd child\n"); 399 return (ENOENT); 400 } 401 402 error = device_probe_and_attach(sc->fbdev); 403 if (error != 0) { 404 device_printf(sc->dev, "failed to attach fbd device\n"); 405 return (error); 406 } 407 408 return (0); 409} 410 411static void 412a10fb_hdmi_event(void *arg, device_t hdmi_dev) 413{ 414 const struct videomode *mode; 415 struct videomode hdmi_mode; 416 struct a10fb_softc *sc; 417 struct edid_info ei; 418 uint8_t *edid; 419 uint32_t edid_len; 420 int error; 421 422 sc = arg; 423 edid = NULL; 424 edid_len = 0; 425 mode = NULL; 426 427 error = HDMI_GET_EDID(hdmi_dev, &edid, &edid_len); 428 if (error != 0) { 429 device_printf(sc->dev, "failed to get EDID: %d\n", error); 430 } else { 431 error = edid_parse(edid, &ei); 432 if (error != 0) { 433 device_printf(sc->dev, "failed to parse EDID: %d\n", 434 error); 435 } else { 436 if (bootverbose) 437 edid_print(&ei); 438 mode = ei.edid_preferred_mode; 439 } 440 } 441 442 /* If the preferred mode could not be determined, use the default */ 443 if (mode == NULL) 444 mode = pick_mode_by_ref(FB_DEFAULT_W, FB_DEFAULT_H, 445 FB_DEFAULT_REF); 446 447 if (mode == NULL) { 448 device_printf(sc->dev, "failed to find usable video mode\n"); 449 return; 450 } 451 452 if (bootverbose) 453 device_printf(sc->dev, "using %dx%d\n", 454 mode->hdisplay, mode->vdisplay); 455 456 /* Disable HDMI */ 457 HDMI_ENABLE(hdmi_dev, 0); 458 459 /* Disable timing controller */ 460 a10fb_enable_tcon(sc, 0); 461 462 /* Configure DEBE and TCON */ 463 error = a10fb_configure(sc, mode); 464 if (error != 0) { 465 device_printf(sc->dev, "failed to configure FB: %d\n", error); 466 return; 467 } 468 469 hdmi_mode = *mode; 470 hdmi_mode.hskew = mode->hsync_end - mode->hsync_start; 471 hdmi_mode.flags |= VID_HSKEW; 472 HDMI_SET_VIDEOMODE(hdmi_dev, &hdmi_mode); 473 474 /* Enable timing controller */ 475 a10fb_enable_tcon(sc, 1); 476 477 DELAY(HDMI_ENABLE_DELAY); 478 479 /* Enable HDMI */ 480 HDMI_ENABLE(hdmi_dev, 1); 481} 482 483static int 484a10fb_probe(device_t dev) 485{ 486 if (!ofw_bus_status_okay(dev)) 487 return (ENXIO); 488 489 if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-fb")) 490 return (ENXIO); 491 492 device_set_desc(dev, "Allwinner Framebuffer"); 493 return (BUS_PROBE_DEFAULT); 494} 495 496static int 497a10fb_attach(device_t dev) 498{ 499 struct a10fb_softc *sc; 500 501 sc = device_get_softc(dev); 502 503 sc->dev = dev; 504 505 if (bus_alloc_resources(dev, a10fb_spec, sc->res)) { 506 device_printf(dev, "cannot allocate resources for device\n"); 507 return (ENXIO); 508 } 509 510 sc->hdmi_evh = EVENTHANDLER_REGISTER(hdmi_event, 511 a10fb_hdmi_event, sc, 0); 512 513 return (0); 514} 515 516static struct fb_info * 517a10fb_fb_getinfo(device_t dev) 518{ 519 struct a10fb_softc *sc; 520 521 sc = device_get_softc(dev); 522 523 return (&sc->info); 524} 525 526static device_method_t a10fb_methods[] = { 527 /* Device interface */ 528 DEVMETHOD(device_probe, a10fb_probe), 529 DEVMETHOD(device_attach, a10fb_attach), 530 531 /* FB interface */ 532 DEVMETHOD(fb_getinfo, a10fb_fb_getinfo), 533 534 DEVMETHOD_END 535}; 536 537static driver_t a10fb_driver = { 538 "fb", 539 a10fb_methods, 540 sizeof(struct a10fb_softc), 541}; 542 543static devclass_t a10fb_devclass; 544 545DRIVER_MODULE(fb, simplebus, a10fb_driver, a10fb_devclass, 0, 0); 546