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