1 /* $NetBSD: sunxi_debe.c,v 1.9 2018/06/01 17:18:44 bouyer Exp $ */ 2 3 /*- 4 * Copyright (c) 2018 Manuel Bouyer <bouyer@antioche.eu.org> 5 * All rights reserved. 6 * 7 * Copyright (c) 2014 Jared D. McNeill <jmcneill@invisible.ca> 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include "genfb.h" 33 34 #ifndef SUNXI_DEBE_VIDEOMEM 35 #define SUNXI_DEBE_VIDEOMEM (16 * 1024 * 1024) 36 #endif 37 38 #define SUNXI_DEBE_CURMAX 64 39 40 #include <sys/cdefs.h> 41 __KERNEL_RCSID(0, "$NetBSD: sunxi_debe.c,v 1.9 2018/06/01 17:18:44 bouyer Exp $"); 42 43 #include <sys/param.h> 44 #include <sys/bus.h> 45 #include <sys/device.h> 46 #include <sys/intr.h> 47 #include <sys/systm.h> 48 #include <sys/kernel.h> 49 #include <sys/mutex.h> 50 #include <sys/condvar.h> 51 52 #include <dev/fdt/fdtvar.h> 53 #include <dev/fdt/fdt_port.h> 54 55 #include <dev/videomode/videomode.h> 56 #include <dev/wscons/wsconsio.h> 57 #include <dev/wsfb/genfbvar.h> 58 59 #include <arm/sunxi/sunxi_debereg.h> 60 #include <arm/sunxi/sunxi_display.h> 61 #include <arm/sunxi/sunxi_platform.h> 62 #include <machine/bootconfig.h> 63 64 enum sunxi_debe_type { 65 DEBE_A10 = 1, 66 }; 67 68 struct sunxi_debe_softc { 69 device_t sc_dev; 70 device_t sc_fbdev; 71 enum sunxi_debe_type sc_type; 72 bus_space_tag_t sc_bst; 73 bus_space_handle_t sc_bsh; 74 bus_dma_tag_t sc_dmat; 75 76 struct clk *sc_clk_ahb; 77 struct clk *sc_clk_mod; 78 struct clk *sc_clk_ram; 79 80 struct fdtbus_reset *sc_rst; 81 82 bus_dma_segment_t sc_dmasegs[1]; 83 bus_size_t sc_dmasize; 84 bus_dmamap_t sc_dmamap; 85 void *sc_dmap; 86 87 bool sc_cursor_enable; 88 int sc_cursor_x, sc_cursor_y; 89 int sc_hot_x, sc_hot_y; 90 uint8_t sc_cursor_bitmap[8 * SUNXI_DEBE_CURMAX]; 91 uint8_t sc_cursor_mask[8 * SUNXI_DEBE_CURMAX]; 92 93 int sc_phandle; 94 struct fdt_device_ports sc_ports; 95 struct fdt_endpoint *sc_out_ep; 96 int sc_unit; /* debe0 or debe1 */ 97 }; 98 99 #define DEBE_READ(sc, reg) \ 100 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 101 #define DEBE_WRITE(sc, reg, val) \ 102 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 103 104 static const struct of_compat_data compat_data[] = { 105 {"allwinner,sun4i-a10-display-backend", DEBE_A10}, 106 {"allwinner,sun7i-a20-display-backend", DEBE_A10}, 107 {NULL} 108 }; 109 110 struct sunxifb_attach_args { 111 void *afb_fb; 112 uint32_t afb_width; 113 uint32_t afb_height; 114 bus_dma_tag_t afb_dmat; 115 bus_dma_segment_t *afb_dmasegs; 116 int afb_ndmasegs; 117 }; 118 119 static void sunxi_debe_ep_connect(device_t, struct fdt_endpoint *, bool); 120 static int sunxi_debe_ep_enable(device_t, struct fdt_endpoint *, bool); 121 static int sunxi_debe_match(device_t, cfdata_t, void *); 122 static void sunxi_debe_attach(device_t, device_t, void *); 123 124 static int sunxi_debe_alloc_videomem(struct sunxi_debe_softc *); 125 static void sunxi_debe_setup_fbdev(struct sunxi_debe_softc *, 126 const struct videomode *); 127 128 static int sunxi_debe_set_curpos(struct sunxi_debe_softc *, int, int); 129 static int sunxi_debe_set_cursor(struct sunxi_debe_softc *, 130 struct wsdisplay_cursor *); 131 static int sunxi_debe_ioctl(device_t, u_long, void *); 132 static void sunxi_befb_set_videomode(device_t, u_int, u_int); 133 void sunxi_debe_dump_regs(int); 134 135 static struct sunxi_debe_softc *debe_console_sc; 136 static int sunxi_simplefb_phandle = -1; 137 138 CFATTACH_DECL_NEW(sunxi_debe, sizeof(struct sunxi_debe_softc), 139 sunxi_debe_match, sunxi_debe_attach, NULL, NULL); 140 141 static int 142 sunxi_debe_match(device_t parent, cfdata_t cf, void *aux) 143 { 144 struct fdt_attach_args * const faa = aux; 145 146 return of_match_compat_data(faa->faa_phandle, compat_data); 147 } 148 149 static void 150 sunxi_debe_attach(device_t parent, device_t self, void *aux) 151 { 152 struct sunxi_debe_softc *sc = device_private(self); 153 struct fdt_attach_args * const faa = aux; 154 const int phandle = faa->faa_phandle; 155 bus_addr_t addr; 156 bus_size_t size; 157 int error; 158 159 sc->sc_dev = self; 160 sc->sc_phandle = phandle; 161 sc->sc_bst = faa->faa_bst; 162 sc->sc_dmat = faa->faa_dmat; 163 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 164 aprint_error(": couldn't get registers\n"); 165 } 166 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 167 aprint_error(": couldn't map registers\n"); 168 return; 169 } 170 171 sc->sc_clk_ahb = fdtbus_clock_get(phandle, "ahb"); 172 sc->sc_clk_mod = fdtbus_clock_get(phandle, "mod"); 173 sc->sc_clk_ram = fdtbus_clock_get(phandle, "ram"); 174 175 if (sc->sc_clk_ahb == NULL || sc->sc_clk_mod == NULL 176 || sc->sc_clk_ram == NULL) { 177 aprint_error(": couldn't get clocks\n"); 178 aprint_debug_dev(self, "clk ahb %s mod %s ram %s\n", 179 sc->sc_clk_ahb == NULL ? "missing" : "present", 180 sc->sc_clk_mod == NULL ? "missing" : "present", 181 sc->sc_clk_ram == NULL ? "missing" : "present"); 182 return; 183 } 184 185 sc->sc_rst = fdtbus_reset_get_index(phandle, 0); 186 if (sc->sc_rst == NULL) { 187 aprint_error(": couldn't get reset\n"); 188 return; 189 } 190 191 sc->sc_type = of_search_compatible(faa->faa_phandle, compat_data)->data; 192 193 aprint_naive("\n"); 194 aprint_normal(": Display Engine Backend (%s)\n", 195 fdtbus_get_string(phandle, "name")); 196 197 198 sc->sc_dmasize = SUNXI_DEBE_VIDEOMEM; 199 200 error = sunxi_debe_alloc_videomem(sc); 201 if (error) { 202 aprint_error_dev(sc->sc_dev, 203 "couldn't allocate video memory, error = %d\n", error); 204 return; 205 } 206 207 sc->sc_unit = -1; 208 sc->sc_ports.dp_ep_connect = sunxi_debe_ep_connect; 209 sc->sc_ports.dp_ep_enable = sunxi_debe_ep_enable; 210 fdt_ports_register(&sc->sc_ports, self, phandle, EP_OTHER); 211 } 212 213 static void 214 sunxi_debe_doreset(void) 215 { 216 device_t dev; 217 struct sunxi_debe_softc *sc; 218 int error; 219 220 for (int i = 0;;i++) { 221 dev = device_find_by_driver_unit("sunxidebe", i); 222 if (dev == NULL) 223 return; 224 sc = device_private(dev); 225 226 if (fdtbus_reset_assert(sc->sc_rst) != 0) { 227 aprint_error_dev(dev, ": couldn't assert reset\n"); 228 return; 229 } 230 delay(1); 231 if (fdtbus_reset_deassert(sc->sc_rst) != 0) { 232 aprint_error_dev(dev, ": couldn't de-assert reset\n"); 233 return; 234 } 235 236 237 error = clk_set_rate(sc->sc_clk_mod, 300000000); 238 if (error) { 239 aprint_error_dev(dev, 240 "couln't set mod clock rate (%d)\n", error); 241 return; 242 } 243 244 if (clk_enable(sc->sc_clk_ahb) != 0 || 245 clk_enable(sc->sc_clk_mod) != 0) { 246 aprint_error_dev(dev, ": couldn't enable clocks\n"); 247 return; 248 } 249 if (clk_disable(sc->sc_clk_ram) != 0) { 250 aprint_error_dev(dev, ": couldn't disable ram clock\n"); 251 } 252 253 for (unsigned int reg = 0x800; reg < 0x1000; reg += 4) { 254 DEBE_WRITE(sc, reg, 0); 255 } 256 257 DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, SUNXI_DEBE_MODCTL_EN); 258 259 DEBE_WRITE(sc, SUNXI_DEBE_HWC_PALETTE_TABLE, 0); 260 261 if (clk_disable(sc->sc_clk_ahb) != 0 || 262 clk_disable(sc->sc_clk_mod) != 0) { 263 aprint_error_dev(sc->sc_dev, 264 ": couldn't disable clocks\n"); 265 } 266 } 267 } 268 269 static void 270 sunxi_debe_ep_connect(device_t self, struct fdt_endpoint *ep, bool connect) 271 { 272 struct sunxi_debe_softc *sc = device_private(self); 273 struct fdt_endpoint *rep = fdt_endpoint_remote(ep); 274 int rep_idx = fdt_endpoint_index(rep); 275 276 KASSERT(device_is_a(self, "sunxidebe")); 277 if (!connect) { 278 aprint_error_dev(self, "endpoint disconnect not supported\n"); 279 return; 280 } 281 282 if (fdt_endpoint_port_index(ep) == 1) { 283 bool do_print = (sc->sc_unit == -1); 284 /* 285 * one of our output endpoints has been connected. 286 * the remote id is our unit number 287 */ 288 if (sc->sc_unit != -1 && rep_idx != -1 && 289 sc->sc_unit != rep_idx) { 290 aprint_error_dev(self, ": remote id %d doens't match" 291 " discovered unit number %d\n", 292 rep_idx, sc->sc_unit); 293 return; 294 } 295 if (!device_is_a(fdt_endpoint_device(rep), "sunxitcon")) { 296 aprint_error_dev(self, 297 ": output %d connected to unknown device\n", 298 fdt_endpoint_index(ep)); 299 return; 300 } 301 if (rep_idx != -1) 302 sc->sc_unit = rep_idx; 303 else { 304 /* assume only one debe */ 305 sc->sc_unit = 0; 306 } 307 if (do_print) 308 aprint_verbose_dev(self, "debe unit %d\n", sc->sc_unit); 309 } 310 } 311 312 static int 313 sunxi_debe_alloc_videomem(struct sunxi_debe_softc *sc) 314 { 315 int error, nsegs; 316 317 error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmasize, 0x1000, 0, 318 sc->sc_dmasegs, 1, &nsegs, BUS_DMA_WAITOK); 319 if (error) 320 return error; 321 error = bus_dmamem_map(sc->sc_dmat, sc->sc_dmasegs, nsegs, 322 sc->sc_dmasize, &sc->sc_dmap, BUS_DMA_WAITOK | BUS_DMA_COHERENT); 323 if (error) 324 goto free; 325 error = bus_dmamap_create(sc->sc_dmat, sc->sc_dmasize, 1, 326 sc->sc_dmasize, 0, BUS_DMA_WAITOK, &sc->sc_dmamap); 327 if (error) 328 goto unmap; 329 error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_dmap, 330 sc->sc_dmasize, NULL, BUS_DMA_WAITOK); 331 if (error) 332 goto destroy; 333 334 memset(sc->sc_dmap, 0, sc->sc_dmasize); 335 336 return 0; 337 338 destroy: 339 bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); 340 unmap: 341 bus_dmamem_unmap(sc->sc_dmat, sc->sc_dmap, sc->sc_dmasize); 342 free: 343 bus_dmamem_free(sc->sc_dmat, sc->sc_dmasegs, nsegs); 344 345 sc->sc_dmasize = 0; 346 sc->sc_dmap = NULL; 347 348 return error; 349 } 350 351 static void 352 sunxi_debe_setup_fbdev(struct sunxi_debe_softc *sc, const struct videomode *mode) 353 { 354 if (mode == NULL) 355 return; 356 357 const u_int interlace_p = !!(mode->flags & VID_INTERLACE); 358 const u_int fb_width = mode->hdisplay; 359 const u_int fb_height = (mode->vdisplay << interlace_p); 360 361 if (mode && sc->sc_fbdev == NULL) { 362 /* see if we are the console */ 363 if (sunxi_simplefb_phandle >= 0) { 364 const char *cons_pipeline = 365 fdtbus_get_string(sunxi_simplefb_phandle, 366 "allwinner,pipeline"); 367 struct fdt_endpoint *ep = fdt_endpoint_get_from_index( 368 &sc->sc_ports, SUNXI_PORT_OUTPUT, sc->sc_unit); 369 struct fdt_endpoint *rep = fdt_endpoint_remote(ep); 370 if (sunxi_tcon_is_console( 371 fdt_endpoint_device(rep), cons_pipeline)) 372 debe_console_sc = sc; 373 } else if (debe_console_sc == NULL) { 374 if (match_bootconf_option(boot_args, 375 "console", "fb0")) { 376 if (sc->sc_unit == 0) 377 debe_console_sc = sc; 378 } else if (match_bootconf_option(boot_args, 379 "console", "fb1")) { 380 if (sc->sc_unit == 1) 381 debe_console_sc = sc; 382 } else if (match_bootconf_option(boot_args, 383 "console", "fb")) { 384 /* match first activated */ 385 debe_console_sc = sc; 386 } 387 } 388 struct sunxifb_attach_args afb = { 389 .afb_fb = sc->sc_dmap, 390 .afb_width = fb_width, 391 .afb_height = fb_height, 392 .afb_dmat = sc->sc_dmat, 393 .afb_dmasegs = sc->sc_dmasegs, 394 .afb_ndmasegs = 1 395 }; 396 sc->sc_fbdev = config_found_ia(sc->sc_dev, "sunxidebe", 397 &afb, NULL); 398 } else if (sc->sc_fbdev != NULL) { 399 sunxi_befb_set_videomode(sc->sc_fbdev, fb_width, fb_height); 400 } 401 } 402 403 static int 404 sunxi_debe_set_curpos(struct sunxi_debe_softc *sc, int x, int y) 405 { 406 int xx, yy; 407 u_int yoff, xoff; 408 409 xoff = yoff = 0; 410 xx = x - sc->sc_hot_x; 411 yy = y - sc->sc_hot_y; 412 if (xx < 0) { 413 xoff -= xx; 414 xx = 0; 415 } 416 if (yy < 0) { 417 yoff -= yy; 418 yy = 0; 419 } 420 421 DEBE_WRITE(sc, SUNXI_DEBE_HWCCTL_REG, 422 __SHIFTIN(yy, SUNXI_DEBE_HWCCTL_YCOOR) | 423 __SHIFTIN(xx, SUNXI_DEBE_HWCCTL_XCOOR)); 424 DEBE_WRITE(sc, SUNXI_DEBE_HWCFBCTL_REG, 425 #if SUNXI_DEBE_CURMAX == 32 426 __SHIFTIN(SUNXI_DEBE_HWCFBCTL_YSIZE_32, SUNXI_DEBE_HWCFBCTL_YSIZE) | 427 __SHIFTIN(SUNXI_DEBE_HWCFBCTL_XSIZE_32, SUNXI_DEBE_HWCFBCTL_XSIZE) | 428 #else 429 __SHIFTIN(SUNXI_DEBE_HWCFBCTL_YSIZE_64, SUNXI_DEBE_HWCFBCTL_YSIZE) | 430 __SHIFTIN(SUNXI_DEBE_HWCFBCTL_XSIZE_64, SUNXI_DEBE_HWCFBCTL_XSIZE) | 431 #endif 432 __SHIFTIN(SUNXI_DEBE_HWCFBCTL_FBFMT_2BPP, SUNXI_DEBE_HWCFBCTL_FBFMT) | 433 __SHIFTIN(yoff, SUNXI_DEBE_HWCFBCTL_YCOOROFF) | 434 __SHIFTIN(xoff, SUNXI_DEBE_HWCFBCTL_XCOOROFF)); 435 436 return 0; 437 } 438 439 static int 440 sunxi_debe_set_cursor(struct sunxi_debe_softc *sc, struct wsdisplay_cursor *cur) 441 { 442 uint32_t val; 443 uint8_t r[4], g[4], b[4]; 444 u_int index, count, shift, off, pcnt; 445 int i, j, idx, error; 446 uint8_t mask; 447 448 if (cur->which & WSDISPLAY_CURSOR_DOCUR) { 449 val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG); 450 if (cur->enable) 451 val |= SUNXI_DEBE_MODCTL_HWC_EN; 452 else 453 val &= ~SUNXI_DEBE_MODCTL_HWC_EN; 454 DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, val); 455 456 sc->sc_cursor_enable = cur->enable; 457 } 458 459 if (cur->which & WSDISPLAY_CURSOR_DOHOT) { 460 sc->sc_hot_x = cur->hot.x; 461 sc->sc_hot_y = cur->hot.y; 462 cur->which |= WSDISPLAY_CURSOR_DOPOS; 463 } 464 465 if (cur->which & WSDISPLAY_CURSOR_DOPOS) { 466 sunxi_debe_set_curpos(sc, cur->pos.x, cur->pos.y); 467 } 468 469 if (cur->which & WSDISPLAY_CURSOR_DOCMAP) { 470 index = cur->cmap.index; 471 count = cur->cmap.count; 472 if (index >= 2 || count > 2 - index) 473 return EINVAL; 474 error = copyin(cur->cmap.red, &r[index], count); 475 if (error) 476 return error; 477 error = copyin(cur->cmap.green, &g[index], count); 478 if (error) 479 return error; 480 error = copyin(cur->cmap.blue, &b[index], count); 481 if (error) 482 return error; 483 484 for (i = index; i < (index + count); i++) { 485 DEBE_WRITE(sc, 486 SUNXI_DEBE_HWC_PALETTE_TABLE + (4 * (i + 2)), 487 (r[i] << 16) | (g[i] << 8) | b[i] | 0xff000000); 488 } 489 } 490 491 if (cur->which & WSDISPLAY_CURSOR_DOSHAPE) { 492 error = copyin(cur->mask, sc->sc_cursor_mask, 493 SUNXI_DEBE_CURMAX * 8); 494 if (error) 495 return error; 496 error = copyin(cur->image, sc->sc_cursor_bitmap, 497 SUNXI_DEBE_CURMAX * 8); 498 if (error) 499 return error; 500 } 501 502 if (cur->which & (WSDISPLAY_CURSOR_DOCMAP|WSDISPLAY_CURSOR_DOSHAPE)) { 503 for (i = 0, pcnt = 0; i < SUNXI_DEBE_CURMAX * 8; i++) { 504 for (j = 0, mask = 1; j < 8; j++, mask <<= 1, pcnt++) { 505 idx = ((sc->sc_cursor_mask[i] & mask) ? 2 : 0) | 506 ((sc->sc_cursor_bitmap[i] & mask) ? 1 : 0); 507 off = (pcnt >> 4) * 4; 508 shift = (pcnt & 0xf) * 2; 509 val = DEBE_READ(sc, 510 SUNXI_DEBE_HWC_PATTERN_BLOCK + off); 511 val &= ~(3 << shift); 512 val |= (idx << shift); 513 DEBE_WRITE(sc, 514 SUNXI_DEBE_HWC_PATTERN_BLOCK + off, val); 515 } 516 } 517 } 518 519 return 0; 520 } 521 522 static int 523 sunxi_debe_ep_enable(device_t dev, struct fdt_endpoint *ep, bool enable) 524 { 525 struct sunxi_debe_softc *sc; 526 uint32_t val; 527 528 KASSERT(device_is_a(dev, "sunxidebe")); 529 sc = device_private(dev); 530 531 if (enable) { 532 if (clk_enable(sc->sc_clk_ram) != 0) { 533 device_printf(dev, 534 ": warning: failed to enable ram clock\n"); 535 } 536 val = DEBE_READ(sc, SUNXI_DEBE_REGBUFFCTL_REG); 537 val |= SUNXI_DEBE_REGBUFFCTL_REGLOADCTL; 538 DEBE_WRITE(sc, SUNXI_DEBE_REGBUFFCTL_REG, val); 539 540 val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG); 541 val |= SUNXI_DEBE_MODCTL_START_CTL; 542 DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, val); 543 #ifdef SUNXI_DEBE_DEBUG 544 sunxi_debe_dump_regs(sc->sc_unit); 545 #endif 546 } else { 547 val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG); 548 val &= ~SUNXI_DEBE_MODCTL_START_CTL; 549 DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, val); 550 if (clk_disable(sc->sc_clk_ram) != 0) { 551 device_printf(dev, 552 ": warning: failed to disable ram clock\n"); 553 } 554 } 555 #if 0 556 for (int i = 0; i < 0x1000; i += 4) { 557 printf("DEBE 0x%04x: 0x%08x\n", i, DEBE_READ(sc, i)); 558 } 559 #endif 560 return 0; 561 } 562 563 void 564 sunxi_debe_set_videomode(device_t dev, const struct videomode *mode) 565 { 566 struct sunxi_debe_softc *sc; 567 uint32_t val; 568 569 KASSERT(device_is_a(dev, "sunxidebe")); 570 sc = device_private(dev); 571 572 if (mode) { 573 const u_int interlace_p = !!(mode->flags & VID_INTERLACE); 574 const u_int width = mode->hdisplay; 575 const u_int height = (mode->vdisplay << interlace_p); 576 const u_int fb_width = width; 577 const u_int fb_height = height; 578 uint32_t vmem = width * height * 4; 579 580 if (vmem > sc->sc_dmasize) { 581 device_printf(sc->sc_dev, 582 "not enough memory for %ux%u fb (req %u have %u)\n", 583 width, height, vmem, (unsigned int)sc->sc_dmasize); 584 return; 585 } 586 587 paddr_t pa = sc->sc_dmamap->dm_segs[0].ds_addr; 588 #if !defined(ALLWINNER_A80) && 0 589 #define SUNXI_SDRAM_PBASE-0 0x40000000 590 /* 591 * On 2GB systems, we need to subtract AWIN_SDRAM_PBASE from 592 * the phys addr. 593 */ 594 if (pa >= SUNXI_SDRAM_PBASE) 595 pa -= SUNXI_SDRAM_PBASE; 596 #endif 597 598 /* notify fb */ 599 sunxi_debe_setup_fbdev(sc, mode); 600 601 DEBE_WRITE(sc, SUNXI_DEBE_DISSIZE_REG, 602 ((height - 1) << 16) | (width - 1)); 603 DEBE_WRITE(sc, SUNXI_DEBE_LAYSIZE_REG, 604 ((fb_height - 1) << 16) | (fb_width - 1)); 605 DEBE_WRITE(sc, SUNXI_DEBE_LAYCOOR_REG, 0); 606 DEBE_WRITE(sc, SUNXI_DEBE_LAYLINEWIDTH_REG, (fb_width << 5)); 607 DEBE_WRITE(sc, SUNXI_DEBE_LAYFB_L32ADD_REG, pa << 3); 608 DEBE_WRITE(sc, SUNXI_DEBE_LAYFB_H4ADD_REG, pa >> 29); 609 610 val = DEBE_READ(sc, SUNXI_DEBE_ATTCTL1_REG); 611 val &= ~SUNXI_DEBE_ATTCTL1_LAY_FBFMT; 612 val |= __SHIFTIN(SUNXI_DEBE_ATTCTL1_LAY_FBFMT_XRGB8888, 613 SUNXI_DEBE_ATTCTL1_LAY_FBFMT); 614 val &= ~SUNXI_DEBE_ATTCTL1_LAY_BRSWAPEN; 615 val &= ~SUNXI_DEBE_ATTCTL1_LAY_FBPS; 616 #if __ARMEB__ 617 val |= __SHIFTIN(SUNXI_DEBE_ATTCTL1_LAY_FBPS_32BPP_BGRA, 618 SUNXI_DEBE_ATTCTL1_LAY_FBPS); 619 #else 620 val |= __SHIFTIN(SUNXI_DEBE_ATTCTL1_LAY_FBPS_32BPP_ARGB, 621 SUNXI_DEBE_ATTCTL1_LAY_FBPS); 622 #endif 623 DEBE_WRITE(sc, SUNXI_DEBE_ATTCTL1_REG, val); 624 625 val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG); 626 val |= SUNXI_DEBE_MODCTL_LAY0_EN; 627 if (interlace_p) { 628 val |= SUNXI_DEBE_MODCTL_ITLMOD_EN; 629 } else { 630 val &= ~SUNXI_DEBE_MODCTL_ITLMOD_EN; 631 } 632 val &= ~SUNXI_DEBE_MODCTL_OUT_SEL; 633 if (sc->sc_unit == 1) { 634 val |= __SHIFTIN(SUNXI_DEBE_MODCTL_OUT_SEL_LCD1, 635 SUNXI_DEBE_MODCTL_OUT_SEL); 636 } 637 DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, val); 638 } else { 639 /* disable */ 640 val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG); 641 val &= ~SUNXI_DEBE_MODCTL_LAY0_EN; 642 val &= ~SUNXI_DEBE_MODCTL_START_CTL; 643 DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, val); 644 645 /* notify fb */ 646 sunxi_debe_setup_fbdev(sc, mode); 647 } 648 } 649 650 static int 651 sunxi_debe_ioctl(device_t self, u_long cmd, void *data) 652 { 653 struct sunxi_debe_softc *sc = device_private(self); 654 struct wsdisplay_curpos *cp; 655 uint32_t val; 656 int enable; 657 658 switch (cmd) { 659 case WSDISPLAYIO_SVIDEO: 660 enable = *(int *)data; 661 val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG); 662 if (enable) { 663 if (val & SUNXI_DEBE_MODCTL_START_CTL) { 664 /* already enabled */ 665 return 0; 666 } 667 } else { 668 if ((val & SUNXI_DEBE_MODCTL_START_CTL) == 0) { 669 /* already disabled */ 670 return 0; 671 } 672 } 673 return fdt_endpoint_enable(sc->sc_out_ep, enable); 674 case WSDISPLAYIO_GVIDEO: 675 val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG); 676 *(int *)data = !!(val & SUNXI_DEBE_MODCTL_LAY0_EN); 677 return 0; 678 case WSDISPLAYIO_GCURPOS: 679 cp = data; 680 cp->x = sc->sc_cursor_x; 681 cp->y = sc->sc_cursor_y; 682 return 0; 683 case WSDISPLAYIO_SCURPOS: 684 cp = data; 685 return sunxi_debe_set_curpos(sc, cp->x, cp->y); 686 case WSDISPLAYIO_GCURMAX: 687 cp = data; 688 cp->x = SUNXI_DEBE_CURMAX; 689 cp->y = SUNXI_DEBE_CURMAX; 690 return 0; 691 case WSDISPLAYIO_SCURSOR: 692 return sunxi_debe_set_cursor(sc, data); 693 } 694 695 return EPASSTHROUGH; 696 } 697 698 /* genfb attachement */ 699 700 struct sunxi_befb_softc { 701 struct genfb_softc sc_gen; 702 device_t sc_debedev; 703 704 bus_dma_tag_t sc_dmat; 705 bus_dma_segment_t *sc_dmasegs; 706 int sc_ndmasegs; 707 }; 708 709 static device_t sunxi_befb_consoledev = NULL; 710 711 static int sunxi_befb_match(device_t, cfdata_t, void *); 712 static void sunxi_befb_attach(device_t, device_t, void *); 713 714 static int sunxi_befb_ioctl(void *, void *, u_long, void *, int, lwp_t *); 715 static paddr_t sunxi_befb_mmap(void *, void *, off_t, int); 716 static bool sunxi_befb_shutdown(device_t, int); 717 718 CFATTACH_DECL_NEW(sunxi_befb, sizeof(struct sunxi_befb_softc), 719 sunxi_befb_match, sunxi_befb_attach, NULL, NULL); 720 721 static int 722 sunxi_befb_match(device_t parent, cfdata_t cf, void *aux) 723 { 724 return 1; 725 } 726 727 static void 728 sunxi_befb_attach(device_t parent, device_t self, void *aux) 729 { 730 struct sunxi_befb_softc *sc = device_private(self); 731 struct sunxifb_attach_args * const afb = aux; 732 prop_dictionary_t cfg = device_properties(self); 733 struct genfb_ops ops; 734 735 sc->sc_gen.sc_dev = self; 736 sc->sc_debedev = parent; 737 sc->sc_dmat = afb->afb_dmat; 738 sc->sc_dmasegs = afb->afb_dmasegs; 739 sc->sc_ndmasegs = afb->afb_ndmasegs; 740 741 prop_dictionary_set_uint32(cfg, "width", afb->afb_width); 742 prop_dictionary_set_uint32(cfg, "height", afb->afb_height); 743 prop_dictionary_set_uint8(cfg, "depth", 32); 744 prop_dictionary_set_uint16(cfg, "linebytes", afb->afb_width * 4); 745 prop_dictionary_set_uint32(cfg, "address", 0); 746 prop_dictionary_set_uint32(cfg, "virtual_address", 747 (uintptr_t)afb->afb_fb); 748 749 genfb_init(&sc->sc_gen); 750 751 if (sc->sc_gen.sc_width == 0 || sc->sc_gen.sc_fbsize == 0) { 752 aprint_normal(": disabled\n"); 753 return; 754 } 755 756 pmf_device_register1(self, NULL, NULL, sunxi_befb_shutdown); 757 758 memset(&ops, 0, sizeof(ops)); 759 ops.genfb_ioctl = sunxi_befb_ioctl; 760 ops.genfb_mmap = sunxi_befb_mmap; 761 762 aprint_naive("\n"); 763 764 bool is_console = (debe_console_sc == device_private(parent)); 765 if (is_console) 766 aprint_normal(": switching to framebuffer console\n"); 767 else 768 aprint_normal("\n"); 769 770 #ifdef WSDISPLAY_MULTICONS 771 /* 772 * if we support multicons, only the first framebuffer is console, 773 * unless we already know which framebuffer will be the console 774 */ 775 if (!is_console && debe_console_sc == NULL && 776 sunxi_befb_consoledev == NULL) 777 is_console = true; 778 #endif 779 prop_dictionary_set_bool(cfg, "is_console", is_console); 780 if (is_console) { 781 KASSERT(sunxi_befb_consoledev == NULL); 782 sunxi_befb_consoledev = self; 783 } 784 785 genfb_attach(&sc->sc_gen, &ops); 786 } 787 788 static int 789 sunxi_befb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l) 790 { 791 struct sunxi_befb_softc *sc = v; 792 struct wsdisplayio_bus_id *busid; 793 struct wsdisplayio_fbinfo *fbi; 794 struct rasops_info *ri; 795 int error; 796 797 switch (cmd) { 798 case WSDISPLAYIO_GTYPE: 799 *(u_int *)data = WSDISPLAY_TYPE_ALLWINNER; 800 return 0; 801 case WSDISPLAYIO_GET_BUSID: 802 busid = data; 803 busid->bus_type = WSDISPLAYIO_BUS_SOC; 804 return 0; 805 case WSDISPLAYIO_GET_FBINFO: 806 fbi = data; 807 ri = &sc->sc_gen.vd.active->scr_ri; 808 error = wsdisplayio_get_fbinfo(ri, fbi); 809 if (error == 0) { 810 fbi->fbi_flags |= WSFB_VRAM_IS_RAM; 811 fbi->fbi_fbsize = sc->sc_dmasegs[0].ds_len; 812 } 813 return error; 814 case WSDISPLAYIO_SVIDEO: 815 case WSDISPLAYIO_GVIDEO: 816 case WSDISPLAYIO_GCURPOS: 817 case WSDISPLAYIO_SCURPOS: 818 case WSDISPLAYIO_GCURMAX: 819 case WSDISPLAYIO_SCURSOR: 820 return sunxi_debe_ioctl(sc->sc_debedev, cmd, data); 821 default: 822 return EPASSTHROUGH; 823 } 824 } 825 826 static paddr_t 827 sunxi_befb_mmap(void *v, void *vs, off_t off, int prot) 828 { 829 struct sunxi_befb_softc *sc = v; 830 831 if (off < 0 || off >= sc->sc_dmasegs[0].ds_len) 832 return -1; 833 834 return bus_dmamem_mmap(sc->sc_dmat, sc->sc_dmasegs, sc->sc_ndmasegs, 835 off, prot, BUS_DMA_PREFETCHABLE); 836 } 837 838 static bool 839 sunxi_befb_shutdown(device_t self, int flags) 840 { 841 genfb_enable_polling(self); 842 return true; 843 } 844 845 static void 846 sunxi_befb_set_videomode(device_t dev, u_int width, u_int height) 847 { 848 struct sunxi_befb_softc *sc = device_private(dev); 849 850 if (sc->sc_gen.sc_width != width || sc->sc_gen.sc_height != height) { 851 device_printf(sc->sc_gen.sc_dev, 852 "mode switching not yet supported\n"); 853 } 854 } 855 856 int 857 sunxi_debe_pipeline(int phandle, bool active) 858 { 859 device_t dev; 860 struct sunxi_debe_softc *sc; 861 struct fdt_endpoint *ep; 862 int i, error; 863 static bool reset_done = false; 864 865 if (!active) 866 return EOPNOTSUPP; 867 868 for (i = 0;;i++) { 869 dev = device_find_by_driver_unit("sunxidebe", i); 870 if (dev == NULL) 871 return ENODEV; 872 sc = device_private(dev); 873 if (sc->sc_phandle == phandle) 874 break; 875 } 876 if (!reset_done) { 877 sunxi_debe_doreset(); 878 sunxi_tcon_doreset(); 879 sunxi_hdmi_doreset(); 880 reset_done = true; 881 } 882 883 aprint_normal("activate %s\n", device_xname(dev)); 884 if (clk_enable(sc->sc_clk_ahb) != 0 || 885 clk_enable(sc->sc_clk_mod) != 0) { 886 aprint_error_dev(dev, "couldn't enable clocks\n"); 887 return EIO; 888 } 889 /* connect debd0 to tcon0, debe1 to tcon1 */ 890 ep = fdt_endpoint_get_from_index(&sc->sc_ports, SUNXI_PORT_OUTPUT, 891 sc->sc_unit); 892 if (ep == NULL) { 893 aprint_error_dev(dev, "no output endpoint for %d\n", 894 sc->sc_unit); 895 return ENODEV; 896 } 897 error = fdt_endpoint_activate(ep, true); 898 if (error) 899 return error; 900 901 sc->sc_out_ep = ep; 902 error = fdt_endpoint_enable(ep, true); 903 return error; 904 } 905 906 /* 907 * we don't want to take over console at this time - simplefb will 908 * do a better job than us. We will take over later. 909 * But we want to record the /chose/framebuffer phandle if there is one 910 */ 911 912 static const char * const simplefb_compatible[] = { 913 "allwinner,simple-framebuffer", 914 NULL 915 }; 916 917 static int 918 sunxidebe_console_match(int phandle) 919 { 920 if (of_match_compatible(phandle, simplefb_compatible)) { 921 sunxi_simplefb_phandle = phandle; 922 } 923 return 0; 924 } 925 926 static void 927 sunxidebe_console_consinit(struct fdt_attach_args *faa, u_int uart_freq) 928 { 929 panic("sunxidebe_console_consinit"); 930 } 931 932 static const struct fdt_console sunxidebe_fdt_console = { 933 .match = sunxidebe_console_match, 934 .consinit = sunxidebe_console_consinit 935 }; 936 937 FDT_CONSOLE(sunxidebe, &sunxidebe_fdt_console); 938 939 #if defined(SUNXI_DEBE_DEBUG) 940 void 941 sunxi_debe_dump_regs(int u) 942 { 943 static const struct { 944 const char *name; 945 uint16_t reg; 946 } regs[] = { 947 { "SUNXI_DEBE_MODCTL_REG", SUNXI_DEBE_MODCTL_REG}, 948 { "SUNXI_DEBE_BACKCOLOR_REG", SUNXI_DEBE_BACKCOLOR_REG}, 949 { "SUNXI_DEBE_DISSIZE_REG", SUNXI_DEBE_DISSIZE_REG}, 950 { "SUNXI_DEBE_LAYSIZE_REG", SUNXI_DEBE_LAYSIZE_REG}, 951 { "SUNXI_DEBE_LAYCOOR_REG", SUNXI_DEBE_LAYCOOR_REG}, 952 { "SUNXI_DEBE_LAYLINEWIDTH_REG", SUNXI_DEBE_LAYLINEWIDTH_REG}, 953 { "SUNXI_DEBE_LAYFB_L32ADD_REG", SUNXI_DEBE_LAYFB_L32ADD_REG}, 954 { "SUNXI_DEBE_LAYFB_H4ADD_REG", SUNXI_DEBE_LAYFB_H4ADD_REG}, 955 { "SUNXI_DEBE_REGBUFFCTL_REG", SUNXI_DEBE_REGBUFFCTL_REG}, 956 { "SUNXI_DEBE_CKMAX_REG", SUNXI_DEBE_CKMAX_REG}, 957 { "SUNXI_DEBE_CKMIN_REG", SUNXI_DEBE_CKMIN_REG}, 958 { "SUNXI_DEBE_CKCFG_REG", SUNXI_DEBE_CKCFG_REG}, 959 { "SUNXI_DEBE_ATTCTL0_REG", SUNXI_DEBE_ATTCTL0_REG}, 960 { "SUNXI_DEBE_ATTCTL1_REG", SUNXI_DEBE_ATTCTL1_REG}, 961 { "SUNXI_DEBE_HWCCTL_REG", SUNXI_DEBE_HWCCTL_REG}, 962 { "SUNXI_DEBE_HWCFBCTL_REG", SUNXI_DEBE_HWCFBCTL_REG}, 963 { "SUNXI_DEBE_WBCTL_REG", SUNXI_DEBE_WBCTL_REG}, 964 { "SUNXI_DEBE_WBADD_REG", SUNXI_DEBE_WBADD_REG}, 965 { "SUNXI_DEBE_WBLINEWIDTH_REG", SUNXI_DEBE_WBLINEWIDTH_REG}, 966 { "SUNXI_DEBE_IYUVCTL_REG", SUNXI_DEBE_IYUVCTL_REG}, 967 { "SUNXI_DEBE_IYUVADD_REG", SUNXI_DEBE_IYUVADD_REG}, 968 { "SUNXI_DEBE_IYUVLINEWIDTH_REG", SUNXI_DEBE_IYUVLINEWIDTH_REG}, 969 { "SUNXI_DEBE_YGCOEF_REG", SUNXI_DEBE_YGCOEF_REG}, 970 { "SUNXI_DEBE_YGCONS_REG", SUNXI_DEBE_YGCONS_REG}, 971 { "SUNXI_DEBE_URCOEF_REG", SUNXI_DEBE_URCOEF_REG}, 972 { "SUNXI_DEBE_URCONS_REG", SUNXI_DEBE_URCONS_REG}, 973 { "SUNXI_DEBE_VBCOEF_REG", SUNXI_DEBE_VBCOEF_REG}, 974 { "SUNXI_DEBE_VBCONS_REG", SUNXI_DEBE_VBCONS_REG}, 975 { "SUNXI_DEBE_OCCTL_REG", SUNXI_DEBE_OCCTL_REG}, 976 { "SUNXI_DEBE_OCRCOEF_REG", SUNXI_DEBE_OCRCOEF_REG}, 977 { "SUNXI_DEBE_OCRCONS_REG", SUNXI_DEBE_OCRCONS_REG}, 978 { "SUNXI_DEBE_OCGCOEF_REG", SUNXI_DEBE_OCGCOEF_REG}, 979 { "SUNXI_DEBE_OCGCONS_REG", SUNXI_DEBE_OCGCONS_REG}, 980 { "SUNXI_DEBE_OCBCOEF_REG", SUNXI_DEBE_OCBCOEF_REG}, 981 { "SUNXI_DEBE_OCBCONS_REG", SUNXI_DEBE_OCBCONS_REG}, 982 }; 983 struct sunxi_debe_softc *sc; 984 device_t dev; 985 986 dev = device_find_by_driver_unit("sunxidebe", u); 987 if (dev == NULL) 988 return; 989 sc = device_private(dev); 990 991 for (int i = 0; i < __arraycount(regs); i++) { 992 printf("%s: 0x%08x\n", regs[i].name, 993 DEBE_READ(sc, regs[i].reg)); 994 } 995 } 996 #endif 997