1/* $NetBSD: sunxi_debe.c,v 1.17 2024/02/02 22:14:04 andvar 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.17 2024/02/02 22:14:04 andvar 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 64enum sunxi_debe_type { 65 DEBE_A10 = 1, 66}; 67 68struct 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 104static const struct device_compatible_entry compat_data[] = { 105 { .compat = "allwinner,sun4i-a10-display-backend", .value = DEBE_A10 }, 106 { .compat = "allwinner,sun7i-a20-display-backend", .value = DEBE_A10 }, 107 DEVICE_COMPAT_EOL 108}; 109 110struct 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 119static void sunxi_debe_ep_connect(device_t, struct fdt_endpoint *, bool); 120static int sunxi_debe_ep_enable(device_t, struct fdt_endpoint *, bool); 121static int sunxi_debe_match(device_t, cfdata_t, void *); 122static void sunxi_debe_attach(device_t, device_t, void *); 123 124static int sunxi_debe_alloc_videomem(struct sunxi_debe_softc *); 125static void sunxi_debe_setup_fbdev(struct sunxi_debe_softc *, 126 const struct videomode *); 127 128static int sunxi_debe_set_curpos(struct sunxi_debe_softc *, int, int); 129static int sunxi_debe_set_cursor(struct sunxi_debe_softc *, 130 struct wsdisplay_cursor *); 131static int sunxi_debe_ioctl(device_t, u_long, void *); 132static void sunxi_befb_set_videomode(device_t, u_int, u_int); 133void sunxi_debe_dump_regs(int); 134 135static struct sunxi_debe_softc *debe_console_sc; 136static int sunxi_simplefb_phandle = -1; 137 138CFATTACH_DECL_NEW(sunxi_debe, sizeof(struct sunxi_debe_softc), 139 sunxi_debe_match, sunxi_debe_attach, NULL, NULL); 140 141static int 142sunxi_debe_match(device_t parent, cfdata_t cf, void *aux) 143{ 144 struct fdt_attach_args * const faa = aux; 145 146 return of_compatible_match(faa->faa_phandle, compat_data); 147} 148 149static void 150sunxi_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 = 192 of_compatible_lookup(faa->faa_phandle, compat_data)->value; 193 194 aprint_naive("\n"); 195 aprint_normal(": Display Engine Backend (%s)\n", 196 fdtbus_get_string(phandle, "name")); 197 198 199 sc->sc_dmasize = SUNXI_DEBE_VIDEOMEM; 200 201 error = sunxi_debe_alloc_videomem(sc); 202 if (error) { 203 aprint_error_dev(sc->sc_dev, 204 "couldn't allocate video memory, error = %d\n", error); 205 return; 206 } 207 208 sc->sc_unit = -1; 209 sc->sc_ports.dp_ep_connect = sunxi_debe_ep_connect; 210 sc->sc_ports.dp_ep_enable = sunxi_debe_ep_enable; 211 fdt_ports_register(&sc->sc_ports, self, phandle, EP_OTHER); 212} 213 214static void 215sunxi_debe_doreset(void) 216{ 217 device_t dev; 218 struct sunxi_debe_softc *sc; 219 int error; 220 221 for (int i = 0;;i++) { 222 dev = device_find_by_driver_unit("sunxidebe", i); 223 if (dev == NULL) 224 return; 225 sc = device_private(dev); 226 227 if (fdtbus_reset_assert(sc->sc_rst) != 0) { 228 aprint_error_dev(dev, ": couldn't assert reset\n"); 229 return; 230 } 231 delay(1); 232 if (fdtbus_reset_deassert(sc->sc_rst) != 0) { 233 aprint_error_dev(dev, ": couldn't de-assert reset\n"); 234 return; 235 } 236 237 238 error = clk_set_rate(sc->sc_clk_mod, 300000000); 239 if (error) { 240 aprint_error_dev(dev, 241 "couldn't set mod clock rate (%d)\n", error); 242 return; 243 } 244 245 if (clk_enable(sc->sc_clk_ahb) != 0 || 246 clk_enable(sc->sc_clk_mod) != 0) { 247 aprint_error_dev(dev, ": couldn't enable clocks\n"); 248 return; 249 } 250 if (clk_disable(sc->sc_clk_ram) != 0) { 251 aprint_error_dev(dev, ": couldn't disable ram clock\n"); 252 } 253 254 for (unsigned int reg = 0x800; reg < 0x1000; reg += 4) { 255 DEBE_WRITE(sc, reg, 0); 256 } 257 258 DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, SUNXI_DEBE_MODCTL_EN); 259 260 DEBE_WRITE(sc, SUNXI_DEBE_HWC_PALETTE_TABLE, 0); 261 262 if (clk_disable(sc->sc_clk_ahb) != 0 || 263 clk_disable(sc->sc_clk_mod) != 0) { 264 aprint_error_dev(sc->sc_dev, 265 ": couldn't disable clocks\n"); 266 } 267 } 268} 269 270static void 271sunxi_debe_ep_connect(device_t self, struct fdt_endpoint *ep, bool connect) 272{ 273 struct sunxi_debe_softc *sc = device_private(self); 274 struct fdt_endpoint *rep = fdt_endpoint_remote(ep); 275 int rep_idx = fdt_endpoint_index(rep); 276 277 KASSERT(device_is_a(self, "sunxidebe")); 278 if (!connect) { 279 aprint_error_dev(self, "endpoint disconnect not supported\n"); 280 return; 281 } 282 283 if (fdt_endpoint_port_index(ep) == 1) { 284 bool do_print = (sc->sc_unit == -1); 285 /* 286 * one of our output endpoints has been connected. 287 * the remote id is our unit number 288 */ 289 if (sc->sc_unit != -1 && rep_idx != -1 && 290 sc->sc_unit != rep_idx) { 291 aprint_error_dev(self, ": remote id %d doesn't match" 292 " discovered unit number %d\n", 293 rep_idx, sc->sc_unit); 294 return; 295 } 296 if (!device_is_a(fdt_endpoint_device(rep), "sunxitcon")) { 297 aprint_error_dev(self, 298 ": output %d connected to unknown device\n", 299 fdt_endpoint_index(ep)); 300 return; 301 } 302 if (rep_idx != -1) 303 sc->sc_unit = rep_idx; 304 else { 305 /* assume only one debe */ 306 sc->sc_unit = 0; 307 } 308 if (do_print) 309 aprint_verbose_dev(self, "debe unit %d\n", sc->sc_unit); 310 } 311} 312 313static int 314sunxi_debe_alloc_videomem(struct sunxi_debe_softc *sc) 315{ 316 int error, nsegs; 317 318 error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmasize, 0x1000, 0, 319 sc->sc_dmasegs, 1, &nsegs, BUS_DMA_WAITOK); 320 if (error) 321 return error; 322 error = bus_dmamem_map(sc->sc_dmat, sc->sc_dmasegs, nsegs, 323 sc->sc_dmasize, &sc->sc_dmap, BUS_DMA_WAITOK | BUS_DMA_COHERENT); 324 if (error) 325 goto free; 326 error = bus_dmamap_create(sc->sc_dmat, sc->sc_dmasize, 1, 327 sc->sc_dmasize, 0, BUS_DMA_WAITOK, &sc->sc_dmamap); 328 if (error) 329 goto unmap; 330 error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_dmap, 331 sc->sc_dmasize, NULL, BUS_DMA_WAITOK); 332 if (error) 333 goto destroy; 334 335 memset(sc->sc_dmap, 0, sc->sc_dmasize); 336 337 return 0; 338 339destroy: 340 bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); 341unmap: 342 bus_dmamem_unmap(sc->sc_dmat, sc->sc_dmap, sc->sc_dmasize); 343free: 344 bus_dmamem_free(sc->sc_dmat, sc->sc_dmasegs, nsegs); 345 346 sc->sc_dmasize = 0; 347 sc->sc_dmap = NULL; 348 349 return error; 350} 351 352static void 353sunxi_debe_setup_fbdev(struct sunxi_debe_softc *sc, const struct videomode *mode) 354{ 355 if (mode == NULL) 356 return; 357 358 const u_int interlace_p = !!(mode->flags & VID_INTERLACE); 359 const u_int fb_width = mode->hdisplay; 360 const u_int fb_height = (mode->vdisplay << interlace_p); 361 362 if (mode && sc->sc_fbdev == NULL) { 363 /* see if we are the console */ 364 if (sunxi_simplefb_phandle >= 0) { 365 const char *cons_pipeline = 366 fdtbus_get_string(sunxi_simplefb_phandle, 367 "allwinner,pipeline"); 368 struct fdt_endpoint *ep = fdt_endpoint_get_from_index( 369 &sc->sc_ports, SUNXI_PORT_OUTPUT, sc->sc_unit); 370 struct fdt_endpoint *rep = fdt_endpoint_remote(ep); 371 if (sunxi_tcon_is_console( 372 fdt_endpoint_device(rep), cons_pipeline)) 373 debe_console_sc = sc; 374 } else if (debe_console_sc == NULL) { 375 if (match_bootconf_option(boot_args, 376 "console", "fb0")) { 377 if (sc->sc_unit == 0) 378 debe_console_sc = sc; 379 } else if (match_bootconf_option(boot_args, 380 "console", "fb1")) { 381 if (sc->sc_unit == 1) 382 debe_console_sc = sc; 383 } else if (match_bootconf_option(boot_args, 384 "console", "fb")) { 385 /* match first activated */ 386 debe_console_sc = sc; 387 } 388 } 389 struct sunxifb_attach_args afb = { 390 .afb_fb = sc->sc_dmap, 391 .afb_width = fb_width, 392 .afb_height = fb_height, 393 .afb_dmat = sc->sc_dmat, 394 .afb_dmasegs = sc->sc_dmasegs, 395 .afb_ndmasegs = 1 396 }; 397 sc->sc_fbdev = config_found(sc->sc_dev, &afb, NULL, CFARGS_NONE); 398 } else if (sc->sc_fbdev != NULL) { 399 sunxi_befb_set_videomode(sc->sc_fbdev, fb_width, fb_height); 400 } 401} 402 403static int 404sunxi_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 439static int 440sunxi_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 522static int 523sunxi_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/* 564 * FIXME 2020/10/19 565 * This function is not called actually at the moment. 566 */ 567void 568sunxi_debe_set_videomode(device_t dev, const struct videomode *mode) 569{ 570 struct sunxi_debe_softc *sc; 571 uint32_t val; 572 573 KASSERT(device_is_a(dev, "sunxidebe")); 574 sc = device_private(dev); 575 576 if (mode) { 577 const u_int interlace_p = !!(mode->flags & VID_INTERLACE); 578 const u_int width = mode->hdisplay; 579 const u_int height = (mode->vdisplay << interlace_p); 580 const u_int fb_width = width; 581 const u_int fb_height = height; 582 uint32_t vmem = width * height * 4; 583 584 if (vmem > sc->sc_dmasize) { 585 device_printf(sc->sc_dev, 586 "not enough memory for %ux%u fb (req %u have %u)\n", 587 width, height, vmem, (unsigned int)sc->sc_dmasize); 588 return; 589 } 590 591 paddr_t pa = sc->sc_dmamap->dm_segs[0].ds_addr; 592#if !defined(ALLWINNER_A80) && 0 593#define SUNXI_SDRAM_PBASE-0 0x40000000 594 /* 595 * On 2GB systems, we need to subtract AWIN_SDRAM_PBASE from 596 * the phys addr. 597 */ 598 if (pa >= SUNXI_SDRAM_PBASE) 599 pa -= SUNXI_SDRAM_PBASE; 600#endif 601 602 /* notify fb */ 603 sunxi_debe_setup_fbdev(sc, mode); 604 605 DEBE_WRITE(sc, SUNXI_DEBE_DISSIZE_REG, 606 ((height - 1) << 16) | (width - 1)); 607 DEBE_WRITE(sc, SUNXI_DEBE_LAYSIZE_REG, 608 ((fb_height - 1) << 16) | (fb_width - 1)); 609 DEBE_WRITE(sc, SUNXI_DEBE_LAYCOOR_REG, 0); 610 DEBE_WRITE(sc, SUNXI_DEBE_LAYLINEWIDTH_REG, (fb_width << 5)); 611 DEBE_WRITE(sc, SUNXI_DEBE_LAYFB_L32ADD_REG, pa << 3); 612 DEBE_WRITE(sc, SUNXI_DEBE_LAYFB_H4ADD_REG, pa >> 29); 613 614 val = DEBE_READ(sc, SUNXI_DEBE_ATTCTL1_REG); 615 val &= ~SUNXI_DEBE_ATTCTL1_LAY_FBFMT; 616 val |= __SHIFTIN(SUNXI_DEBE_ATTCTL1_LAY_FBFMT_XRGB8888, 617 SUNXI_DEBE_ATTCTL1_LAY_FBFMT); 618 val &= ~SUNXI_DEBE_ATTCTL1_LAY_BRSWAPEN; 619 val &= ~SUNXI_DEBE_ATTCTL1_LAY_FBPS; 620#if 0 /* __ARMEB__ */ 621 /* 622 * For big endian, we dynamically override FDT to let 623 * genfb(4) know that framebuffer is byte-swapped. 624 * See fdt_update_fb_format() in fdt_machdep.c. 625 */ 626 val |= __SHIFTIN(SUNXI_DEBE_ATTCTL1_LAY_FBPS_32BPP_BGRA, 627 SUNXI_DEBE_ATTCTL1_LAY_FBPS); 628#else 629 val |= __SHIFTIN(SUNXI_DEBE_ATTCTL1_LAY_FBPS_32BPP_ARGB, 630 SUNXI_DEBE_ATTCTL1_LAY_FBPS); 631#endif 632 DEBE_WRITE(sc, SUNXI_DEBE_ATTCTL1_REG, val); 633 634 val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG); 635 val |= SUNXI_DEBE_MODCTL_LAY0_EN; 636 if (interlace_p) { 637 val |= SUNXI_DEBE_MODCTL_ITLMOD_EN; 638 } else { 639 val &= ~SUNXI_DEBE_MODCTL_ITLMOD_EN; 640 } 641 val &= ~SUNXI_DEBE_MODCTL_OUT_SEL; 642 if (sc->sc_unit == 1) { 643 val |= __SHIFTIN(SUNXI_DEBE_MODCTL_OUT_SEL_LCD1, 644 SUNXI_DEBE_MODCTL_OUT_SEL); 645 } 646 DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, val); 647 } else { 648 /* disable */ 649 val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG); 650 val &= ~SUNXI_DEBE_MODCTL_LAY0_EN; 651 val &= ~SUNXI_DEBE_MODCTL_START_CTL; 652 DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, val); 653 654 /* notify fb */ 655 sunxi_debe_setup_fbdev(sc, mode); 656 } 657} 658 659static int 660sunxi_debe_ioctl(device_t self, u_long cmd, void *data) 661{ 662 struct sunxi_debe_softc *sc = device_private(self); 663 struct wsdisplay_curpos *cp; 664 uint32_t val; 665 int enable; 666 667 switch (cmd) { 668 case WSDISPLAYIO_SVIDEO: 669 enable = *(int *)data; 670 val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG); 671 if (enable) { 672 if (val & SUNXI_DEBE_MODCTL_START_CTL) { 673 /* already enabled */ 674 return 0; 675 } 676 } else { 677 if ((val & SUNXI_DEBE_MODCTL_START_CTL) == 0) { 678 /* already disabled */ 679 return 0; 680 } 681 } 682 return fdt_endpoint_enable(sc->sc_out_ep, enable); 683 case WSDISPLAYIO_GVIDEO: 684 val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG); 685 *(int *)data = !!(val & SUNXI_DEBE_MODCTL_LAY0_EN); 686 return 0; 687 case WSDISPLAYIO_GCURPOS: 688 cp = data; 689 cp->x = sc->sc_cursor_x; 690 cp->y = sc->sc_cursor_y; 691 return 0; 692 case WSDISPLAYIO_SCURPOS: 693 cp = data; 694 return sunxi_debe_set_curpos(sc, cp->x, cp->y); 695 case WSDISPLAYIO_GCURMAX: 696 cp = data; 697 cp->x = SUNXI_DEBE_CURMAX; 698 cp->y = SUNXI_DEBE_CURMAX; 699 return 0; 700 case WSDISPLAYIO_SCURSOR: 701 return sunxi_debe_set_cursor(sc, data); 702 } 703 704 return EPASSTHROUGH; 705} 706 707/* genfb attachment */ 708 709struct sunxi_befb_softc { 710 struct genfb_softc sc_gen; 711 device_t sc_debedev; 712 713 bus_dma_tag_t sc_dmat; 714 bus_dma_segment_t *sc_dmasegs; 715 int sc_ndmasegs; 716}; 717 718static device_t sunxi_befb_consoledev = NULL; 719 720static int sunxi_befb_match(device_t, cfdata_t, void *); 721static void sunxi_befb_attach(device_t, device_t, void *); 722 723static int sunxi_befb_ioctl(void *, void *, u_long, void *, int, lwp_t *); 724static paddr_t sunxi_befb_mmap(void *, void *, off_t, int); 725static bool sunxi_befb_shutdown(device_t, int); 726 727CFATTACH_DECL_NEW(sunxi_befb, sizeof(struct sunxi_befb_softc), 728 sunxi_befb_match, sunxi_befb_attach, NULL, NULL); 729 730static int 731sunxi_befb_match(device_t parent, cfdata_t cf, void *aux) 732{ 733 return 1; 734} 735 736static void 737sunxi_befb_attach(device_t parent, device_t self, void *aux) 738{ 739 struct sunxi_befb_softc *sc = device_private(self); 740 struct sunxifb_attach_args * const afb = aux; 741 prop_dictionary_t cfg = device_properties(self); 742 struct genfb_ops ops; 743 744 sc->sc_gen.sc_dev = self; 745 sc->sc_debedev = parent; 746 sc->sc_dmat = afb->afb_dmat; 747 sc->sc_dmasegs = afb->afb_dmasegs; 748 sc->sc_ndmasegs = afb->afb_ndmasegs; 749 750 prop_dictionary_set_uint32(cfg, "width", afb->afb_width); 751 prop_dictionary_set_uint32(cfg, "height", afb->afb_height); 752 prop_dictionary_set_uint8(cfg, "depth", 32); 753 prop_dictionary_set_uint16(cfg, "linebytes", afb->afb_width * 4); 754 prop_dictionary_set_uint32(cfg, "address", 0); 755 prop_dictionary_set_uint32(cfg, "virtual_address", 756 (uintptr_t)afb->afb_fb); 757 758 genfb_init(&sc->sc_gen); 759 760 if (sc->sc_gen.sc_width == 0 || sc->sc_gen.sc_fbsize == 0) { 761 aprint_normal(": disabled\n"); 762 return; 763 } 764 765 pmf_device_register1(self, NULL, NULL, sunxi_befb_shutdown); 766 767 memset(&ops, 0, sizeof(ops)); 768 ops.genfb_ioctl = sunxi_befb_ioctl; 769 ops.genfb_mmap = sunxi_befb_mmap; 770 771 aprint_naive("\n"); 772 773 bool is_console = (debe_console_sc == device_private(parent)); 774 if (is_console) 775 aprint_normal(": switching to framebuffer console\n"); 776 else 777 aprint_normal("\n"); 778 779#ifdef WSDISPLAY_MULTICONS 780 /* 781 * if we support multicons, only the first framebuffer is console, 782 * unless we already know which framebuffer will be the console 783 */ 784 if (!is_console && debe_console_sc == NULL && 785 sunxi_befb_consoledev == NULL) 786 is_console = true; 787#endif 788 prop_dictionary_set_bool(cfg, "is_console", is_console); 789 if (is_console) { 790 KASSERT(sunxi_befb_consoledev == NULL); 791 sunxi_befb_consoledev = self; 792 } 793 794 genfb_attach(&sc->sc_gen, &ops); 795} 796 797static int 798sunxi_befb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l) 799{ 800 struct sunxi_befb_softc *sc = v; 801 struct wsdisplayio_bus_id *busid; 802 struct wsdisplayio_fbinfo *fbi; 803 struct rasops_info *ri; 804 int error; 805 806 switch (cmd) { 807 case WSDISPLAYIO_GTYPE: 808 *(u_int *)data = WSDISPLAY_TYPE_ALLWINNER; 809 return 0; 810 case WSDISPLAYIO_GET_BUSID: 811 busid = data; 812 busid->bus_type = WSDISPLAYIO_BUS_SOC; 813 return 0; 814 case WSDISPLAYIO_GET_FBINFO: 815 fbi = data; 816 ri = &sc->sc_gen.vd.active->scr_ri; 817 error = wsdisplayio_get_fbinfo(ri, fbi); 818 if (error == 0) { 819 fbi->fbi_flags |= WSFB_VRAM_IS_RAM; 820 fbi->fbi_fbsize = sc->sc_dmasegs[0].ds_len; 821 } 822 return error; 823 case WSDISPLAYIO_SVIDEO: 824 case WSDISPLAYIO_GVIDEO: 825 case WSDISPLAYIO_GCURPOS: 826 case WSDISPLAYIO_SCURPOS: 827 case WSDISPLAYIO_GCURMAX: 828 case WSDISPLAYIO_SCURSOR: 829 return sunxi_debe_ioctl(sc->sc_debedev, cmd, data); 830 default: 831 return EPASSTHROUGH; 832 } 833} 834 835static paddr_t 836sunxi_befb_mmap(void *v, void *vs, off_t off, int prot) 837{ 838 struct sunxi_befb_softc *sc = v; 839 840 if (off < 0 || off >= sc->sc_dmasegs[0].ds_len) 841 return -1; 842 843 return bus_dmamem_mmap(sc->sc_dmat, sc->sc_dmasegs, sc->sc_ndmasegs, 844 off, prot, BUS_DMA_PREFETCHABLE); 845} 846 847static bool 848sunxi_befb_shutdown(device_t self, int flags) 849{ 850 genfb_enable_polling(self); 851 return true; 852} 853 854static void 855sunxi_befb_set_videomode(device_t dev, u_int width, u_int height) 856{ 857 struct sunxi_befb_softc *sc = device_private(dev); 858 859 if (sc->sc_gen.sc_width != width || sc->sc_gen.sc_height != height) { 860 device_printf(sc->sc_gen.sc_dev, 861 "mode switching not yet supported\n"); 862 } 863} 864 865int 866sunxi_debe_pipeline(int phandle, bool active) 867{ 868 device_t dev; 869 struct sunxi_debe_softc *sc; 870 struct fdt_endpoint *ep; 871 int i, error; 872 static bool reset_done = false; 873 874 if (!active) 875 return EOPNOTSUPP; 876 877 for (i = 0;;i++) { 878 dev = device_find_by_driver_unit("sunxidebe", i); 879 if (dev == NULL) 880 return ENODEV; 881 sc = device_private(dev); 882 if (sc->sc_phandle == phandle) 883 break; 884 } 885 if (!reset_done) { 886 sunxi_debe_doreset(); 887 sunxi_tcon_doreset(); 888 sunxi_hdmi_doreset(); 889 reset_done = true; 890 } 891 892 aprint_normal("activate %s\n", device_xname(dev)); 893 if (clk_enable(sc->sc_clk_ahb) != 0 || 894 clk_enable(sc->sc_clk_mod) != 0) { 895 aprint_error_dev(dev, "couldn't enable clocks\n"); 896 return EIO; 897 } 898 /* connect debd0 to tcon0, debe1 to tcon1 */ 899 ep = fdt_endpoint_get_from_index(&sc->sc_ports, SUNXI_PORT_OUTPUT, 900 sc->sc_unit); 901 if (ep == NULL) { 902 aprint_error_dev(dev, "no output endpoint for %d\n", 903 sc->sc_unit); 904 return ENODEV; 905 } 906 error = fdt_endpoint_activate(ep, true); 907 if (error) 908 return error; 909 910 sc->sc_out_ep = ep; 911 error = fdt_endpoint_enable(ep, true); 912 return error; 913} 914 915/* 916 * we don't want to take over console at this time - simplefb will 917 * do a better job than us. We will take over later. 918 * But we want to record the /chose/framebuffer phandle if there is one 919 */ 920 921static const struct device_compatible_entry simplefb_compat_data[] = { 922 { .compat = "allwinner,simple-framebuffer" }, 923 DEVICE_COMPAT_EOL 924}; 925 926static int 927sunxidebe_console_match(int phandle) 928{ 929 if (of_compatible_match(phandle, simplefb_compat_data)) { 930 sunxi_simplefb_phandle = phandle; 931 } 932 return 0; 933} 934 935static void 936sunxidebe_console_consinit(struct fdt_attach_args *faa, u_int uart_freq) 937{ 938 panic("sunxidebe_console_consinit"); 939} 940 941static const struct fdt_console sunxidebe_fdt_console = { 942 .match = sunxidebe_console_match, 943 .consinit = sunxidebe_console_consinit 944}; 945 946FDT_CONSOLE(sunxidebe, &sunxidebe_fdt_console); 947 948#if defined(SUNXI_DEBE_DEBUG) 949void 950sunxi_debe_dump_regs(int u) 951{ 952 static const struct { 953 const char *name; 954 uint16_t reg; 955 } regs[] = { 956 { "SUNXI_DEBE_MODCTL_REG", SUNXI_DEBE_MODCTL_REG}, 957 { "SUNXI_DEBE_BACKCOLOR_REG", SUNXI_DEBE_BACKCOLOR_REG}, 958 { "SUNXI_DEBE_DISSIZE_REG", SUNXI_DEBE_DISSIZE_REG}, 959 { "SUNXI_DEBE_LAYSIZE_REG", SUNXI_DEBE_LAYSIZE_REG}, 960 { "SUNXI_DEBE_LAYCOOR_REG", SUNXI_DEBE_LAYCOOR_REG}, 961 { "SUNXI_DEBE_LAYLINEWIDTH_REG", SUNXI_DEBE_LAYLINEWIDTH_REG}, 962 { "SUNXI_DEBE_LAYFB_L32ADD_REG", SUNXI_DEBE_LAYFB_L32ADD_REG}, 963 { "SUNXI_DEBE_LAYFB_H4ADD_REG", SUNXI_DEBE_LAYFB_H4ADD_REG}, 964 { "SUNXI_DEBE_REGBUFFCTL_REG", SUNXI_DEBE_REGBUFFCTL_REG}, 965 { "SUNXI_DEBE_CKMAX_REG", SUNXI_DEBE_CKMAX_REG}, 966 { "SUNXI_DEBE_CKMIN_REG", SUNXI_DEBE_CKMIN_REG}, 967 { "SUNXI_DEBE_CKCFG_REG", SUNXI_DEBE_CKCFG_REG}, 968 { "SUNXI_DEBE_ATTCTL0_REG", SUNXI_DEBE_ATTCTL0_REG}, 969 { "SUNXI_DEBE_ATTCTL1_REG", SUNXI_DEBE_ATTCTL1_REG}, 970 { "SUNXI_DEBE_HWCCTL_REG", SUNXI_DEBE_HWCCTL_REG}, 971 { "SUNXI_DEBE_HWCFBCTL_REG", SUNXI_DEBE_HWCFBCTL_REG}, 972 { "SUNXI_DEBE_WBCTL_REG", SUNXI_DEBE_WBCTL_REG}, 973 { "SUNXI_DEBE_WBADD_REG", SUNXI_DEBE_WBADD_REG}, 974 { "SUNXI_DEBE_WBLINEWIDTH_REG", SUNXI_DEBE_WBLINEWIDTH_REG}, 975 { "SUNXI_DEBE_IYUVCTL_REG", SUNXI_DEBE_IYUVCTL_REG}, 976 { "SUNXI_DEBE_IYUVADD_REG", SUNXI_DEBE_IYUVADD_REG}, 977 { "SUNXI_DEBE_IYUVLINEWIDTH_REG", SUNXI_DEBE_IYUVLINEWIDTH_REG}, 978 { "SUNXI_DEBE_YGCOEF_REG", SUNXI_DEBE_YGCOEF_REG}, 979 { "SUNXI_DEBE_YGCONS_REG", SUNXI_DEBE_YGCONS_REG}, 980 { "SUNXI_DEBE_URCOEF_REG", SUNXI_DEBE_URCOEF_REG}, 981 { "SUNXI_DEBE_URCONS_REG", SUNXI_DEBE_URCONS_REG}, 982 { "SUNXI_DEBE_VBCOEF_REG", SUNXI_DEBE_VBCOEF_REG}, 983 { "SUNXI_DEBE_VBCONS_REG", SUNXI_DEBE_VBCONS_REG}, 984 { "SUNXI_DEBE_OCCTL_REG", SUNXI_DEBE_OCCTL_REG}, 985 { "SUNXI_DEBE_OCRCOEF_REG", SUNXI_DEBE_OCRCOEF_REG}, 986 { "SUNXI_DEBE_OCRCONS_REG", SUNXI_DEBE_OCRCONS_REG}, 987 { "SUNXI_DEBE_OCGCOEF_REG", SUNXI_DEBE_OCGCOEF_REG}, 988 { "SUNXI_DEBE_OCGCONS_REG", SUNXI_DEBE_OCGCONS_REG}, 989 { "SUNXI_DEBE_OCBCOEF_REG", SUNXI_DEBE_OCBCOEF_REG}, 990 { "SUNXI_DEBE_OCBCONS_REG", SUNXI_DEBE_OCBCONS_REG}, 991 }; 992 struct sunxi_debe_softc *sc; 993 device_t dev; 994 995 dev = device_find_by_driver_unit("sunxidebe", u); 996 if (dev == NULL) 997 return; 998 sc = device_private(dev); 999 1000 for (int i = 0; i < __arraycount(regs); i++) { 1001 printf("%s: 0x%08x\n", regs[i].name, 1002 DEBE_READ(sc, regs[i].reg)); 1003 } 1004} 1005#endif 1006