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