1 /* $NetBSD: wiiufb.c,v 1.5 2026/02/01 12:09:40 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2025 Jared McNeill <jmcneill (at) invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: wiiufb.c,v 1.5 2026/02/01 12:09:40 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/device.h> 35 #include <sys/systm.h> 36 37 #include <machine/wii.h> 38 #include <machine/wiiu.h> 39 40 #include <dev/wscons/wsconsio.h> 41 #include <dev/wscons/wsdisplayvar.h> 42 #include <dev/rasops/rasops.h> 43 #include <dev/wsfont/wsfont.h> 44 #include <dev/wscons/wsdisplay_vconsvar.h> 45 #include <dev/wsfb/genfbvar.h> 46 47 #include "mainbus.h" 48 49 /* Set to true to output to Gamepad instead of TV */ 50 static bool wiiufb_drc; 51 52 #define WIIUFB_BASE (wiiufb_drc ? WIIU_GFX_DRC_BASE : \ 53 WIIU_GFX_TV_BASE) 54 #define WIIUFB_WIDTH (wiiufb_drc ? 854 : 1280) 55 #define WIIUFB_HEIGHT (wiiufb_drc ? 480 : 720) 56 #define WIIUFB_BPP 32 57 #define WIIUFB_STRIDE ((wiiufb_drc ? 896 : 1280) * WIIUFB_BPP / NBBY) 58 #define WIIUFB_SIZE (WIIUFB_STRIDE * WIIUFB_HEIGHT) 59 60 #define WIIUFB_CURMAX 64 61 #define WIIUFB_CURSOR_SIZE (WIIUFB_CURMAX * WIIUFB_CURMAX * 4) 62 #define WIIUFB_CURSOR_ALIGN 0x1000 63 #define WIIUFB_CURSOR_BITS (WIIUFB_CURMAX * WIIUFB_CURMAX / NBBY) 64 65 #define REG_OFFSET(d, r) ((d) * 0x800 + (r)) 66 #define DGRPH_SWAP_CNTL(d) REG_OFFSET(d, 0x610c) 67 #define DGRPH_SWAP_ENDIAN_SWAP __BITS(1, 0) 68 #define DGRPH_SWAP_ENDIAN_SWAP_8IN32 __SHIFTIN(2, DGRPH_SWAP_ENDIAN_SWAP) 69 #define DCRTC_BLANK_CONTROL(d) REG_OFFSET(d, 0x6084) 70 #define DCRTC_BLANK_DATA_EN __BIT(8) 71 #define DCUR_CONTROL(d) REG_OFFSET(d, 0x6400) 72 #define DCUR_CONTROL_EN __BIT(0) 73 #define DCUR_CONTROL_MODE __BITS(9, 8) 74 #define DCUR_CONTROL_MODE_ARGB_PM __SHIFTIN(2, DCUR_CONTROL_MODE) 75 #define DCUR_SURFACE_ADDRESS(d) REG_OFFSET(d, 0x6408) 76 #define DCUR_SIZE(d) REG_OFFSET(d, 0x6410) 77 #define DCUR_SIZE_HEIGHT(h) ((h) - 1) 78 #define DCUR_SIZE_WIDTH(w) (((w) - 1) << 16) 79 #define DCUR_POSITION(d) REG_OFFSET(d, 0x6414) 80 #define DCUR_POSITION_Y(y) (y) 81 #define DCUR_POSITION_X(x) ((x) << 16) 82 #define DCUR_HOT_SPOT(d) REG_OFFSET(d, 0x6418) 83 #define DCUR_HOT_SPOT_Y(y) (y) 84 #define DCUR_HOT_SPOT_X(x) ((x) << 16) 85 #define DCUR_UPDATE(d) REG_OFFSET(d, 0x6424) 86 #define DCUR_UPDATE_LOCK __BIT(16) 87 88 struct wiiufb_dma { 89 bus_dmamap_t dma_map; 90 bus_dma_tag_t dma_tag; 91 bus_size_t dma_size; 92 bus_dma_segment_t dma_segs[1]; 93 int dma_nsegs; 94 void *dma_addr; 95 }; 96 97 struct wiiufb_cursor { 98 bool c_enable; 99 struct wsdisplay_curpos c_pos; 100 struct wsdisplay_curpos c_hot; 101 struct wsdisplay_curpos c_size; 102 uint32_t c_cmap[2]; 103 uint8_t c_image[WIIUFB_CURSOR_BITS]; 104 uint8_t c_mask[WIIUFB_CURSOR_BITS]; 105 }; 106 107 struct wiiufb_softc { 108 struct genfb_softc sc_gen; 109 110 bus_space_tag_t sc_bst; 111 bus_space_handle_t sc_bsh; 112 bus_dma_tag_t sc_dmat; 113 114 uint32_t sc_disp; 115 116 struct wiiufb_cursor sc_cursor; 117 struct wiiufb_cursor sc_tempcursor; 118 struct wiiufb_dma sc_curdma; 119 }; 120 121 static struct wiiufb_softc *wiiufb_sc; 122 123 static int wiiufb_match(device_t, cfdata_t, void *); 124 static void wiiufb_attach(device_t, device_t, void *); 125 126 static bool wiiufb_shutdown(device_t, int); 127 static int wiiufb_ioctl(void *, void *, u_long, void *, int, lwp_t *); 128 static paddr_t wiiufb_mmap(void *, void *, off_t, int); 129 130 static int wiiufb_dma_alloc(struct wiiufb_softc *, bus_size_t, bus_size_t, 131 int, struct wiiufb_dma *); 132 static void wiiufb_gpu_write(uint16_t, uint32_t); 133 static uint32_t wiiufb_gpu_read(uint16_t); 134 static void wiiufb_gpu_set(uint16_t, uint32_t); 135 static void wiiufb_gpu_clear(uint16_t, uint32_t); 136 137 void wiiufb_consinit(void); 138 139 static struct genfb_ops wiiufb_ops = { 140 .genfb_ioctl = wiiufb_ioctl, 141 .genfb_mmap = wiiufb_mmap, 142 }; 143 144 struct vcons_screen wiiufb_console_screen; 145 static struct wsscreen_descr wiiufb_stdscreen = { 146 "std", 147 0, 0, 148 0, 149 0, 0, 150 0, 151 NULL 152 }; 153 154 CFATTACH_DECL_NEW(wiiufb, sizeof(struct wiiufb_softc), 155 wiiufb_match, wiiufb_attach, NULL, NULL); 156 157 static int 158 wiiufb_match(device_t parent, cfdata_t cf, void *aux) 159 { 160 struct mainbus_attach_args *maa = aux; 161 162 return wiiu_native && strcmp(maa->maa_name, "genfb") == 0; 163 } 164 165 static void 166 wiiufb_attach(device_t parent, device_t self, void *aux) 167 { 168 struct wiiufb_softc *sc = device_private(self); 169 prop_dictionary_t dict = device_properties(self); 170 struct mainbus_attach_args *maa = aux; 171 void *bits; 172 int error; 173 174 sc->sc_gen.sc_dev = self; 175 sc->sc_bst = maa->maa_bst; 176 error = bus_space_map(sc->sc_bst, maa->maa_addr, WIIU_GX2_SIZE, 0, 177 &sc->sc_bsh); 178 if (error != 0) { 179 panic("couldn't map registers"); 180 } 181 sc->sc_dmat = maa->maa_dmat; 182 sc->sc_disp = wiiufb_drc ? 1 : 0; 183 184 wiiufb_sc = sc; 185 186 /* 187 * powerpc bus_space_map will use the BAT mapping if present, 188 * ignoring the map flags passed in. Just use mapiodev directly 189 * to ensure that the FB is mapped by the kernel pmap. 190 */ 191 bits = mapiodev(WIIUFB_BASE, WIIUFB_SIZE, true); 192 193 prop_dictionary_set_uint32(dict, "width", WIIUFB_WIDTH); 194 prop_dictionary_set_uint32(dict, "height", WIIUFB_HEIGHT); 195 prop_dictionary_set_uint8(dict, "depth", WIIUFB_BPP); 196 prop_dictionary_set_uint16(dict, "linebytes", WIIUFB_STRIDE); 197 prop_dictionary_set_uint32(dict, "address", WIIUFB_BASE); 198 prop_dictionary_set_uint32(dict, "virtual_address", (uintptr_t)bits); 199 200 genfb_init(&sc->sc_gen); 201 202 aprint_naive("\n"); 203 aprint_normal(": Wii U %s framebuffer (%ux%u %u-bpp @ 0x%08x)\n", 204 sc->sc_disp ? "DRC" : "TV", WIIUFB_WIDTH, WIIUFB_HEIGHT, 205 WIIUFB_BPP, WIIUFB_BASE); 206 207 pmf_device_register1(self, NULL, NULL, wiiufb_shutdown); 208 209 error = wiiufb_dma_alloc(sc, WIIUFB_CURSOR_SIZE, WIIUFB_CURSOR_ALIGN, 210 BUS_DMA_NOCACHE, &sc->sc_curdma); 211 if (error != 0) { 212 panic("couldn't alloc hardware cursor"); 213 } 214 wiiufb_gpu_set(DCUR_UPDATE(sc->sc_disp), DCUR_UPDATE_LOCK); 215 wiiufb_gpu_set(DCUR_CONTROL(sc->sc_disp), 0); 216 wiiufb_gpu_clear(DCUR_UPDATE(sc->sc_disp), DCUR_UPDATE_LOCK); 217 218 genfb_cnattach(); 219 prop_dictionary_set_bool(dict, "is_console", true); 220 genfb_attach(&sc->sc_gen, &wiiufb_ops); 221 } 222 223 static bool 224 wiiufb_shutdown(device_t self, int flags) 225 { 226 genfb_enable_polling(self); 227 return true; 228 } 229 230 static int 231 wiiufb_dma_alloc(struct wiiufb_softc *sc, bus_size_t size, bus_size_t align, 232 int flags, struct wiiufb_dma *dma) 233 { 234 bus_dma_tag_t dmat = sc->sc_dmat; 235 int error; 236 237 error = bus_dmamem_alloc(dmat, size, align, 0, 238 dma->dma_segs, 1, &dma->dma_nsegs, BUS_DMA_WAITOK); 239 if (error) 240 return error; 241 242 error = bus_dmamem_map(dmat, dma->dma_segs, dma->dma_nsegs, 243 size, &dma->dma_addr, BUS_DMA_WAITOK | flags); 244 if (error) 245 goto free; 246 247 error = bus_dmamap_create(dmat, size, dma->dma_nsegs, 248 size, 0, BUS_DMA_WAITOK, &dma->dma_map); 249 if (error) 250 goto unmap; 251 252 error = bus_dmamap_load(dmat, dma->dma_map, dma->dma_addr, 253 size, NULL, BUS_DMA_WAITOK); 254 if (error) 255 goto destroy; 256 257 dma->dma_size = size; 258 dma->dma_tag = dmat; 259 260 memset(dma->dma_addr, 0, dma->dma_size); 261 262 return 0; 263 264 destroy: 265 bus_dmamap_destroy(dmat, dma->dma_map); 266 unmap: 267 bus_dmamem_unmap(dmat, dma->dma_addr, dma->dma_size); 268 free: 269 bus_dmamem_free(dmat, dma->dma_segs, dma->dma_nsegs); 270 271 return error; 272 } 273 274 static void 275 wiiufb_cursor_shape(struct wiiufb_softc *sc) 276 { 277 const uint8_t *msk = sc->sc_cursor.c_mask; 278 const uint8_t *img = sc->sc_cursor.c_image; 279 uint32_t *out = sc->sc_curdma.dma_addr; 280 uint8_t bit; 281 int i, j, px; 282 283 for (i = 0; i < WIIUFB_CURMAX * NBBY; i++) { 284 bit = 1; 285 for (j = 0; j < 8; j++) { 286 px = ((*msk & bit) ? 2 : 0) | ((*img & bit) ? 1 : 0); 287 switch (px) { 288 case 0: 289 case 1: 290 *out = 0; 291 break; 292 case 2: 293 case 3: 294 *out = htole32( 295 0xff000000 | sc->sc_cursor.c_cmap[px - 2]); 296 break; 297 } 298 out++; 299 bit <<= 1; 300 } 301 msk++; 302 img++; 303 } 304 } 305 306 static void 307 wiiufb_cursor_visible(struct wiiufb_softc *sc) 308 { 309 uint32_t control; 310 311 wiiufb_gpu_set(DCUR_UPDATE(sc->sc_disp), DCUR_UPDATE_LOCK); 312 313 if (sc->sc_cursor.c_enable) { 314 control = DCUR_CONTROL_EN | DCUR_CONTROL_MODE_ARGB_PM; 315 } else { 316 control = 0; 317 } 318 wiiufb_gpu_write(DCUR_CONTROL(sc->sc_disp), control); 319 320 wiiufb_gpu_clear(DCUR_UPDATE(sc->sc_disp), DCUR_UPDATE_LOCK); 321 } 322 323 static void 324 wiiufb_cursor_position(struct wiiufb_softc *sc) 325 { 326 int x, y; 327 328 wiiufb_gpu_set(DCUR_UPDATE(sc->sc_disp), DCUR_UPDATE_LOCK); 329 330 x = uimin(sc->sc_cursor.c_pos.x, WIIUFB_WIDTH - 1); 331 y = uimin(sc->sc_cursor.c_pos.y, WIIUFB_HEIGHT - 1); 332 333 wiiufb_gpu_write(DCUR_SIZE(sc->sc_disp), 334 DCUR_SIZE_WIDTH(WIIUFB_CURMAX) | 335 DCUR_SIZE_HEIGHT(WIIUFB_CURMAX)); 336 wiiufb_gpu_write(DCUR_SURFACE_ADDRESS(sc->sc_disp), 337 sc->sc_curdma.dma_segs[0].ds_addr); 338 wiiufb_gpu_write(DCUR_POSITION(sc->sc_disp), 339 DCUR_POSITION_X(x) | 340 DCUR_POSITION_Y(y)); 341 wiiufb_gpu_write(DCUR_HOT_SPOT(sc->sc_disp), 342 DCUR_HOT_SPOT_X(sc->sc_cursor.c_hot.x) | 343 DCUR_HOT_SPOT_Y(sc->sc_cursor.c_hot.y)); 344 345 wiiufb_gpu_clear(DCUR_UPDATE(sc->sc_disp), DCUR_UPDATE_LOCK); 346 } 347 348 static void 349 wiiufb_cursor_update(struct wiiufb_softc *sc, unsigned which) 350 { 351 if ((which & (WSDISPLAY_CURSOR_DOCMAP|WSDISPLAY_CURSOR_DOSHAPE)) != 0) { 352 wiiufb_cursor_shape(sc); 353 } 354 355 if ((which & WSDISPLAY_CURSOR_DOCUR) != 0) { 356 wiiufb_cursor_visible(sc); 357 } 358 359 wiiufb_cursor_position(sc); 360 } 361 362 static int 363 wiiufb_set_cursor(struct wiiufb_softc *sc, struct wsdisplay_cursor *wc) 364 { 365 uint8_t r[2], g[2], b[2]; 366 unsigned which, index, count; 367 int i, err, pitch, size; 368 struct wiiufb_cursor *nc = &sc->sc_tempcursor; 369 370 which = wc->which; 371 *nc = sc->sc_cursor; 372 373 if ((which & WSDISPLAY_CURSOR_DOCMAP) != 0) { 374 index = wc->cmap.index; 375 count = wc->cmap.count; 376 377 if (index >= 2 || count > 2 - index) { 378 return EINVAL; 379 } 380 381 err = copyin(wc->cmap.red, &r[index], count); 382 if (err != 0) { 383 return err; 384 } 385 err = copyin(wc->cmap.green, &g[index], count); 386 if (err != 0) { 387 return err; 388 } 389 err = copyin(wc->cmap.blue, &b[index], count); 390 if (err != 0) { 391 return err; 392 } 393 394 for (i = index; i < index + count; i++) { 395 nc->c_cmap[i] = 396 (r[i] << 16) + (g[i] << 8) + (b[i] << 0); 397 } 398 } 399 400 if ((which & WSDISPLAY_CURSOR_DOSHAPE) != 0) { 401 if (wc->size.x > WIIUFB_CURMAX || wc->size.y > WIIUFB_CURMAX) { 402 return EINVAL; 403 } 404 405 pitch = (wc->size.x + 7) / 8; 406 size = pitch * wc->size.y; 407 408 memset(nc->c_image, 0, sizeof(nc->c_image)); 409 memset(nc->c_mask, 0, sizeof(nc->c_mask)); 410 nc->c_size = wc->size; 411 412 if ((err = copyin(wc->image, nc->c_image, size)) != 0) { 413 return err; 414 } 415 if ((err = copyin(wc->mask, nc->c_mask, size)) != 0) { 416 return err; 417 } 418 } 419 420 if ((which & WSDISPLAY_CURSOR_DOHOT) != 0) { 421 nc->c_hot = wc->hot; 422 if (nc->c_hot.x >= nc->c_size.x) { 423 nc->c_hot.x = nc->c_size.x - 1; 424 } 425 if (nc->c_hot.y >= nc->c_size.y) { 426 nc->c_hot.y = nc->c_size.y - 1; 427 } 428 } 429 430 if ((which & WSDISPLAY_CURSOR_DOPOS) != 0) { 431 nc->c_pos = wc->pos; 432 if (nc->c_pos.x >= WIIUFB_WIDTH) { 433 nc->c_pos.x = WIIUFB_WIDTH - 1; 434 } 435 if (nc->c_pos.y >= WIIUFB_HEIGHT) { 436 nc->c_pos.y = WIIUFB_HEIGHT - 1; 437 } 438 } 439 440 if ((which & WSDISPLAY_CURSOR_DOCUR) != 0) { 441 nc->c_enable = wc->enable; 442 } 443 444 sc->sc_cursor = *nc; 445 wiiufb_cursor_update(sc, wc->which); 446 447 return 0; 448 } 449 450 static int 451 wiiufb_set_curpos(struct wiiufb_softc *sc, struct wsdisplay_curpos *pos) 452 { 453 sc->sc_cursor.c_pos.x = uimin(uimax(pos->x, 0), WIIUFB_WIDTH - 1); 454 sc->sc_cursor.c_pos.y = uimin(uimax(pos->y, 0), WIIUFB_HEIGHT - 1); 455 456 wiiufb_cursor_position(sc); 457 458 return 0; 459 } 460 461 static int 462 wiiufb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l) 463 { 464 struct wiiufb_softc *sc = v; 465 struct wsdisplayio_bus_id *busid; 466 struct wsdisplayio_fbinfo *fbi; 467 struct wsdisplay_curpos *cp; 468 struct rasops_info *ri; 469 u_int video; 470 int error; 471 472 switch (cmd) { 473 case WSDISPLAYIO_GTYPE: 474 *(u_int *)data = WSDISPLAY_TYPE_GENFB; 475 return 0; 476 case WSDISPLAYIO_GET_BUSID: 477 busid = data; 478 busid->bus_type = WSDISPLAYIO_BUS_SOC; 479 return 0; 480 case WSDISPLAYIO_GET_FBINFO: 481 fbi = data; 482 ri = &sc->sc_gen.vd.active->scr_ri; 483 error = wsdisplayio_get_fbinfo(ri, fbi); 484 if (error == 0) { 485 fbi->fbi_flags |= WSFB_VRAM_IS_RAM; 486 } 487 return error; 488 case WSDISPLAYIO_SVIDEO: 489 video = *(u_int *)data; 490 switch (video) { 491 case WSDISPLAYIO_VIDEO_OFF: 492 wiiufb_gpu_write(DCRTC_BLANK_CONTROL(sc->sc_disp), 493 DCRTC_BLANK_DATA_EN); 494 break; 495 case WSDISPLAYIO_VIDEO_ON: 496 wiiufb_gpu_write(DCRTC_BLANK_CONTROL(sc->sc_disp), 0); 497 break; 498 default: 499 return EINVAL; 500 } 501 return 0; 502 case WSDISPLAYIO_GCURMAX: 503 cp = data; 504 cp->x = WIIUFB_CURMAX; 505 cp->y = WIIUFB_CURMAX; 506 return 0; 507 case WSDISPLAYIO_GCURPOS: 508 cp = data; 509 cp->x = sc->sc_cursor.c_pos.x; 510 cp->y = sc->sc_cursor.c_pos.y; 511 return 0; 512 case WSDISPLAYIO_SCURPOS: 513 return wiiufb_set_curpos(sc, data); 514 case WSDISPLAYIO_SCURSOR: 515 return wiiufb_set_cursor(sc, data); 516 } 517 518 return EPASSTHROUGH; 519 } 520 521 static paddr_t 522 wiiufb_mmap(void *v, void *vs, off_t off, int prot) 523 { 524 struct wiiufb_softc *sc = v; 525 526 if (off < 0 || off >= WIIUFB_SIZE) { 527 return -1; 528 } 529 530 return bus_space_mmap(sc->sc_bst, WIIUFB_BASE, off, prot, 531 BUS_SPACE_MAP_LINEAR | BUS_DMA_PREFETCHABLE); 532 } 533 534 static void 535 wiiufb_gpu_write(uint16_t reg, uint32_t data) 536 { 537 if (wiiufb_sc != NULL) { 538 bus_space_write_4(wiiufb_sc->sc_bst, wiiufb_sc->sc_bsh, 539 reg, data); 540 } else { 541 out32(LT_GPUINDADDR, LT_GPUINDADDR_REGSPACE_GPU | reg); 542 out32(LT_GPUINDDATA, data); 543 in32(LT_GPUINDDATA); 544 } 545 } 546 547 static uint32_t 548 wiiufb_gpu_read(uint16_t reg) 549 { 550 if (wiiufb_sc != NULL) { 551 return bus_space_read_4(wiiufb_sc->sc_bst, wiiufb_sc->sc_bsh, 552 reg); 553 } else { 554 out32(LT_GPUINDADDR, LT_GPUINDADDR_REGSPACE_GPU | reg); 555 return in32(LT_GPUINDDATA); 556 } 557 } 558 559 static void 560 wiiufb_gpu_set(uint16_t reg, uint32_t mask) 561 { 562 wiiufb_gpu_write(reg, wiiufb_gpu_read(reg) | mask); 563 } 564 565 static void 566 wiiufb_gpu_clear(uint16_t reg, uint32_t mask) 567 { 568 wiiufb_gpu_write(reg, wiiufb_gpu_read(reg) & ~mask); 569 } 570 571 void 572 wiiufb_consinit(void) 573 { 574 extern char wii_cmdline[]; 575 struct rasops_info *ri = &wiiufb_console_screen.scr_ri; 576 long defattr; 577 void *bits; 578 const char *cmdline = wii_cmdline; 579 580 memset(&wiiufb_console_screen, 0, sizeof(wiiufb_console_screen)); 581 582 while (*cmdline != '\0') { 583 if (strcmp(cmdline, "video=drc") == 0) { 584 /* Output to the gamepad instead of TV. */ 585 wiiufb_drc = true; 586 break; 587 } 588 cmdline += strlen(cmdline) + 1; 589 } 590 591 /* Blank the CRTC we are not using. */ 592 wiiufb_gpu_write(DCRTC_BLANK_CONTROL(wiiufb_drc ? 0 : 1), 593 DCRTC_BLANK_DATA_EN); 594 595 /* Ensure that the ARGB8888 framebuffer is in a sane state. */ 596 wiiufb_gpu_write(DGRPH_SWAP_CNTL(0), DGRPH_SWAP_ENDIAN_SWAP_8IN32); 597 wiiufb_gpu_write(DGRPH_SWAP_CNTL(1), DGRPH_SWAP_ENDIAN_SWAP_8IN32); 598 599 /* 600 * Need to use the BAT mapping here as pmap isn't initialized yet. 601 * 602 * Unfortunately, we have a single large (256MB) BAT mapping to cover 603 * both conventional memory and the framebuffer in MEM1, which means 604 * the early FB is mapped cacheable. Better than nothing (it's 605 * useful for debugging) and it's only like this until wiiufb is 606 * attached later on. 607 * 608 * This could be enhanced in the future to hook in to rasops and 609 * insert proper cache maintenance operations. Just don't flush the 610 * whole framebuffer every time something changes, it will be very 611 * slow. 612 */ 613 bits = (void *)WIIUFB_BASE; 614 615 wsfont_init(); 616 617 ri->ri_width = WIIUFB_WIDTH; 618 ri->ri_height = WIIUFB_HEIGHT; 619 ri->ri_depth = WIIUFB_BPP; 620 ri->ri_stride = WIIUFB_STRIDE; 621 ri->ri_bits = bits; 622 ri->ri_flg = RI_NO_AUTO | RI_CLEAR | RI_FULLCLEAR | RI_CENTER; 623 rasops_init(ri, ri->ri_height / 8, ri->ri_width / 8); 624 625 ri->ri_caps = WSSCREEN_WSCOLORS; 626 rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight, 627 ri->ri_width / ri->ri_font->fontwidth); 628 629 wiiufb_stdscreen.nrows = ri->ri_rows; 630 wiiufb_stdscreen.ncols = ri->ri_cols; 631 wiiufb_stdscreen.textops = &ri->ri_ops; 632 wiiufb_stdscreen.capabilities = ri->ri_caps; 633 634 ri->ri_ops.allocattr(ri, 0, 0, 0, &defattr); 635 636 wsdisplay_preattach(&wiiufb_stdscreen, ri, 0, 0, defattr); 637 } 638