wiifb.c revision 1.1
1/* $NetBSD: wiifb.c,v 1.1 2026/01/09 22:54:30 jmcneill Exp $ */ 2 3/*- 4 * Copyright (c) 2024-2025 Jared McNeill <jmcneill@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: wiifb.c,v 1.1 2026/01/09 22:54:30 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#include <powerpc/spr.h> 40#include <powerpc/oea/spr.h> 41#include <powerpc/oea/hid.h> 42 43#include <dev/videomode/videomode.h> 44#include <dev/wsfb/genfbvar.h> 45 46#include "mainbus.h" 47#include "vireg.h" 48#include "gxreg.h" 49 50#define WIIFB_RGB_WIDTH 640 51#define WIIFB_RGB_HEIGHT 480 52#define WIIFB_RGB_BPP 32 53 54#define WIIFB_ERROR_BLINK_INTERVAL 1000000 55 56#define WIIFB_TOP_BOTTOM_BORDER 16 57#define WIIFB_EFFECTIVE_START(p, w) \ 58 ((uintptr_t)(p) + WIIFB_TOP_BOTTOM_BORDER * (w) * 2) 59#define WIIFB_EFFECTIVE_HEIGHT(h) \ 60 ((h) - WIIFB_TOP_BOTTOM_BORDER * 2) 61 62#define WIIFB_FIFO_SIZE (256 * 1024) 63 64#define IBM750CL_SPR_HID2 920 65#define IBM750CL_SPR_HID2_WPE 0x40000000 /* Write pipe enable */ 66#define IBM750CL_SPR_WPAR 921 67 68struct wiifb_mode { 69 const char * name; 70 u_int width; 71 u_int height; 72 u_int lines; 73}; 74 75static uint32_t wiifb_devcmap[16] = { 76 0x00800080, /* Black */ 77 0x1dff1d6b, /* Blue */ 78 0x4b554b4a, /* Green */ 79 0x80808080, /* Cyan */ 80 0x4c544cff, /* Red */ 81 0x3aaa34b5, /* Magenta */ 82 0x7140718a, /* Brown */ 83 0xff80ff80, /* White */ 84 0x80808080, /* Gray */ 85 0xc399c36a, /* Bright Blue */ 86 0xd076d074, /* Bright Green */ 87 0x80808080, /* Bright Cyan */ 88 0x4c544cff, /* Bright Red */ 89 0x3aaa34b5, /* Bright Magenta */ 90 0xe100e194, /* Bright Yellow */ 91 0xff80ff80 /* Bright White */ 92}; 93 94#define WIIFB_MODE_INDEX(fmt, interlaced) ((fmt << 1) | interlaced) 95 96static const struct wiifb_mode wiifb_modes[] = { 97 [WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 0)] = { 98 .name = "NTSC 480p", 99 .width = 640, 100 .height = 480, 101 .lines = 525, 102 }, 103 [WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 1)] = { 104 .name = "NTSC 480i", 105 .width = 640, 106 .height = 480, 107 .lines = 525, 108 }, 109 [WIIFB_MODE_INDEX(VI_DCR_FMT_PAL, 1)] = { 110 .name = "PAL 576i", 111 .width = 640, 112 .height = 574, 113 .lines = 625, 114 }, 115 116}; 117#define WIIFB_NMODES __arraycount(wiifb_modes) 118 119struct wiifb_dma { 120 bus_dmamap_t dma_map; 121 bus_dma_tag_t dma_tag; 122 bus_size_t dma_size; 123 bus_dma_segment_t dma_segs[1]; 124 int dma_nsegs; 125 void *dma_addr; 126}; 127 128struct wiifb_softc { 129 struct genfb_softc sc_gen; 130 131 bus_space_tag_t sc_bst; 132 bus_space_handle_t sc_bsh; 133 bus_dma_tag_t sc_dmat; 134 135 void *sc_bits; 136 137 uint8_t sc_format; 138 bool sc_interlaced; 139 140 const struct wiifb_mode *sc_curmode; 141 142 u_int sc_wsmode; 143 144 volatile uint32_t *sc_efb; 145 volatile uint16_t *sc_cp; 146 volatile uint16_t *sc_pe; 147 volatile uint32_t *sc_pi; 148 gx_wgpipe_t *sc_wgpipe; 149 150 struct wiifb_dma sc_rgb; 151 struct wiifb_dma sc_fifo; 152 153 uint16_t sc_token; 154}; 155 156#define RD2(sc, reg) \ 157 bus_space_read_2((sc)->sc_bst, (sc)->sc_bsh, (reg)) 158#define RD4(sc, reg) \ 159 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 160#define WR2(sc, reg, val) \ 161 bus_space_write_2((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 162#define WR4(sc, reg, val) \ 163 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 164 165#define CP_WRITE(sc, off, val) (sc)->sc_cp[(off)] = (val) 166#define CP_READ(sc, off) (sc)->sc_cp[(off)] 167 168#define PE_WRITE(sc, off, val) (sc)->sc_pe[(off)] = (val) 169#define PE_READ(sc, off) (sc)->sc_pe[(off)] 170 171#define PI_WRITE(sc, off, val) (sc)->sc_pi[(off)] = (val) 172#define PI_READ(sc, off) (sc)->sc_pi[(off)] 173 174static int wiifb_match(device_t, cfdata_t, void *); 175static void wiifb_attach(device_t, device_t, void *); 176 177static void wiifb_accel_init(struct wiifb_softc *); 178static int wiifb_vi_intr(void *); 179static void wiifb_vi_refresh(void *); 180 181static void wiifb_init(struct wiifb_softc *); 182static void wiifb_set_mode(struct wiifb_softc *, uint8_t, bool); 183static void wiifb_set_fb(struct wiifb_softc *); 184static void wiifb_clear_xfb(struct wiifb_softc *); 185 186static int wiifb_ioctl(void *, void *, u_long, void *, int, lwp_t *); 187static paddr_t wiifb_mmap(void *, void *, off_t, int); 188 189static struct genfb_ops wiifb_ops = { 190 .genfb_ioctl = wiifb_ioctl, 191 .genfb_mmap = wiifb_mmap, 192}; 193 194CFATTACH_DECL_NEW(wiifb, sizeof(struct wiifb_softc), 195 wiifb_match, wiifb_attach, NULL, NULL); 196 197static int 198wiifb_match(device_t parent, cfdata_t cf, void *aux) 199{ 200 struct mainbus_attach_args *maa = aux; 201 202 return !wiiu_native && strcmp(maa->maa_name, "genfb") == 0; 203} 204 205static void 206wiifb_attach(device_t parent, device_t self, void *aux) 207{ 208 struct wiifb_softc *sc = device_private(self); 209 prop_dictionary_t dict = device_properties(self); 210 struct mainbus_attach_args *maa = aux; 211 int error; 212 213 sc->sc_gen.sc_dev = self; 214 sc->sc_bst = maa->maa_bst; 215 error = bus_space_map(sc->sc_bst, VI_BASE, VI_SIZE, 0, &sc->sc_bsh); 216 if (error != 0) { 217 panic("couldn't map registers"); 218 } 219 sc->sc_bits = mapiodev(XFB_START, XFB_SIZE, true); 220 sc->sc_wsmode = WSDISPLAYIO_MODE_EMUL; 221 sc->sc_dmat = maa->maa_dmat; 222 223 wiifb_clear_xfb(sc); 224 225 wiifb_init(sc); 226 wiifb_set_mode(sc, sc->sc_format, sc->sc_interlaced); 227 228 prop_dictionary_set_uint32(dict, "width", sc->sc_curmode->width); 229 prop_dictionary_set_uint32(dict, "height", 230 WIIFB_EFFECTIVE_HEIGHT(sc->sc_curmode->height)); 231 prop_dictionary_set_uint8(dict, "depth", 16); 232 prop_dictionary_set_uint32(dict, "address", XFB_START); 233 prop_dictionary_set_uint32(dict, "virtual_address", 234 WIIFB_EFFECTIVE_START(sc->sc_bits, sc->sc_curmode->width)); 235 prop_dictionary_set_uint64(dict, "devcmap", (uintptr_t)wiifb_devcmap); 236 237 genfb_init(&sc->sc_gen); 238 239 aprint_naive("\n"); 240 aprint_normal(": %s\n", sc->sc_curmode->name); 241 242 genfb_cnattach(); 243 prop_dictionary_set_bool(dict, "is_console", true); 244 genfb_attach(&sc->sc_gen, &wiifb_ops); 245 246 wiifb_accel_init(sc); 247} 248 249static void 250wiifb_clear_xfb(struct wiifb_softc *sc) 251{ 252 u_int offset; 253 uint32_t *p; 254 255 /* 256 * Paint the entire XFB black. Use 4-byte accesses as the Wii will 257 * ignore 1- and 2- byte writes to uncached memory. 258 */ 259 for (p = sc->sc_bits, offset = 0; 260 offset < XFB_SIZE; 261 offset += 4, p++) { 262 *p = wiifb_devcmap[0]; 263 } 264} 265 266static int 267wiifb_vi_init(struct wiifb_softc *sc) 268{ 269 device_t dev = sc->sc_gen.sc_dev; 270 void *ih; 271 272 WR4(sc, VI_DI0, VI_DI_ENB | 273 __SHIFTIN(1, VI_DI_VCT) | 274 __SHIFTIN(1, VI_DI_HCT)); 275 WR4(sc, VI_DI1, 0); 276 WR4(sc, VI_DI2, 0); 277 WR4(sc, VI_DI3, 0); 278 279 ih = intr_establish_xname(PI_IRQ_VI, IST_LEVEL, IPL_TTY, 280 wiifb_vi_intr, sc, device_xname(dev)); 281 if (ih == NULL) { 282 aprint_error_dev(dev, "failed to install VI intr handler\n"); 283 return EIO; 284 } 285 286 return 0; 287} 288 289static int 290wiifb_dma_alloc(struct wiifb_softc *sc, bus_size_t size, bus_size_t align, 291 int flags, struct wiifb_dma *dma) 292{ 293 bus_dma_tag_t dmat = sc->sc_dmat; // &wii_mem1_bus_dma_tag; 294 int error; 295 296 error = bus_dmamem_alloc(dmat, size, align, 0, 297 dma->dma_segs, 1, &dma->dma_nsegs, BUS_DMA_WAITOK); 298 if (error) 299 return error; 300 301 error = bus_dmamem_map(dmat, dma->dma_segs, dma->dma_nsegs, 302 size, &dma->dma_addr, BUS_DMA_WAITOK | flags); 303 if (error) 304 goto free; 305 306 error = bus_dmamap_create(dmat, size, dma->dma_nsegs, 307 size, 0, BUS_DMA_WAITOK, &dma->dma_map); 308 if (error) 309 goto unmap; 310 311 error = bus_dmamap_load(dmat, dma->dma_map, dma->dma_addr, 312 size, NULL, BUS_DMA_WAITOK); 313 if (error) 314 goto destroy; 315 316 dma->dma_size = size; 317 dma->dma_tag = dmat; 318 319 memset(dma->dma_addr, 0, dma->dma_size); 320 321 return 0; 322 323destroy: 324 bus_dmamap_destroy(dmat, dma->dma_map); 325unmap: 326 bus_dmamem_unmap(dmat, dma->dma_addr, dma->dma_size); 327free: 328 bus_dmamem_free(dmat, dma->dma_segs, dma->dma_nsegs); 329 330 return error; 331} 332 333static void * 334wiifb_mapreg(struct wiifb_softc *sc, bus_addr_t base, bus_size_t size) 335{ 336 bus_space_handle_t bsh; 337 int error; 338 339 error = bus_space_map(sc->sc_bst, base, size, 340 BUS_SPACE_MAP_LINEAR, &bsh); 341 if (error != 0) { 342 panic("couldn't map 0x%x", base); 343 } 344 return bus_space_vaddr(sc->sc_bst, bsh); 345} 346 347static void 348wiifb_set_wgpipe(bus_addr_t base) 349{ 350 uint32_t value; 351 352 if (base) { 353 mtspr(IBM750CL_SPR_WPAR, base); 354 } 355 value = mfspr(IBM750CL_SPR_HID2); 356 if (base) { 357 value |= IBM750CL_SPR_HID2_WPE; 358 } else { 359 value &= ~IBM750CL_SPR_HID2_WPE; 360 } 361 mtspr(IBM750CL_SPR_HID2, value); 362} 363 364static void 365wiifb_ppcsync(void) 366{ 367 uint32_t value; 368 369 value = mfspr(SPR_HID0); 370 mtspr(SPR_HID0, value | 0x8); 371 asm volatile("isync" ::: "memory"); 372 asm volatile("sync" ::: "memory"); 373 mtspr(SPR_HID0, value); 374} 375 376static void 377wiifb_gx_bp_load(struct wiifb_softc *sc, uint32_t data) 378{ 379 GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x61); 380 GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data); 381} 382 383static void 384wiifb_gx_cp_load(struct wiifb_softc *sc, uint8_t addr, uint32_t data) 385{ 386 GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x08); 387 GX_STRICT_ORDER(sc->sc_wgpipe->u8 = addr); 388 GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data); 389} 390 391static void 392wiifb_gx_xf_load(struct wiifb_softc *sc, uint16_t addr, uint32_t data) 393{ 394 GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x10); 395 GX_STRICT_ORDER(sc->sc_wgpipe->u32 = addr); 396 GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data); 397} 398 399static void 400wiifb_gx_xf_load_multi(struct wiifb_softc *sc, uint16_t addr, 401 uint16_t count, uint32_t *data) 402{ 403 uint16_t n; 404 405 GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x10); 406 GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 407 (((uint32_t)count - 1) << 16) | addr); 408 for (n = 0; n < count; n++) { 409 GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data[n]); 410 } 411} 412 413static void 414wiifb_gx_set_viewport(struct wiifb_softc *sc, u_int w, u_int h) 415{ 416 uint32_t data[6]; 417 418 KASSERT(w == 640); 419 KASSERT(h == 480); 420 421 data[0] = 0x00000140; 422 data[1] = 0xffffff10; 423 data[2] = 0x00ffffff; 424 data[3] = 0x00000296; 425 data[4] = 0x00000246; 426 data[5] = 0x00ffffff; 427 428 wiifb_gx_xf_load_multi(sc, GX_XF_VIEWPORT_X0, 429 __arraycount(data), data); 430} 431 432static void 433wiifb_gx_set_scissor(struct wiifb_softc *sc, u_int x, u_int y, 434 u_int w, u_int h) 435{ 436 uint32_t xo = x + 0x156; 437 uint32_t yo = y + 0x156; 438 uint32_t wo = xo + w - 1; 439 uint32_t ho = yo + h - 1; 440 441 wiifb_gx_bp_load(sc, 0x20000000 | yo | (xo << 12)); 442 wiifb_gx_bp_load(sc, 0x21000000 | ho | (wo << 12)); 443 wiifb_gx_bp_load(sc, 0x59000000 | GX_XY(xo >> 1, yo >> 1)); 444} 445 446static void 447wiifb_gx_init(struct wiifb_softc *sc) 448{ 449 const uint32_t fifo_start = sc->sc_fifo.dma_segs[0].ds_addr; 450 const uint32_t fifo_end = fifo_start + sc->sc_fifo.dma_size - 4; 451 const uint32_t fifo_hiwat = GX_FIFO_HIWAT(sc->sc_fifo.dma_size); 452 const uint32_t fifo_lowat = GX_FIFO_LOWAT(sc->sc_fifo.dma_size); 453 454 /* Disable WGPIPE and unlink CP FIFO before changing settings. */ 455 wiifb_set_wgpipe(0); 456 CP_WRITE(sc, CP_CR, 0); 457 CP_WRITE(sc, CP_CLEAR, CP_CLEAR_UNDERFLOW | CP_CLEAR_OVERFLOW); 458 459 /* Setup GP FIFO */ 460 CP_WRITE(sc, CP_FIFO_BASE_LO, LOWER_16_BITS(fifo_start)); 461 CP_WRITE(sc, CP_FIFO_BASE_HI, UPPER_16_BITS(fifo_start)); 462 CP_WRITE(sc, CP_FIFO_END_LO, LOWER_16_BITS(fifo_end)); 463 CP_WRITE(sc, CP_FIFO_END_HI, UPPER_16_BITS(fifo_end)); 464 CP_WRITE(sc, CP_FIFO_HIWAT_LO, LOWER_16_BITS(fifo_hiwat)); 465 CP_WRITE(sc, CP_FIFO_HIWAT_HI, UPPER_16_BITS(fifo_hiwat)); 466 CP_WRITE(sc, CP_FIFO_LOWAT_LO, LOWER_16_BITS(fifo_lowat)); 467 CP_WRITE(sc, CP_FIFO_LOWAT_HI, UPPER_16_BITS(fifo_lowat)); 468 CP_WRITE(sc, CP_FIFO_RW_DIST_LO, 0); 469 CP_WRITE(sc, CP_FIFO_RW_DIST_HI, 0); 470 CP_WRITE(sc, CP_FIFO_WRITE_PTR_LO, LOWER_16_BITS(fifo_start)); 471 CP_WRITE(sc, CP_FIFO_WRITE_PTR_HI, UPPER_16_BITS(fifo_start)); 472 CP_WRITE(sc, CP_FIFO_READ_PTR_LO, LOWER_16_BITS(fifo_start)); 473 CP_WRITE(sc, CP_FIFO_READ_PTR_HI, UPPER_16_BITS(fifo_start)); 474 wiifb_ppcsync(); 475 476 /* Setup CPU FIFO */ 477 PI_WRITE(sc, PI_FIFO_BASE_START, fifo_start); 478 PI_WRITE(sc, PI_FIFO_BASE_END, fifo_end); 479 PI_WRITE(sc, PI_FIFO_WRITE_PTR, fifo_start); 480 wiifb_ppcsync(); 481 482 /* Link CP/PE FIFO and enable GP FIFO */ 483 CP_WRITE(sc, CP_CR, CP_CR_GP_LINK_ENABLE); 484 CP_WRITE(sc, CP_CR, CP_READ(sc, CP_CR) | CP_CR_READ_ENABLE); 485 486 /* Init pixel engine */ 487 PE_WRITE(sc, PE_ZCONF, 488 PE_ZCONF_UPD_ENABLE | 489 PE_ZCONF_FUNC_ALWAYS | 490 PE_ZCONF_COMP_ENABLE); 491 PE_WRITE(sc, PE_ALPHA_CONF, 492 PE_ALPHA_CONF_OP_SET | 493 PE_ALPHA_CONF_SRC_1 | 494 PE_ALPHA_CONF_DST_0 | 495 PE_ALPHA_CONF_UPD_A | 496 PE_ALPHA_CONF_UPD_C); 497 PE_WRITE(sc, PE_ALPHA_DEST, 0); 498 PE_WRITE(sc, PE_ALPHA_MODE, PE_ALPHA_MODE_ALWAYS); 499 PE_WRITE(sc, PE_ALPHA_READ, 500 PE_ALPHA_READ_UNK | PE_ALPHA_READ_FF); 501 502 /* Enable WG pipe */ 503 wiifb_set_wgpipe(WGPIPE_BASE); 504 505 /* Sanitize command processor registers */ 506 for (int n = 0; n < 8; n++) { 507 wiifb_gx_cp_load(sc, 0x80 + n, 0x80000000); 508 } 509 wiifb_gx_cp_load(sc, 0x20, 0); 510 511 /* Sanitize transform unit registers */ 512 wiifb_gx_xf_load(sc, 0x1000, 0x3f); 513 wiifb_gx_xf_load(sc, 0x1005, 0x01); 514 wiifb_gx_xf_load(sc, 0x1012, 0x01); 515 wiifb_gx_xf_load(sc, 0x1006, 0); 516 517 /* Initialize blitting processor */ 518 wiifb_gx_bp_load(sc, 0x00000001); 519 wiifb_gx_bp_load(sc, 0x01666666); 520 wiifb_gx_bp_load(sc, 0x02666666); 521 wiifb_gx_bp_load(sc, 0x03666666); 522 wiifb_gx_bp_load(sc, 0x04666666); 523 wiifb_gx_bp_load(sc, 0x22000606); 524 wiifb_gx_bp_load(sc, 0x23000000); 525 wiifb_gx_bp_load(sc, 0x24000000); 526 wiifb_gx_bp_load(sc, 0x42000000); 527 wiifb_gx_bp_load(sc, 0x44000003); 528 wiifb_gx_bp_load(sc, 0x43000000); 529 wiifb_gx_bp_load(sc, 0x53595000); 530 wiifb_gx_bp_load(sc, 0x54000015); 531 wiifb_gx_bp_load(sc, 0x550003ff); 532 wiifb_gx_bp_load(sc, 0x560003ff); 533 wiifb_gx_bp_load(sc, 0x5800000f); 534 wiifb_gx_bp_load(sc, 0x67000000); 535 536 /* Set viewport and scissor parameters */ 537 wiifb_gx_set_viewport(sc, WIIFB_RGB_WIDTH, WIIFB_RGB_HEIGHT); 538 wiifb_gx_set_scissor(sc, 0, 0, WIIFB_RGB_WIDTH, WIIFB_RGB_HEIGHT); 539 540 wiifb_gx_bp_load(sc, 0x4000001f); 541 542 if (sc->sc_curmode->height == 574) { 543 /* Scale 480 lines to PAL display height */ 544 wiifb_gx_bp_load(sc, 0x4e000127); 545 } 546 547 /* Copy mode parameters */ 548 wiifb_gx_bp_load(sc, 0x410004bc); 549 550 /* Copy source */ 551 wiifb_gx_bp_load(sc, 0x49000000 | GX_XY(0, 0)); 552 wiifb_gx_bp_load(sc, 0x4a000000 | 553 GX_XY(WIIFB_RGB_WIDTH - 1, WIIFB_RGB_HEIGHT - 1)); 554 555 /* Copy destination */ 556 wiifb_gx_bp_load(sc, 0x4d000000 | (WIIFB_RGB_WIDTH >> 4)); 557 558 /* XFB address */ 559 wiifb_gx_bp_load(sc, 0x4b000000 | (XFB_START >> 5)); 560 561 /* Copy clear settings */ 562 wiifb_gx_bp_load(sc, 0x4f000000); 563 wiifb_gx_bp_load(sc, 0x50000000); 564 wiifb_gx_bp_load(sc, 0x5100ffff); 565} 566 567static void 568wiifb_gx_flush(struct wiifb_softc *sc) 569{ 570 GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 571 GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 572 GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 573 GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 574 GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 575 GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 576 GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 577 GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 578 wiifb_ppcsync(); 579} 580 581static void 582wiifb_accel_init(struct wiifb_softc *sc) 583{ 584 bus_size_t rgb_size; 585 bus_size_t fifo_size; 586 587 if (wiifb_vi_init(sc) != 0) { 588 panic("couldn't init VI"); 589 } 590 591 rgb_size = WIIFB_RGB_WIDTH * WIIFB_RGB_HEIGHT; 592 rgb_size = rgb_size * WIIFB_RGB_BPP / NBBY; 593 rgb_size = roundup(rgb_size, PAGE_SIZE); 594 if (wiifb_dma_alloc(sc, rgb_size, PAGE_SIZE, BUS_DMA_PREFETCHABLE, 595 &sc->sc_rgb) != 0) { 596 panic("couldn't alloc rgb fb"); 597 } 598 599 fifo_size = WIIFB_FIFO_SIZE; 600 if (wiifb_dma_alloc(sc, fifo_size, GX_FIFO_ALIGN, BUS_DMA_NOCACHE, 601 &sc->sc_fifo) != 0) { 602 panic("couldn't alloc gx fifo"); 603 } 604 605 sc->sc_efb = wiifb_mapreg(sc, EFB_BASE, EFB_SIZE); 606 sc->sc_cp = wiifb_mapreg(sc, CP_BASE, CP_SIZE); 607 sc->sc_pe = wiifb_mapreg(sc, PE_BASE, PE_SIZE); 608 sc->sc_pi = wiifb_mapreg(sc, PI_BASE, PI_SIZE); 609 sc->sc_wgpipe = wiifb_mapreg(sc, WGPIPE_BASE, WGPIPE_SIZE); 610 611 wiifb_gx_init(sc); 612}; 613 614static void 615wiifb_rgb_to_efb(struct wiifb_softc *sc) 616{ 617 u_int y; 618 uint32_t *src = sc->sc_rgb.dma_addr; 619 uint32_t *dst = __UNVOLATILE(sc->sc_efb); 620 register_t hid0 = mfspr(SPR_HID0); 621 622 KASSERT(src != NULL); 623 KASSERT(dst != NULL); 624 625 /* Disable store gathering while writing to EFB. */ 626 mtspr(SPR_HID0, hid0 & ~HID0_SGE); 627 628 for (y = 0; y < WIIFB_RGB_HEIGHT; y++) { 629 memcpy(dst, src, WIIFB_RGB_WIDTH * 4); 630 src += WIIFB_RGB_WIDTH; 631 dst += 1024; 632 } 633 634 /* Re-enable store gathering. */ 635 mtspr(SPR_HID0, hid0); 636} 637 638static void 639wiifb_efb_to_xfb(struct wiifb_softc *sc) 640{ 641 const uint32_t copy_mask = sc->sc_curmode->height == 574 ? 0x400 : 0x0; 642 643 /* Execute copy to XFB */ 644 wiifb_gx_bp_load(sc, 0x52004803 | copy_mask); 645} 646 647static void 648wiifb_gx_draw_done(struct wiifb_softc *sc, uint16_t token) 649{ 650 /* Draw done */ 651 wiifb_gx_bp_load(sc, 0x45000002); 652 /* Write tokens */ 653 wiifb_gx_bp_load(sc, 0x48000000 | token); 654 wiifb_gx_bp_load(sc, 0x47000000 | token); 655 /* Flush WG pipe */ 656 wiifb_gx_flush(sc); 657} 658 659static void 660wiifb_vi_refresh(void *priv) 661{ 662 struct wiifb_softc *sc = priv; 663 664 wiifb_rgb_to_efb(sc); 665 wiifb_efb_to_xfb(sc); 666 wiifb_gx_draw_done(sc, sc->sc_token++); 667} 668 669static int 670wiifb_vi_intr(void *priv) 671{ 672 struct wiifb_softc *sc = priv; 673 uint32_t di0; 674 int ret = 0; 675 676 di0 = RD4(sc, VI_DI0); 677 678 WR4(sc, VI_DI0, RD4(sc, VI_DI0) & ~VI_DI_INT); 679 WR4(sc, VI_DI1, RD4(sc, VI_DI1) & ~VI_DI_INT); 680 WR4(sc, VI_DI2, RD4(sc, VI_DI2) & ~VI_DI_INT); 681 WR4(sc, VI_DI3, RD4(sc, VI_DI3) & ~VI_DI_INT); 682 683 if ((di0 & VI_DI_INT) != 0 && 684 sc->sc_wsmode != WSDISPLAYIO_MODE_EMUL) { 685 wiifb_vi_refresh(sc); 686 ret = 1; 687 } 688 689 return ret; 690} 691 692static void 693wiifb_init(struct wiifb_softc *sc) 694{ 695 uint16_t dcr; 696 uint16_t visel; 697 698 /* Read current display format and interlaced settings. */ 699 dcr = RD2(sc, VI_DCR); 700 if ((dcr & VI_DCR_ENB) != 0) { 701 sc->sc_format = __SHIFTOUT(dcr, VI_DCR_FMT); 702 sc->sc_interlaced = (dcr & VI_DCR_NIN) == 0; 703 } else { 704 visel = RD2(sc, VI_VISEL); 705 sc->sc_format = VI_DCR_FMT_NTSC; 706 sc->sc_interlaced = (visel & VI_VISEL_COMPONENT_CABLE) == 0; 707 } 708 709 /* Reset video interface. */ 710 WR2(sc, VI_DCR, VI_DCR_RST); 711 delay(1000); 712 713 /* Initialize video format and interlace selector. */ 714 dcr = __SHIFTIN(sc->sc_format, VI_DCR_FMT) | 715 (sc->sc_interlaced ? 0 : VI_DCR_NIN); 716 WR2(sc, VI_DCR, dcr); 717} 718 719static void 720wiifb_set_mode(struct wiifb_softc *sc, uint8_t format, bool interlaced) 721{ 722 u_int modeidx; 723 u_int strides, reads; 724 725 modeidx = WIIFB_MODE_INDEX(format, interlaced); 726 if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 1)) { 727 /* NTSC 480i Magic numbers from YAGCD. */ 728 WR2(sc, VI_VTR, 0x0f06); 729 WR4(sc, VI_HTR0, 0x476901AD); 730 WR4(sc, VI_HTR1, 0x02EA5140); 731 WR4(sc, VI_VTO, 0x00030018); 732 WR4(sc, VI_VTE, 0x00020019); 733 WR4(sc, VI_BBOI, 0x410C410C); 734 WR4(sc, VI_BBEI, 0x40ED40ED); 735 WR2(sc, VI_DPV, 0x0000); 736 WR2(sc, VI_DPH, 0x0000); 737 } else if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 0)) { 738 /* NTSC 480p */ 739 WR2(sc, VI_VTR, 0x1e0c); 740 WR4(sc, VI_HTR0, 0x476901ad); 741 WR4(sc, VI_HTR1, 0x030a4940); 742 WR4(sc, VI_VTO, 0x00060030); 743 WR4(sc, VI_VTE, 0x00060030); 744 WR4(sc, VI_BBOI, 0x81d881d8); 745 WR4(sc, VI_BBEI, 0x81d881d8); 746 WR2(sc, VI_DPV, 0x0000); 747 WR2(sc, VI_DPH, 0x0000); 748 } else if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_PAL, 1)) { 749 /* PAL 576i */ 750 WR2(sc, VI_VTR, 0x11f5); 751 WR4(sc, VI_HTR0, 0x4b6a01b0); 752 WR4(sc, VI_HTR1, 0x02f85640); 753 WR4(sc, VI_VTO, 0x00010023); 754 WR4(sc, VI_VTE, 0x00000024); 755 WR4(sc, VI_BBOI, 0x4d2b4d6d); 756 WR4(sc, VI_BBEI, 0x4d8a4d4c); 757 WR2(sc, VI_DPV, 0x013c); 758 WR2(sc, VI_DPH, 0x0144); 759 } else { 760 /* 761 * Display mode is not supported. Blink the slot LED to 762 * indicate failure. 763 */ 764 wii_slot_led_blink(WIIFB_ERROR_BLINK_INTERVAL); 765 } 766 767 if (modeidx >= WIIFB_NMODES || wiifb_modes[modeidx].name == NULL) { 768 panic("Unsupported format (0x%x) / interlaced (%d) settings", 769 sc->sc_format, sc->sc_interlaced); 770 } 771 sc->sc_curmode = &wiifb_modes[modeidx]; 772 773 /* Filter coefficient table, values from YAGCD. */ 774 WR4(sc, VI_FCT0, 0x1ae771f0); 775 WR4(sc, VI_FCT1, 0x0db4a574); 776 WR4(sc, VI_FCT2, 0x00c1188e); 777 WR4(sc, VI_FCT3, 0xc4c0cbe2); 778 WR4(sc, VI_FCT4, 0xfcecdecf); 779 WR4(sc, VI_FCT5, 0x13130f08); 780 WR4(sc, VI_FCT6, 0x00080C0f); 781 782 /* Unknown registers. */ 783 WR4(sc, VI_UNKNOWN_68H, 0x00ff0000); 784 WR2(sc, VI_UNKNOWN_76H, 0x00ff); 785 WR4(sc, VI_UNKNOWN_78H, 0x00ff00ff); 786 WR4(sc, VI_UNKNOWN_7CH, 0x00ff00ff); 787 788 /* Picture configuration */ 789 strides = (sc->sc_curmode->width * 2) / (interlaced ? 16 : 32); 790 reads = (sc->sc_curmode->width * 2) / 32; 791 WR2(sc, VI_PICCONF, 792 __SHIFTIN(strides, VI_PICCONF_STRIDES) | 793 __SHIFTIN(reads, VI_PICCONF_READS)); 794 795 /* Horizontal scaler configuration */ 796 if (interlaced) { 797 WR2(sc, VI_HSR, __SHIFTIN(256, VI_HSR_STP)); 798 } else { 799 WR2(sc, VI_HSR, __SHIFTIN(244, VI_HSR_STP) | VI_HSR_HS_EN); 800 } 801 802 /* Video clock configuration */ 803 WR2(sc, VI_VICLK, 804 interlaced ? VI_VICLK_SEL_27MHZ : VI_VICLK_SEL_54MHZ); 805 806 /* Horizontal scaling width */ 807 WR2(sc, VI_HSCALINGW, sc->sc_curmode->width); 808 809 /* Set framebuffer address */ 810 wiifb_set_fb(sc); 811 812 /* Finally, enable the framebuffer */ 813 WR2(sc, VI_DCR, RD2(sc, VI_DCR) | VI_DCR_ENB); 814} 815 816static void 817wiifb_set_fb(struct wiifb_softc *sc) 818{ 819 uint32_t taddr = XFB_START; 820 uint32_t baddr = taddr + (sc->sc_interlaced ? 821 sc->sc_curmode->width * 2 : 0); 822 823 WR4(sc, VI_TFBL, 824 VI_TFBL_PGOFF | 825 __SHIFTIN((taddr >> 5), VI_TFBL_FBB) | 826 __SHIFTIN((taddr / 2) & 0xf, VI_TFBL_XOF)); 827 WR4(sc, VI_TFBR, 0); 828 829 WR4(sc, VI_BFBL, 830 VI_BFBL_PGOFF | 831 __SHIFTIN((baddr >> 5), VI_BFBL_FBB) | 832 __SHIFTIN((baddr / 2) & 0xf, VI_BFBL_XOF)); 833 WR4(sc, VI_BFBR, 0); 834} 835 836static int 837wiifb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l) 838{ 839 struct wiifb_softc *sc = v; 840 struct wsdisplayio_bus_id *busid; 841 struct wsdisplayio_fbinfo *fbi; 842 u_int video; 843 u_int wsmode; 844 845 switch (cmd) { 846 case WSDISPLAYIO_GTYPE: 847 *(u_int *)data = WSDISPLAY_TYPE_GENFB; 848 return 0; 849 case WSDISPLAYIO_GET_BUSID: 850 busid = data; 851 busid->bus_type = WSDISPLAYIO_BUS_SOC; 852 return 0; 853 case WSDISPLAYIO_GET_FBINFO: 854 fbi = data; 855 /* 856 * rasops info does not match the pixel encoding due to our 857 * devcmap, so fill out fbinfo manually instead of relying 858 * on wsdisplayio_get_fbinfo. 859 */ 860 fbi->fbi_fboffset = 0; 861 fbi->fbi_width = WIIFB_RGB_WIDTH; 862 fbi->fbi_height = WIIFB_RGB_HEIGHT; 863 fbi->fbi_bitsperpixel = WIIFB_RGB_BPP; 864 fbi->fbi_stride = fbi->fbi_width * fbi->fbi_bitsperpixel / 8; 865 fbi->fbi_fbsize = fbi->fbi_height * fbi->fbi_stride; 866 fbi->fbi_pixeltype = WSFB_RGB; 867 fbi->fbi_flags = WSFB_VRAM_IS_RAM; 868 return 0; 869 870 case WSDISPLAYIO_SVIDEO: 871 video = *(u_int *)data; 872 switch (video) { 873 case WSDISPLAYIO_VIDEO_OFF: 874 out32(HW_VIDIM, __SHIFTIN(7, VIDIM_Y) | 875 __SHIFTIN(7, VIDIM_C) | 876 VIDIM_E); 877 return 0; 878 case WSDISPLAYIO_VIDEO_ON: 879 out32(HW_VIDIM, 0); 880 return 0; 881 default: 882 return EINVAL; 883 } 884 885 case WSDISPLAYIO_SMODE: 886 wsmode = *(u_int *)data; 887 if (wsmode != WSDISPLAYIO_MODE_EMUL) { 888 /* Blank the RGB FB when leaving text mode */ 889 memset(sc->sc_rgb.dma_addr, 0, sc->sc_rgb.dma_size); 890 } 891 if (sc->sc_wsmode != wsmode) { 892 sc->sc_wsmode = wsmode; 893 894 if (wsmode == WSDISPLAYIO_MODE_EMUL) { 895 wiifb_clear_xfb(sc); 896 } 897 } 898 return EPASSTHROUGH; 899 } 900 901 return EPASSTHROUGH; 902} 903 904static paddr_t 905wiifb_mmap(void *v, void *vs, off_t off, int prot) 906{ 907 struct wiifb_softc *sc = v; 908 909 if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL || sc->sc_efb == NULL) { 910 return -1; 911 } 912 913 if (off < 0 || off >= sc->sc_rgb.dma_size) { 914 return -1; 915 } 916 917 return bus_dmamem_mmap(sc->sc_rgb.dma_tag, 918 sc->sc_rgb.dma_segs, sc->sc_rgb.dma_nsegs, 919 off, prot, BUS_DMA_PREFETCHABLE); 920} 921