11.1Sjmcneill/* $NetBSD: wiifb.c,v 1.1 2026/01/09 22:54:30 jmcneill Exp $ */ 21.1Sjmcneill 31.1Sjmcneill/*- 41.1Sjmcneill * Copyright (c) 2024-2025 Jared McNeill <jmcneill@invisible.ca> 51.1Sjmcneill * All rights reserved. 61.1Sjmcneill * 71.1Sjmcneill * Redistribution and use in source and binary forms, with or without 81.1Sjmcneill * modification, are permitted provided that the following conditions 91.1Sjmcneill * are met: 101.1Sjmcneill * 1. Redistributions of source code must retain the above copyright 111.1Sjmcneill * notice, this list of conditions and the following disclaimer. 121.1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright 131.1Sjmcneill * notice, this list of conditions and the following disclaimer in the 141.1Sjmcneill * documentation and/or other materials provided with the distribution. 151.1Sjmcneill * 161.1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 171.1Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 181.1Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 191.1Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 201.1Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 211.1Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 221.1Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 231.1Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 241.1Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 251.1Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 261.1Sjmcneill * SUCH DAMAGE. 271.1Sjmcneill */ 281.1Sjmcneill 291.1Sjmcneill#include <sys/cdefs.h> 301.1Sjmcneill__KERNEL_RCSID(0, "$NetBSD: wiifb.c,v 1.1 2026/01/09 22:54:30 jmcneill Exp $"); 311.1Sjmcneill 321.1Sjmcneill#include <sys/param.h> 331.1Sjmcneill#include <sys/bus.h> 341.1Sjmcneill#include <sys/device.h> 351.1Sjmcneill#include <sys/systm.h> 361.1Sjmcneill 371.1Sjmcneill#include <machine/wii.h> 381.1Sjmcneill#include <machine/wiiu.h> 391.1Sjmcneill#include <powerpc/spr.h> 401.1Sjmcneill#include <powerpc/oea/spr.h> 411.1Sjmcneill#include <powerpc/oea/hid.h> 421.1Sjmcneill 431.1Sjmcneill#include <dev/videomode/videomode.h> 441.1Sjmcneill#include <dev/wsfb/genfbvar.h> 451.1Sjmcneill 461.1Sjmcneill#include "mainbus.h" 471.1Sjmcneill#include "vireg.h" 481.1Sjmcneill#include "gxreg.h" 491.1Sjmcneill 501.1Sjmcneill#define WIIFB_RGB_WIDTH 640 511.1Sjmcneill#define WIIFB_RGB_HEIGHT 480 521.1Sjmcneill#define WIIFB_RGB_BPP 32 531.1Sjmcneill 541.1Sjmcneill#define WIIFB_ERROR_BLINK_INTERVAL 1000000 551.1Sjmcneill 561.1Sjmcneill#define WIIFB_TOP_BOTTOM_BORDER 16 571.1Sjmcneill#define WIIFB_EFFECTIVE_START(p, w) \ 581.1Sjmcneill ((uintptr_t)(p) + WIIFB_TOP_BOTTOM_BORDER * (w) * 2) 591.1Sjmcneill#define WIIFB_EFFECTIVE_HEIGHT(h) \ 601.1Sjmcneill ((h) - WIIFB_TOP_BOTTOM_BORDER * 2) 611.1Sjmcneill 621.1Sjmcneill#define WIIFB_FIFO_SIZE (256 * 1024) 631.1Sjmcneill 641.1Sjmcneill#define IBM750CL_SPR_HID2 920 651.1Sjmcneill#define IBM750CL_SPR_HID2_WPE 0x40000000 /* Write pipe enable */ 661.1Sjmcneill#define IBM750CL_SPR_WPAR 921 671.1Sjmcneill 681.1Sjmcneillstruct wiifb_mode { 691.1Sjmcneill const char * name; 701.1Sjmcneill u_int width; 711.1Sjmcneill u_int height; 721.1Sjmcneill u_int lines; 731.1Sjmcneill}; 741.1Sjmcneill 751.1Sjmcneillstatic uint32_t wiifb_devcmap[16] = { 761.1Sjmcneill 0x00800080, /* Black */ 771.1Sjmcneill 0x1dff1d6b, /* Blue */ 781.1Sjmcneill 0x4b554b4a, /* Green */ 791.1Sjmcneill 0x80808080, /* Cyan */ 801.1Sjmcneill 0x4c544cff, /* Red */ 811.1Sjmcneill 0x3aaa34b5, /* Magenta */ 821.1Sjmcneill 0x7140718a, /* Brown */ 831.1Sjmcneill 0xff80ff80, /* White */ 841.1Sjmcneill 0x80808080, /* Gray */ 851.1Sjmcneill 0xc399c36a, /* Bright Blue */ 861.1Sjmcneill 0xd076d074, /* Bright Green */ 871.1Sjmcneill 0x80808080, /* Bright Cyan */ 881.1Sjmcneill 0x4c544cff, /* Bright Red */ 891.1Sjmcneill 0x3aaa34b5, /* Bright Magenta */ 901.1Sjmcneill 0xe100e194, /* Bright Yellow */ 911.1Sjmcneill 0xff80ff80 /* Bright White */ 921.1Sjmcneill}; 931.1Sjmcneill 941.1Sjmcneill#define WIIFB_MODE_INDEX(fmt, interlaced) ((fmt << 1) | interlaced) 951.1Sjmcneill 961.1Sjmcneillstatic const struct wiifb_mode wiifb_modes[] = { 971.1Sjmcneill [WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 0)] = { 981.1Sjmcneill .name = "NTSC 480p", 991.1Sjmcneill .width = 640, 1001.1Sjmcneill .height = 480, 1011.1Sjmcneill .lines = 525, 1021.1Sjmcneill }, 1031.1Sjmcneill [WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 1)] = { 1041.1Sjmcneill .name = "NTSC 480i", 1051.1Sjmcneill .width = 640, 1061.1Sjmcneill .height = 480, 1071.1Sjmcneill .lines = 525, 1081.1Sjmcneill }, 1091.1Sjmcneill [WIIFB_MODE_INDEX(VI_DCR_FMT_PAL, 1)] = { 1101.1Sjmcneill .name = "PAL 576i", 1111.1Sjmcneill .width = 640, 1121.1Sjmcneill .height = 574, 1131.1Sjmcneill .lines = 625, 1141.1Sjmcneill }, 1151.1Sjmcneill 1161.1Sjmcneill}; 1171.1Sjmcneill#define WIIFB_NMODES __arraycount(wiifb_modes) 1181.1Sjmcneill 1191.1Sjmcneillstruct wiifb_dma { 1201.1Sjmcneill bus_dmamap_t dma_map; 1211.1Sjmcneill bus_dma_tag_t dma_tag; 1221.1Sjmcneill bus_size_t dma_size; 1231.1Sjmcneill bus_dma_segment_t dma_segs[1]; 1241.1Sjmcneill int dma_nsegs; 1251.1Sjmcneill void *dma_addr; 1261.1Sjmcneill}; 1271.1Sjmcneill 1281.1Sjmcneillstruct wiifb_softc { 1291.1Sjmcneill struct genfb_softc sc_gen; 1301.1Sjmcneill 1311.1Sjmcneill bus_space_tag_t sc_bst; 1321.1Sjmcneill bus_space_handle_t sc_bsh; 1331.1Sjmcneill bus_dma_tag_t sc_dmat; 1341.1Sjmcneill 1351.1Sjmcneill void *sc_bits; 1361.1Sjmcneill 1371.1Sjmcneill uint8_t sc_format; 1381.1Sjmcneill bool sc_interlaced; 1391.1Sjmcneill 1401.1Sjmcneill const struct wiifb_mode *sc_curmode; 1411.1Sjmcneill 1421.1Sjmcneill u_int sc_wsmode; 1431.1Sjmcneill 1441.1Sjmcneill volatile uint32_t *sc_efb; 1451.1Sjmcneill volatile uint16_t *sc_cp; 1461.1Sjmcneill volatile uint16_t *sc_pe; 1471.1Sjmcneill volatile uint32_t *sc_pi; 1481.1Sjmcneill gx_wgpipe_t *sc_wgpipe; 1491.1Sjmcneill 1501.1Sjmcneill struct wiifb_dma sc_rgb; 1511.1Sjmcneill struct wiifb_dma sc_fifo; 1521.1Sjmcneill 1531.1Sjmcneill uint16_t sc_token; 1541.1Sjmcneill}; 1551.1Sjmcneill 1561.1Sjmcneill#define RD2(sc, reg) \ 1571.1Sjmcneill bus_space_read_2((sc)->sc_bst, (sc)->sc_bsh, (reg)) 1581.1Sjmcneill#define RD4(sc, reg) \ 1591.1Sjmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 1601.1Sjmcneill#define WR2(sc, reg, val) \ 1611.1Sjmcneill bus_space_write_2((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 1621.1Sjmcneill#define WR4(sc, reg, val) \ 1631.1Sjmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 1641.1Sjmcneill 1651.1Sjmcneill#define CP_WRITE(sc, off, val) (sc)->sc_cp[(off)] = (val) 1661.1Sjmcneill#define CP_READ(sc, off) (sc)->sc_cp[(off)] 1671.1Sjmcneill 1681.1Sjmcneill#define PE_WRITE(sc, off, val) (sc)->sc_pe[(off)] = (val) 1691.1Sjmcneill#define PE_READ(sc, off) (sc)->sc_pe[(off)] 1701.1Sjmcneill 1711.1Sjmcneill#define PI_WRITE(sc, off, val) (sc)->sc_pi[(off)] = (val) 1721.1Sjmcneill#define PI_READ(sc, off) (sc)->sc_pi[(off)] 1731.1Sjmcneill 1741.1Sjmcneillstatic int wiifb_match(device_t, cfdata_t, void *); 1751.1Sjmcneillstatic void wiifb_attach(device_t, device_t, void *); 1761.1Sjmcneill 1771.1Sjmcneillstatic void wiifb_accel_init(struct wiifb_softc *); 1781.1Sjmcneillstatic int wiifb_vi_intr(void *); 1791.1Sjmcneillstatic void wiifb_vi_refresh(void *); 1801.1Sjmcneill 1811.1Sjmcneillstatic void wiifb_init(struct wiifb_softc *); 1821.1Sjmcneillstatic void wiifb_set_mode(struct wiifb_softc *, uint8_t, bool); 1831.1Sjmcneillstatic void wiifb_set_fb(struct wiifb_softc *); 1841.1Sjmcneillstatic void wiifb_clear_xfb(struct wiifb_softc *); 1851.1Sjmcneill 1861.1Sjmcneillstatic int wiifb_ioctl(void *, void *, u_long, void *, int, lwp_t *); 1871.1Sjmcneillstatic paddr_t wiifb_mmap(void *, void *, off_t, int); 1881.1Sjmcneill 1891.1Sjmcneillstatic struct genfb_ops wiifb_ops = { 1901.1Sjmcneill .genfb_ioctl = wiifb_ioctl, 1911.1Sjmcneill .genfb_mmap = wiifb_mmap, 1921.1Sjmcneill}; 1931.1Sjmcneill 1941.1SjmcneillCFATTACH_DECL_NEW(wiifb, sizeof(struct wiifb_softc), 1951.1Sjmcneill wiifb_match, wiifb_attach, NULL, NULL); 1961.1Sjmcneill 1971.1Sjmcneillstatic int 1981.1Sjmcneillwiifb_match(device_t parent, cfdata_t cf, void *aux) 1991.1Sjmcneill{ 2001.1Sjmcneill struct mainbus_attach_args *maa = aux; 2011.1Sjmcneill 2021.1Sjmcneill return !wiiu_native && strcmp(maa->maa_name, "genfb") == 0; 2031.1Sjmcneill} 2041.1Sjmcneill 2051.1Sjmcneillstatic void 2061.1Sjmcneillwiifb_attach(device_t parent, device_t self, void *aux) 2071.1Sjmcneill{ 2081.1Sjmcneill struct wiifb_softc *sc = device_private(self); 2091.1Sjmcneill prop_dictionary_t dict = device_properties(self); 2101.1Sjmcneill struct mainbus_attach_args *maa = aux; 2111.1Sjmcneill int error; 2121.1Sjmcneill 2131.1Sjmcneill sc->sc_gen.sc_dev = self; 2141.1Sjmcneill sc->sc_bst = maa->maa_bst; 2151.1Sjmcneill error = bus_space_map(sc->sc_bst, VI_BASE, VI_SIZE, 0, &sc->sc_bsh); 2161.1Sjmcneill if (error != 0) { 2171.1Sjmcneill panic("couldn't map registers"); 2181.1Sjmcneill } 2191.1Sjmcneill sc->sc_bits = mapiodev(XFB_START, XFB_SIZE, true); 2201.1Sjmcneill sc->sc_wsmode = WSDISPLAYIO_MODE_EMUL; 2211.1Sjmcneill sc->sc_dmat = maa->maa_dmat; 2221.1Sjmcneill 2231.1Sjmcneill wiifb_clear_xfb(sc); 2241.1Sjmcneill 2251.1Sjmcneill wiifb_init(sc); 2261.1Sjmcneill wiifb_set_mode(sc, sc->sc_format, sc->sc_interlaced); 2271.1Sjmcneill 2281.1Sjmcneill prop_dictionary_set_uint32(dict, "width", sc->sc_curmode->width); 2291.1Sjmcneill prop_dictionary_set_uint32(dict, "height", 2301.1Sjmcneill WIIFB_EFFECTIVE_HEIGHT(sc->sc_curmode->height)); 2311.1Sjmcneill prop_dictionary_set_uint8(dict, "depth", 16); 2321.1Sjmcneill prop_dictionary_set_uint32(dict, "address", XFB_START); 2331.1Sjmcneill prop_dictionary_set_uint32(dict, "virtual_address", 2341.1Sjmcneill WIIFB_EFFECTIVE_START(sc->sc_bits, sc->sc_curmode->width)); 2351.1Sjmcneill prop_dictionary_set_uint64(dict, "devcmap", (uintptr_t)wiifb_devcmap); 2361.1Sjmcneill 2371.1Sjmcneill genfb_init(&sc->sc_gen); 2381.1Sjmcneill 2391.1Sjmcneill aprint_naive("\n"); 2401.1Sjmcneill aprint_normal(": %s\n", sc->sc_curmode->name); 2411.1Sjmcneill 2421.1Sjmcneill genfb_cnattach(); 2431.1Sjmcneill prop_dictionary_set_bool(dict, "is_console", true); 2441.1Sjmcneill genfb_attach(&sc->sc_gen, &wiifb_ops); 2451.1Sjmcneill 2461.1Sjmcneill wiifb_accel_init(sc); 2471.1Sjmcneill} 2481.1Sjmcneill 2491.1Sjmcneillstatic void 2501.1Sjmcneillwiifb_clear_xfb(struct wiifb_softc *sc) 2511.1Sjmcneill{ 2521.1Sjmcneill u_int offset; 2531.1Sjmcneill uint32_t *p; 2541.1Sjmcneill 2551.1Sjmcneill /* 2561.1Sjmcneill * Paint the entire XFB black. Use 4-byte accesses as the Wii will 2571.1Sjmcneill * ignore 1- and 2- byte writes to uncached memory. 2581.1Sjmcneill */ 2591.1Sjmcneill for (p = sc->sc_bits, offset = 0; 2601.1Sjmcneill offset < XFB_SIZE; 2611.1Sjmcneill offset += 4, p++) { 2621.1Sjmcneill *p = wiifb_devcmap[0]; 2631.1Sjmcneill } 2641.1Sjmcneill} 2651.1Sjmcneill 2661.1Sjmcneillstatic int 2671.1Sjmcneillwiifb_vi_init(struct wiifb_softc *sc) 2681.1Sjmcneill{ 2691.1Sjmcneill device_t dev = sc->sc_gen.sc_dev; 2701.1Sjmcneill void *ih; 2711.1Sjmcneill 2721.1Sjmcneill WR4(sc, VI_DI0, VI_DI_ENB | 2731.1Sjmcneill __SHIFTIN(1, VI_DI_VCT) | 2741.1Sjmcneill __SHIFTIN(1, VI_DI_HCT)); 2751.1Sjmcneill WR4(sc, VI_DI1, 0); 2761.1Sjmcneill WR4(sc, VI_DI2, 0); 2771.1Sjmcneill WR4(sc, VI_DI3, 0); 2781.1Sjmcneill 2791.1Sjmcneill ih = intr_establish_xname(PI_IRQ_VI, IST_LEVEL, IPL_TTY, 2801.1Sjmcneill wiifb_vi_intr, sc, device_xname(dev)); 2811.1Sjmcneill if (ih == NULL) { 2821.1Sjmcneill aprint_error_dev(dev, "failed to install VI intr handler\n"); 2831.1Sjmcneill return EIO; 2841.1Sjmcneill } 2851.1Sjmcneill 2861.1Sjmcneill return 0; 2871.1Sjmcneill} 2881.1Sjmcneill 2891.1Sjmcneillstatic int 2901.1Sjmcneillwiifb_dma_alloc(struct wiifb_softc *sc, bus_size_t size, bus_size_t align, 2911.1Sjmcneill int flags, struct wiifb_dma *dma) 2921.1Sjmcneill{ 2931.1Sjmcneill bus_dma_tag_t dmat = sc->sc_dmat; // &wii_mem1_bus_dma_tag; 2941.1Sjmcneill int error; 2951.1Sjmcneill 2961.1Sjmcneill error = bus_dmamem_alloc(dmat, size, align, 0, 2971.1Sjmcneill dma->dma_segs, 1, &dma->dma_nsegs, BUS_DMA_WAITOK); 2981.1Sjmcneill if (error) 2991.1Sjmcneill return error; 3001.1Sjmcneill 3011.1Sjmcneill error = bus_dmamem_map(dmat, dma->dma_segs, dma->dma_nsegs, 3021.1Sjmcneill size, &dma->dma_addr, BUS_DMA_WAITOK | flags); 3031.1Sjmcneill if (error) 3041.1Sjmcneill goto free; 3051.1Sjmcneill 3061.1Sjmcneill error = bus_dmamap_create(dmat, size, dma->dma_nsegs, 3071.1Sjmcneill size, 0, BUS_DMA_WAITOK, &dma->dma_map); 3081.1Sjmcneill if (error) 3091.1Sjmcneill goto unmap; 3101.1Sjmcneill 3111.1Sjmcneill error = bus_dmamap_load(dmat, dma->dma_map, dma->dma_addr, 3121.1Sjmcneill size, NULL, BUS_DMA_WAITOK); 3131.1Sjmcneill if (error) 3141.1Sjmcneill goto destroy; 3151.1Sjmcneill 3161.1Sjmcneill dma->dma_size = size; 3171.1Sjmcneill dma->dma_tag = dmat; 3181.1Sjmcneill 3191.1Sjmcneill memset(dma->dma_addr, 0, dma->dma_size); 3201.1Sjmcneill 3211.1Sjmcneill return 0; 3221.1Sjmcneill 3231.1Sjmcneilldestroy: 3241.1Sjmcneill bus_dmamap_destroy(dmat, dma->dma_map); 3251.1Sjmcneillunmap: 3261.1Sjmcneill bus_dmamem_unmap(dmat, dma->dma_addr, dma->dma_size); 3271.1Sjmcneillfree: 3281.1Sjmcneill bus_dmamem_free(dmat, dma->dma_segs, dma->dma_nsegs); 3291.1Sjmcneill 3301.1Sjmcneill return error; 3311.1Sjmcneill} 3321.1Sjmcneill 3331.1Sjmcneillstatic void * 3341.1Sjmcneillwiifb_mapreg(struct wiifb_softc *sc, bus_addr_t base, bus_size_t size) 3351.1Sjmcneill{ 3361.1Sjmcneill bus_space_handle_t bsh; 3371.1Sjmcneill int error; 3381.1Sjmcneill 3391.1Sjmcneill error = bus_space_map(sc->sc_bst, base, size, 3401.1Sjmcneill BUS_SPACE_MAP_LINEAR, &bsh); 3411.1Sjmcneill if (error != 0) { 3421.1Sjmcneill panic("couldn't map 0x%x", base); 3431.1Sjmcneill } 3441.1Sjmcneill return bus_space_vaddr(sc->sc_bst, bsh); 3451.1Sjmcneill} 3461.1Sjmcneill 3471.1Sjmcneillstatic void 3481.1Sjmcneillwiifb_set_wgpipe(bus_addr_t base) 3491.1Sjmcneill{ 3501.1Sjmcneill uint32_t value; 3511.1Sjmcneill 3521.1Sjmcneill if (base) { 3531.1Sjmcneill mtspr(IBM750CL_SPR_WPAR, base); 3541.1Sjmcneill } 3551.1Sjmcneill value = mfspr(IBM750CL_SPR_HID2); 3561.1Sjmcneill if (base) { 3571.1Sjmcneill value |= IBM750CL_SPR_HID2_WPE; 3581.1Sjmcneill } else { 3591.1Sjmcneill value &= ~IBM750CL_SPR_HID2_WPE; 3601.1Sjmcneill } 3611.1Sjmcneill mtspr(IBM750CL_SPR_HID2, value); 3621.1Sjmcneill} 3631.1Sjmcneill 3641.1Sjmcneillstatic void 3651.1Sjmcneillwiifb_ppcsync(void) 3661.1Sjmcneill{ 3671.1Sjmcneill uint32_t value; 3681.1Sjmcneill 3691.1Sjmcneill value = mfspr(SPR_HID0); 3701.1Sjmcneill mtspr(SPR_HID0, value | 0x8); 3711.1Sjmcneill asm volatile("isync" ::: "memory"); 3721.1Sjmcneill asm volatile("sync" ::: "memory"); 3731.1Sjmcneill mtspr(SPR_HID0, value); 3741.1Sjmcneill} 3751.1Sjmcneill 3761.1Sjmcneillstatic void 3771.1Sjmcneillwiifb_gx_bp_load(struct wiifb_softc *sc, uint32_t data) 3781.1Sjmcneill{ 3791.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x61); 3801.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data); 3811.1Sjmcneill} 3821.1Sjmcneill 3831.1Sjmcneillstatic void 3841.1Sjmcneillwiifb_gx_cp_load(struct wiifb_softc *sc, uint8_t addr, uint32_t data) 3851.1Sjmcneill{ 3861.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x08); 3871.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u8 = addr); 3881.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data); 3891.1Sjmcneill} 3901.1Sjmcneill 3911.1Sjmcneillstatic void 3921.1Sjmcneillwiifb_gx_xf_load(struct wiifb_softc *sc, uint16_t addr, uint32_t data) 3931.1Sjmcneill{ 3941.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x10); 3951.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u32 = addr); 3961.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data); 3971.1Sjmcneill} 3981.1Sjmcneill 3991.1Sjmcneillstatic void 4001.1Sjmcneillwiifb_gx_xf_load_multi(struct wiifb_softc *sc, uint16_t addr, 4011.1Sjmcneill uint16_t count, uint32_t *data) 4021.1Sjmcneill{ 4031.1Sjmcneill uint16_t n; 4041.1Sjmcneill 4051.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x10); 4061.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 4071.1Sjmcneill (((uint32_t)count - 1) << 16) | addr); 4081.1Sjmcneill for (n = 0; n < count; n++) { 4091.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data[n]); 4101.1Sjmcneill } 4111.1Sjmcneill} 4121.1Sjmcneill 4131.1Sjmcneillstatic void 4141.1Sjmcneillwiifb_gx_set_viewport(struct wiifb_softc *sc, u_int w, u_int h) 4151.1Sjmcneill{ 4161.1Sjmcneill uint32_t data[6]; 4171.1Sjmcneill 4181.1Sjmcneill KASSERT(w == 640); 4191.1Sjmcneill KASSERT(h == 480); 4201.1Sjmcneill 4211.1Sjmcneill data[0] = 0x00000140; 4221.1Sjmcneill data[1] = 0xffffff10; 4231.1Sjmcneill data[2] = 0x00ffffff; 4241.1Sjmcneill data[3] = 0x00000296; 4251.1Sjmcneill data[4] = 0x00000246; 4261.1Sjmcneill data[5] = 0x00ffffff; 4271.1Sjmcneill 4281.1Sjmcneill wiifb_gx_xf_load_multi(sc, GX_XF_VIEWPORT_X0, 4291.1Sjmcneill __arraycount(data), data); 4301.1Sjmcneill} 4311.1Sjmcneill 4321.1Sjmcneillstatic void 4331.1Sjmcneillwiifb_gx_set_scissor(struct wiifb_softc *sc, u_int x, u_int y, 4341.1Sjmcneill u_int w, u_int h) 4351.1Sjmcneill{ 4361.1Sjmcneill uint32_t xo = x + 0x156; 4371.1Sjmcneill uint32_t yo = y + 0x156; 4381.1Sjmcneill uint32_t wo = xo + w - 1; 4391.1Sjmcneill uint32_t ho = yo + h - 1; 4401.1Sjmcneill 4411.1Sjmcneill wiifb_gx_bp_load(sc, 0x20000000 | yo | (xo << 12)); 4421.1Sjmcneill wiifb_gx_bp_load(sc, 0x21000000 | ho | (wo << 12)); 4431.1Sjmcneill wiifb_gx_bp_load(sc, 0x59000000 | GX_XY(xo >> 1, yo >> 1)); 4441.1Sjmcneill} 4451.1Sjmcneill 4461.1Sjmcneillstatic void 4471.1Sjmcneillwiifb_gx_init(struct wiifb_softc *sc) 4481.1Sjmcneill{ 4491.1Sjmcneill const uint32_t fifo_start = sc->sc_fifo.dma_segs[0].ds_addr; 4501.1Sjmcneill const uint32_t fifo_end = fifo_start + sc->sc_fifo.dma_size - 4; 4511.1Sjmcneill const uint32_t fifo_hiwat = GX_FIFO_HIWAT(sc->sc_fifo.dma_size); 4521.1Sjmcneill const uint32_t fifo_lowat = GX_FIFO_LOWAT(sc->sc_fifo.dma_size); 4531.1Sjmcneill 4541.1Sjmcneill /* Disable WGPIPE and unlink CP FIFO before changing settings. */ 4551.1Sjmcneill wiifb_set_wgpipe(0); 4561.1Sjmcneill CP_WRITE(sc, CP_CR, 0); 4571.1Sjmcneill CP_WRITE(sc, CP_CLEAR, CP_CLEAR_UNDERFLOW | CP_CLEAR_OVERFLOW); 4581.1Sjmcneill 4591.1Sjmcneill /* Setup GP FIFO */ 4601.1Sjmcneill CP_WRITE(sc, CP_FIFO_BASE_LO, LOWER_16_BITS(fifo_start)); 4611.1Sjmcneill CP_WRITE(sc, CP_FIFO_BASE_HI, UPPER_16_BITS(fifo_start)); 4621.1Sjmcneill CP_WRITE(sc, CP_FIFO_END_LO, LOWER_16_BITS(fifo_end)); 4631.1Sjmcneill CP_WRITE(sc, CP_FIFO_END_HI, UPPER_16_BITS(fifo_end)); 4641.1Sjmcneill CP_WRITE(sc, CP_FIFO_HIWAT_LO, LOWER_16_BITS(fifo_hiwat)); 4651.1Sjmcneill CP_WRITE(sc, CP_FIFO_HIWAT_HI, UPPER_16_BITS(fifo_hiwat)); 4661.1Sjmcneill CP_WRITE(sc, CP_FIFO_LOWAT_LO, LOWER_16_BITS(fifo_lowat)); 4671.1Sjmcneill CP_WRITE(sc, CP_FIFO_LOWAT_HI, UPPER_16_BITS(fifo_lowat)); 4681.1Sjmcneill CP_WRITE(sc, CP_FIFO_RW_DIST_LO, 0); 4691.1Sjmcneill CP_WRITE(sc, CP_FIFO_RW_DIST_HI, 0); 4701.1Sjmcneill CP_WRITE(sc, CP_FIFO_WRITE_PTR_LO, LOWER_16_BITS(fifo_start)); 4711.1Sjmcneill CP_WRITE(sc, CP_FIFO_WRITE_PTR_HI, UPPER_16_BITS(fifo_start)); 4721.1Sjmcneill CP_WRITE(sc, CP_FIFO_READ_PTR_LO, LOWER_16_BITS(fifo_start)); 4731.1Sjmcneill CP_WRITE(sc, CP_FIFO_READ_PTR_HI, UPPER_16_BITS(fifo_start)); 4741.1Sjmcneill wiifb_ppcsync(); 4751.1Sjmcneill 4761.1Sjmcneill /* Setup CPU FIFO */ 4771.1Sjmcneill PI_WRITE(sc, PI_FIFO_BASE_START, fifo_start); 4781.1Sjmcneill PI_WRITE(sc, PI_FIFO_BASE_END, fifo_end); 4791.1Sjmcneill PI_WRITE(sc, PI_FIFO_WRITE_PTR, fifo_start); 4801.1Sjmcneill wiifb_ppcsync(); 4811.1Sjmcneill 4821.1Sjmcneill /* Link CP/PE FIFO and enable GP FIFO */ 4831.1Sjmcneill CP_WRITE(sc, CP_CR, CP_CR_GP_LINK_ENABLE); 4841.1Sjmcneill CP_WRITE(sc, CP_CR, CP_READ(sc, CP_CR) | CP_CR_READ_ENABLE); 4851.1Sjmcneill 4861.1Sjmcneill /* Init pixel engine */ 4871.1Sjmcneill PE_WRITE(sc, PE_ZCONF, 4881.1Sjmcneill PE_ZCONF_UPD_ENABLE | 4891.1Sjmcneill PE_ZCONF_FUNC_ALWAYS | 4901.1Sjmcneill PE_ZCONF_COMP_ENABLE); 4911.1Sjmcneill PE_WRITE(sc, PE_ALPHA_CONF, 4921.1Sjmcneill PE_ALPHA_CONF_OP_SET | 4931.1Sjmcneill PE_ALPHA_CONF_SRC_1 | 4941.1Sjmcneill PE_ALPHA_CONF_DST_0 | 4951.1Sjmcneill PE_ALPHA_CONF_UPD_A | 4961.1Sjmcneill PE_ALPHA_CONF_UPD_C); 4971.1Sjmcneill PE_WRITE(sc, PE_ALPHA_DEST, 0); 4981.1Sjmcneill PE_WRITE(sc, PE_ALPHA_MODE, PE_ALPHA_MODE_ALWAYS); 4991.1Sjmcneill PE_WRITE(sc, PE_ALPHA_READ, 5001.1Sjmcneill PE_ALPHA_READ_UNK | PE_ALPHA_READ_FF); 5011.1Sjmcneill 5021.1Sjmcneill /* Enable WG pipe */ 5031.1Sjmcneill wiifb_set_wgpipe(WGPIPE_BASE); 5041.1Sjmcneill 5051.1Sjmcneill /* Sanitize command processor registers */ 5061.1Sjmcneill for (int n = 0; n < 8; n++) { 5071.1Sjmcneill wiifb_gx_cp_load(sc, 0x80 + n, 0x80000000); 5081.1Sjmcneill } 5091.1Sjmcneill wiifb_gx_cp_load(sc, 0x20, 0); 5101.1Sjmcneill 5111.1Sjmcneill /* Sanitize transform unit registers */ 5121.1Sjmcneill wiifb_gx_xf_load(sc, 0x1000, 0x3f); 5131.1Sjmcneill wiifb_gx_xf_load(sc, 0x1005, 0x01); 5141.1Sjmcneill wiifb_gx_xf_load(sc, 0x1012, 0x01); 5151.1Sjmcneill wiifb_gx_xf_load(sc, 0x1006, 0); 5161.1Sjmcneill 5171.1Sjmcneill /* Initialize blitting processor */ 5181.1Sjmcneill wiifb_gx_bp_load(sc, 0x00000001); 5191.1Sjmcneill wiifb_gx_bp_load(sc, 0x01666666); 5201.1Sjmcneill wiifb_gx_bp_load(sc, 0x02666666); 5211.1Sjmcneill wiifb_gx_bp_load(sc, 0x03666666); 5221.1Sjmcneill wiifb_gx_bp_load(sc, 0x04666666); 5231.1Sjmcneill wiifb_gx_bp_load(sc, 0x22000606); 5241.1Sjmcneill wiifb_gx_bp_load(sc, 0x23000000); 5251.1Sjmcneill wiifb_gx_bp_load(sc, 0x24000000); 5261.1Sjmcneill wiifb_gx_bp_load(sc, 0x42000000); 5271.1Sjmcneill wiifb_gx_bp_load(sc, 0x44000003); 5281.1Sjmcneill wiifb_gx_bp_load(sc, 0x43000000); 5291.1Sjmcneill wiifb_gx_bp_load(sc, 0x53595000); 5301.1Sjmcneill wiifb_gx_bp_load(sc, 0x54000015); 5311.1Sjmcneill wiifb_gx_bp_load(sc, 0x550003ff); 5321.1Sjmcneill wiifb_gx_bp_load(sc, 0x560003ff); 5331.1Sjmcneill wiifb_gx_bp_load(sc, 0x5800000f); 5341.1Sjmcneill wiifb_gx_bp_load(sc, 0x67000000); 5351.1Sjmcneill 5361.1Sjmcneill /* Set viewport and scissor parameters */ 5371.1Sjmcneill wiifb_gx_set_viewport(sc, WIIFB_RGB_WIDTH, WIIFB_RGB_HEIGHT); 5381.1Sjmcneill wiifb_gx_set_scissor(sc, 0, 0, WIIFB_RGB_WIDTH, WIIFB_RGB_HEIGHT); 5391.1Sjmcneill 5401.1Sjmcneill wiifb_gx_bp_load(sc, 0x4000001f); 5411.1Sjmcneill 5421.1Sjmcneill if (sc->sc_curmode->height == 574) { 5431.1Sjmcneill /* Scale 480 lines to PAL display height */ 5441.1Sjmcneill wiifb_gx_bp_load(sc, 0x4e000127); 5451.1Sjmcneill } 5461.1Sjmcneill 5471.1Sjmcneill /* Copy mode parameters */ 5481.1Sjmcneill wiifb_gx_bp_load(sc, 0x410004bc); 5491.1Sjmcneill 5501.1Sjmcneill /* Copy source */ 5511.1Sjmcneill wiifb_gx_bp_load(sc, 0x49000000 | GX_XY(0, 0)); 5521.1Sjmcneill wiifb_gx_bp_load(sc, 0x4a000000 | 5531.1Sjmcneill GX_XY(WIIFB_RGB_WIDTH - 1, WIIFB_RGB_HEIGHT - 1)); 5541.1Sjmcneill 5551.1Sjmcneill /* Copy destination */ 5561.1Sjmcneill wiifb_gx_bp_load(sc, 0x4d000000 | (WIIFB_RGB_WIDTH >> 4)); 5571.1Sjmcneill 5581.1Sjmcneill /* XFB address */ 5591.1Sjmcneill wiifb_gx_bp_load(sc, 0x4b000000 | (XFB_START >> 5)); 5601.1Sjmcneill 5611.1Sjmcneill /* Copy clear settings */ 5621.1Sjmcneill wiifb_gx_bp_load(sc, 0x4f000000); 5631.1Sjmcneill wiifb_gx_bp_load(sc, 0x50000000); 5641.1Sjmcneill wiifb_gx_bp_load(sc, 0x5100ffff); 5651.1Sjmcneill} 5661.1Sjmcneill 5671.1Sjmcneillstatic void 5681.1Sjmcneillwiifb_gx_flush(struct wiifb_softc *sc) 5691.1Sjmcneill{ 5701.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 5711.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 5721.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 5731.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 5741.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 5751.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 5761.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 5771.1Sjmcneill GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0); 5781.1Sjmcneill wiifb_ppcsync(); 5791.1Sjmcneill} 5801.1Sjmcneill 5811.1Sjmcneillstatic void 5821.1Sjmcneillwiifb_accel_init(struct wiifb_softc *sc) 5831.1Sjmcneill{ 5841.1Sjmcneill bus_size_t rgb_size; 5851.1Sjmcneill bus_size_t fifo_size; 5861.1Sjmcneill 5871.1Sjmcneill if (wiifb_vi_init(sc) != 0) { 5881.1Sjmcneill panic("couldn't init VI"); 5891.1Sjmcneill } 5901.1Sjmcneill 5911.1Sjmcneill rgb_size = WIIFB_RGB_WIDTH * WIIFB_RGB_HEIGHT; 5921.1Sjmcneill rgb_size = rgb_size * WIIFB_RGB_BPP / NBBY; 5931.1Sjmcneill rgb_size = roundup(rgb_size, PAGE_SIZE); 5941.1Sjmcneill if (wiifb_dma_alloc(sc, rgb_size, PAGE_SIZE, BUS_DMA_PREFETCHABLE, 5951.1Sjmcneill &sc->sc_rgb) != 0) { 5961.1Sjmcneill panic("couldn't alloc rgb fb"); 5971.1Sjmcneill } 5981.1Sjmcneill 5991.1Sjmcneill fifo_size = WIIFB_FIFO_SIZE; 6001.1Sjmcneill if (wiifb_dma_alloc(sc, fifo_size, GX_FIFO_ALIGN, BUS_DMA_NOCACHE, 6011.1Sjmcneill &sc->sc_fifo) != 0) { 6021.1Sjmcneill panic("couldn't alloc gx fifo"); 6031.1Sjmcneill } 6041.1Sjmcneill 6051.1Sjmcneill sc->sc_efb = wiifb_mapreg(sc, EFB_BASE, EFB_SIZE); 6061.1Sjmcneill sc->sc_cp = wiifb_mapreg(sc, CP_BASE, CP_SIZE); 6071.1Sjmcneill sc->sc_pe = wiifb_mapreg(sc, PE_BASE, PE_SIZE); 6081.1Sjmcneill sc->sc_pi = wiifb_mapreg(sc, PI_BASE, PI_SIZE); 6091.1Sjmcneill sc->sc_wgpipe = wiifb_mapreg(sc, WGPIPE_BASE, WGPIPE_SIZE); 6101.1Sjmcneill 6111.1Sjmcneill wiifb_gx_init(sc); 6121.1Sjmcneill}; 6131.1Sjmcneill 6141.1Sjmcneillstatic void 6151.1Sjmcneillwiifb_rgb_to_efb(struct wiifb_softc *sc) 6161.1Sjmcneill{ 6171.1Sjmcneill u_int y; 6181.1Sjmcneill uint32_t *src = sc->sc_rgb.dma_addr; 6191.1Sjmcneill uint32_t *dst = __UNVOLATILE(sc->sc_efb); 6201.1Sjmcneill register_t hid0 = mfspr(SPR_HID0); 6211.1Sjmcneill 6221.1Sjmcneill KASSERT(src != NULL); 6231.1Sjmcneill KASSERT(dst != NULL); 6241.1Sjmcneill 6251.1Sjmcneill /* Disable store gathering while writing to EFB. */ 6261.1Sjmcneill mtspr(SPR_HID0, hid0 & ~HID0_SGE); 6271.1Sjmcneill 6281.1Sjmcneill for (y = 0; y < WIIFB_RGB_HEIGHT; y++) { 6291.1Sjmcneill memcpy(dst, src, WIIFB_RGB_WIDTH * 4); 6301.1Sjmcneill src += WIIFB_RGB_WIDTH; 6311.1Sjmcneill dst += 1024; 6321.1Sjmcneill } 6331.1Sjmcneill 6341.1Sjmcneill /* Re-enable store gathering. */ 6351.1Sjmcneill mtspr(SPR_HID0, hid0); 6361.1Sjmcneill} 6371.1Sjmcneill 6381.1Sjmcneillstatic void 6391.1Sjmcneillwiifb_efb_to_xfb(struct wiifb_softc *sc) 6401.1Sjmcneill{ 6411.1Sjmcneill const uint32_t copy_mask = sc->sc_curmode->height == 574 ? 0x400 : 0x0; 6421.1Sjmcneill 6431.1Sjmcneill /* Execute copy to XFB */ 6441.1Sjmcneill wiifb_gx_bp_load(sc, 0x52004803 | copy_mask); 6451.1Sjmcneill} 6461.1Sjmcneill 6471.1Sjmcneillstatic void 6481.1Sjmcneillwiifb_gx_draw_done(struct wiifb_softc *sc, uint16_t token) 6491.1Sjmcneill{ 6501.1Sjmcneill /* Draw done */ 6511.1Sjmcneill wiifb_gx_bp_load(sc, 0x45000002); 6521.1Sjmcneill /* Write tokens */ 6531.1Sjmcneill wiifb_gx_bp_load(sc, 0x48000000 | token); 6541.1Sjmcneill wiifb_gx_bp_load(sc, 0x47000000 | token); 6551.1Sjmcneill /* Flush WG pipe */ 6561.1Sjmcneill wiifb_gx_flush(sc); 6571.1Sjmcneill} 6581.1Sjmcneill 6591.1Sjmcneillstatic void 6601.1Sjmcneillwiifb_vi_refresh(void *priv) 6611.1Sjmcneill{ 6621.1Sjmcneill struct wiifb_softc *sc = priv; 6631.1Sjmcneill 6641.1Sjmcneill wiifb_rgb_to_efb(sc); 6651.1Sjmcneill wiifb_efb_to_xfb(sc); 6661.1Sjmcneill wiifb_gx_draw_done(sc, sc->sc_token++); 6671.1Sjmcneill} 6681.1Sjmcneill 6691.1Sjmcneillstatic int 6701.1Sjmcneillwiifb_vi_intr(void *priv) 6711.1Sjmcneill{ 6721.1Sjmcneill struct wiifb_softc *sc = priv; 6731.1Sjmcneill uint32_t di0; 6741.1Sjmcneill int ret = 0; 6751.1Sjmcneill 6761.1Sjmcneill di0 = RD4(sc, VI_DI0); 6771.1Sjmcneill 6781.1Sjmcneill WR4(sc, VI_DI0, RD4(sc, VI_DI0) & ~VI_DI_INT); 6791.1Sjmcneill WR4(sc, VI_DI1, RD4(sc, VI_DI1) & ~VI_DI_INT); 6801.1Sjmcneill WR4(sc, VI_DI2, RD4(sc, VI_DI2) & ~VI_DI_INT); 6811.1Sjmcneill WR4(sc, VI_DI3, RD4(sc, VI_DI3) & ~VI_DI_INT); 6821.1Sjmcneill 6831.1Sjmcneill if ((di0 & VI_DI_INT) != 0 && 6841.1Sjmcneill sc->sc_wsmode != WSDISPLAYIO_MODE_EMUL) { 6851.1Sjmcneill wiifb_vi_refresh(sc); 6861.1Sjmcneill ret = 1; 6871.1Sjmcneill } 6881.1Sjmcneill 6891.1Sjmcneill return ret; 6901.1Sjmcneill} 6911.1Sjmcneill 6921.1Sjmcneillstatic void 6931.1Sjmcneillwiifb_init(struct wiifb_softc *sc) 6941.1Sjmcneill{ 6951.1Sjmcneill uint16_t dcr; 6961.1Sjmcneill uint16_t visel; 6971.1Sjmcneill 6981.1Sjmcneill /* Read current display format and interlaced settings. */ 6991.1Sjmcneill dcr = RD2(sc, VI_DCR); 7001.1Sjmcneill if ((dcr & VI_DCR_ENB) != 0) { 7011.1Sjmcneill sc->sc_format = __SHIFTOUT(dcr, VI_DCR_FMT); 7021.1Sjmcneill sc->sc_interlaced = (dcr & VI_DCR_NIN) == 0; 7031.1Sjmcneill } else { 7041.1Sjmcneill visel = RD2(sc, VI_VISEL); 7051.1Sjmcneill sc->sc_format = VI_DCR_FMT_NTSC; 7061.1Sjmcneill sc->sc_interlaced = (visel & VI_VISEL_COMPONENT_CABLE) == 0; 7071.1Sjmcneill } 7081.1Sjmcneill 7091.1Sjmcneill /* Reset video interface. */ 7101.1Sjmcneill WR2(sc, VI_DCR, VI_DCR_RST); 7111.1Sjmcneill delay(1000); 7121.1Sjmcneill 7131.1Sjmcneill /* Initialize video format and interlace selector. */ 7141.1Sjmcneill dcr = __SHIFTIN(sc->sc_format, VI_DCR_FMT) | 7151.1Sjmcneill (sc->sc_interlaced ? 0 : VI_DCR_NIN); 7161.1Sjmcneill WR2(sc, VI_DCR, dcr); 7171.1Sjmcneill} 7181.1Sjmcneill 7191.1Sjmcneillstatic void 7201.1Sjmcneillwiifb_set_mode(struct wiifb_softc *sc, uint8_t format, bool interlaced) 7211.1Sjmcneill{ 7221.1Sjmcneill u_int modeidx; 7231.1Sjmcneill u_int strides, reads; 7241.1Sjmcneill 7251.1Sjmcneill modeidx = WIIFB_MODE_INDEX(format, interlaced); 7261.1Sjmcneill if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 1)) { 7271.1Sjmcneill /* NTSC 480i Magic numbers from YAGCD. */ 7281.1Sjmcneill WR2(sc, VI_VTR, 0x0f06); 7291.1Sjmcneill WR4(sc, VI_HTR0, 0x476901AD); 7301.1Sjmcneill WR4(sc, VI_HTR1, 0x02EA5140); 7311.1Sjmcneill WR4(sc, VI_VTO, 0x00030018); 7321.1Sjmcneill WR4(sc, VI_VTE, 0x00020019); 7331.1Sjmcneill WR4(sc, VI_BBOI, 0x410C410C); 7341.1Sjmcneill WR4(sc, VI_BBEI, 0x40ED40ED); 7351.1Sjmcneill WR2(sc, VI_DPV, 0x0000); 7361.1Sjmcneill WR2(sc, VI_DPH, 0x0000); 7371.1Sjmcneill } else if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 0)) { 7381.1Sjmcneill /* NTSC 480p */ 7391.1Sjmcneill WR2(sc, VI_VTR, 0x1e0c); 7401.1Sjmcneill WR4(sc, VI_HTR0, 0x476901ad); 7411.1Sjmcneill WR4(sc, VI_HTR1, 0x030a4940); 7421.1Sjmcneill WR4(sc, VI_VTO, 0x00060030); 7431.1Sjmcneill WR4(sc, VI_VTE, 0x00060030); 7441.1Sjmcneill WR4(sc, VI_BBOI, 0x81d881d8); 7451.1Sjmcneill WR4(sc, VI_BBEI, 0x81d881d8); 7461.1Sjmcneill WR2(sc, VI_DPV, 0x0000); 7471.1Sjmcneill WR2(sc, VI_DPH, 0x0000); 7481.1Sjmcneill } else if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_PAL, 1)) { 7491.1Sjmcneill /* PAL 576i */ 7501.1Sjmcneill WR2(sc, VI_VTR, 0x11f5); 7511.1Sjmcneill WR4(sc, VI_HTR0, 0x4b6a01b0); 7521.1Sjmcneill WR4(sc, VI_HTR1, 0x02f85640); 7531.1Sjmcneill WR4(sc, VI_VTO, 0x00010023); 7541.1Sjmcneill WR4(sc, VI_VTE, 0x00000024); 7551.1Sjmcneill WR4(sc, VI_BBOI, 0x4d2b4d6d); 7561.1Sjmcneill WR4(sc, VI_BBEI, 0x4d8a4d4c); 7571.1Sjmcneill WR2(sc, VI_DPV, 0x013c); 7581.1Sjmcneill WR2(sc, VI_DPH, 0x0144); 7591.1Sjmcneill } else { 7601.1Sjmcneill /* 7611.1Sjmcneill * Display mode is not supported. Blink the slot LED to 7621.1Sjmcneill * indicate failure. 7631.1Sjmcneill */ 7641.1Sjmcneill wii_slot_led_blink(WIIFB_ERROR_BLINK_INTERVAL); 7651.1Sjmcneill } 7661.1Sjmcneill 7671.1Sjmcneill if (modeidx >= WIIFB_NMODES || wiifb_modes[modeidx].name == NULL) { 7681.1Sjmcneill panic("Unsupported format (0x%x) / interlaced (%d) settings", 7691.1Sjmcneill sc->sc_format, sc->sc_interlaced); 7701.1Sjmcneill } 7711.1Sjmcneill sc->sc_curmode = &wiifb_modes[modeidx]; 7721.1Sjmcneill 7731.1Sjmcneill /* Filter coefficient table, values from YAGCD. */ 7741.1Sjmcneill WR4(sc, VI_FCT0, 0x1ae771f0); 7751.1Sjmcneill WR4(sc, VI_FCT1, 0x0db4a574); 7761.1Sjmcneill WR4(sc, VI_FCT2, 0x00c1188e); 7771.1Sjmcneill WR4(sc, VI_FCT3, 0xc4c0cbe2); 7781.1Sjmcneill WR4(sc, VI_FCT4, 0xfcecdecf); 7791.1Sjmcneill WR4(sc, VI_FCT5, 0x13130f08); 7801.1Sjmcneill WR4(sc, VI_FCT6, 0x00080C0f); 7811.1Sjmcneill 7821.1Sjmcneill /* Unknown registers. */ 7831.1Sjmcneill WR4(sc, VI_UNKNOWN_68H, 0x00ff0000); 7841.1Sjmcneill WR2(sc, VI_UNKNOWN_76H, 0x00ff); 7851.1Sjmcneill WR4(sc, VI_UNKNOWN_78H, 0x00ff00ff); 7861.1Sjmcneill WR4(sc, VI_UNKNOWN_7CH, 0x00ff00ff); 7871.1Sjmcneill 7881.1Sjmcneill /* Picture configuration */ 7891.1Sjmcneill strides = (sc->sc_curmode->width * 2) / (interlaced ? 16 : 32); 7901.1Sjmcneill reads = (sc->sc_curmode->width * 2) / 32; 7911.1Sjmcneill WR2(sc, VI_PICCONF, 7921.1Sjmcneill __SHIFTIN(strides, VI_PICCONF_STRIDES) | 7931.1Sjmcneill __SHIFTIN(reads, VI_PICCONF_READS)); 7941.1Sjmcneill 7951.1Sjmcneill /* Horizontal scaler configuration */ 7961.1Sjmcneill if (interlaced) { 7971.1Sjmcneill WR2(sc, VI_HSR, __SHIFTIN(256, VI_HSR_STP)); 7981.1Sjmcneill } else { 7991.1Sjmcneill WR2(sc, VI_HSR, __SHIFTIN(244, VI_HSR_STP) | VI_HSR_HS_EN); 8001.1Sjmcneill } 8011.1Sjmcneill 8021.1Sjmcneill /* Video clock configuration */ 8031.1Sjmcneill WR2(sc, VI_VICLK, 8041.1Sjmcneill interlaced ? VI_VICLK_SEL_27MHZ : VI_VICLK_SEL_54MHZ); 8051.1Sjmcneill 8061.1Sjmcneill /* Horizontal scaling width */ 8071.1Sjmcneill WR2(sc, VI_HSCALINGW, sc->sc_curmode->width); 8081.1Sjmcneill 8091.1Sjmcneill /* Set framebuffer address */ 8101.1Sjmcneill wiifb_set_fb(sc); 8111.1Sjmcneill 8121.1Sjmcneill /* Finally, enable the framebuffer */ 8131.1Sjmcneill WR2(sc, VI_DCR, RD2(sc, VI_DCR) | VI_DCR_ENB); 8141.1Sjmcneill} 8151.1Sjmcneill 8161.1Sjmcneillstatic void 8171.1Sjmcneillwiifb_set_fb(struct wiifb_softc *sc) 8181.1Sjmcneill{ 8191.1Sjmcneill uint32_t taddr = XFB_START; 8201.1Sjmcneill uint32_t baddr = taddr + (sc->sc_interlaced ? 8211.1Sjmcneill sc->sc_curmode->width * 2 : 0); 8221.1Sjmcneill 8231.1Sjmcneill WR4(sc, VI_TFBL, 8241.1Sjmcneill VI_TFBL_PGOFF | 8251.1Sjmcneill __SHIFTIN((taddr >> 5), VI_TFBL_FBB) | 8261.1Sjmcneill __SHIFTIN((taddr / 2) & 0xf, VI_TFBL_XOF)); 8271.1Sjmcneill WR4(sc, VI_TFBR, 0); 8281.1Sjmcneill 8291.1Sjmcneill WR4(sc, VI_BFBL, 8301.1Sjmcneill VI_BFBL_PGOFF | 8311.1Sjmcneill __SHIFTIN((baddr >> 5), VI_BFBL_FBB) | 8321.1Sjmcneill __SHIFTIN((baddr / 2) & 0xf, VI_BFBL_XOF)); 8331.1Sjmcneill WR4(sc, VI_BFBR, 0); 8341.1Sjmcneill} 8351.1Sjmcneill 8361.1Sjmcneillstatic int 8371.1Sjmcneillwiifb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l) 8381.1Sjmcneill{ 8391.1Sjmcneill struct wiifb_softc *sc = v; 8401.1Sjmcneill struct wsdisplayio_bus_id *busid; 8411.1Sjmcneill struct wsdisplayio_fbinfo *fbi; 8421.1Sjmcneill u_int video; 8431.1Sjmcneill u_int wsmode; 8441.1Sjmcneill 8451.1Sjmcneill switch (cmd) { 8461.1Sjmcneill case WSDISPLAYIO_GTYPE: 8471.1Sjmcneill *(u_int *)data = WSDISPLAY_TYPE_GENFB; 8481.1Sjmcneill return 0; 8491.1Sjmcneill case WSDISPLAYIO_GET_BUSID: 8501.1Sjmcneill busid = data; 8511.1Sjmcneill busid->bus_type = WSDISPLAYIO_BUS_SOC; 8521.1Sjmcneill return 0; 8531.1Sjmcneill case WSDISPLAYIO_GET_FBINFO: 8541.1Sjmcneill fbi = data; 8551.1Sjmcneill /* 8561.1Sjmcneill * rasops info does not match the pixel encoding due to our 8571.1Sjmcneill * devcmap, so fill out fbinfo manually instead of relying 8581.1Sjmcneill * on wsdisplayio_get_fbinfo. 8591.1Sjmcneill */ 8601.1Sjmcneill fbi->fbi_fboffset = 0; 8611.1Sjmcneill fbi->fbi_width = WIIFB_RGB_WIDTH; 8621.1Sjmcneill fbi->fbi_height = WIIFB_RGB_HEIGHT; 8631.1Sjmcneill fbi->fbi_bitsperpixel = WIIFB_RGB_BPP; 8641.1Sjmcneill fbi->fbi_stride = fbi->fbi_width * fbi->fbi_bitsperpixel / 8; 8651.1Sjmcneill fbi->fbi_fbsize = fbi->fbi_height * fbi->fbi_stride; 8661.1Sjmcneill fbi->fbi_pixeltype = WSFB_RGB; 8671.1Sjmcneill fbi->fbi_flags = WSFB_VRAM_IS_RAM; 8681.1Sjmcneill return 0; 8691.1Sjmcneill 8701.1Sjmcneill case WSDISPLAYIO_SVIDEO: 8711.1Sjmcneill video = *(u_int *)data; 8721.1Sjmcneill switch (video) { 8731.1Sjmcneill case WSDISPLAYIO_VIDEO_OFF: 8741.1Sjmcneill out32(HW_VIDIM, __SHIFTIN(7, VIDIM_Y) | 8751.1Sjmcneill __SHIFTIN(7, VIDIM_C) | 8761.1Sjmcneill VIDIM_E); 8771.1Sjmcneill return 0; 8781.1Sjmcneill case WSDISPLAYIO_VIDEO_ON: 8791.1Sjmcneill out32(HW_VIDIM, 0); 8801.1Sjmcneill return 0; 8811.1Sjmcneill default: 8821.1Sjmcneill return EINVAL; 8831.1Sjmcneill } 8841.1Sjmcneill 8851.1Sjmcneill case WSDISPLAYIO_SMODE: 8861.1Sjmcneill wsmode = *(u_int *)data; 8871.1Sjmcneill if (wsmode != WSDISPLAYIO_MODE_EMUL) { 8881.1Sjmcneill /* Blank the RGB FB when leaving text mode */ 8891.1Sjmcneill memset(sc->sc_rgb.dma_addr, 0, sc->sc_rgb.dma_size); 8901.1Sjmcneill } 8911.1Sjmcneill if (sc->sc_wsmode != wsmode) { 8921.1Sjmcneill sc->sc_wsmode = wsmode; 8931.1Sjmcneill 8941.1Sjmcneill if (wsmode == WSDISPLAYIO_MODE_EMUL) { 8951.1Sjmcneill wiifb_clear_xfb(sc); 8961.1Sjmcneill } 8971.1Sjmcneill } 8981.1Sjmcneill return EPASSTHROUGH; 8991.1Sjmcneill } 9001.1Sjmcneill 9011.1Sjmcneill return EPASSTHROUGH; 9021.1Sjmcneill} 9031.1Sjmcneill 9041.1Sjmcneillstatic paddr_t 9051.1Sjmcneillwiifb_mmap(void *v, void *vs, off_t off, int prot) 9061.1Sjmcneill{ 9071.1Sjmcneill struct wiifb_softc *sc = v; 9081.1Sjmcneill 9091.1Sjmcneill if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL || sc->sc_efb == NULL) { 9101.1Sjmcneill return -1; 9111.1Sjmcneill } 9121.1Sjmcneill 9131.1Sjmcneill if (off < 0 || off >= sc->sc_rgb.dma_size) { 9141.1Sjmcneill return -1; 9151.1Sjmcneill } 9161.1Sjmcneill 9171.1Sjmcneill return bus_dmamem_mmap(sc->sc_rgb.dma_tag, 9181.1Sjmcneill sc->sc_rgb.dma_segs, sc->sc_rgb.dma_nsegs, 9191.1Sjmcneill off, prot, BUS_DMA_PREFETCHABLE); 9201.1Sjmcneill} 921