wiiufb.c revision 1.3
11.3Sjmcneill/* $NetBSD: wiiufb.c,v 1.3 2026/01/10 23:55:24 jmcneill Exp $ */ 21.1Sjmcneill 31.1Sjmcneill/*- 41.1Sjmcneill * Copyright (c) 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.3Sjmcneill__KERNEL_RCSID(0, "$NetBSD: wiiufb.c,v 1.3 2026/01/10 23:55:24 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 401.1Sjmcneill#include <dev/wscons/wsconsio.h> 411.1Sjmcneill#include <dev/wscons/wsdisplayvar.h> 421.1Sjmcneill#include <dev/rasops/rasops.h> 431.1Sjmcneill#include <dev/wsfont/wsfont.h> 441.1Sjmcneill#include <dev/wscons/wsdisplay_vconsvar.h> 451.1Sjmcneill#include <dev/wsfb/genfbvar.h> 461.1Sjmcneill 471.1Sjmcneill#include "mainbus.h" 481.1Sjmcneill 491.1Sjmcneill/* Set to true to output to Gamepad instead of TV */ 501.1Sjmcneillstatic bool wiiufb_drc; 511.1Sjmcneill 521.1Sjmcneill#define WIIUFB_BASE (wiiufb_drc ? WIIU_GFX_DRC_BASE : \ 531.1Sjmcneill WIIU_GFX_TV_BASE) 541.1Sjmcneill#define WIIUFB_WIDTH (wiiufb_drc ? 854 : 1280) 551.1Sjmcneill#define WIIUFB_HEIGHT (wiiufb_drc ? 480 : 720) 561.1Sjmcneill#define WIIUFB_BPP 32 571.1Sjmcneill#define WIIUFB_STRIDE ((wiiufb_drc ? 896 : 1280) * WIIUFB_BPP / NBBY) 581.1Sjmcneill#define WIIUFB_SIZE (WIIUFB_STRIDE * WIIUFB_HEIGHT) 591.1Sjmcneill 601.3Sjmcneill#define D1GRPH_SWAP_CNTL 0x610c 611.3Sjmcneill#define D2GRPH_SWAP_CNTL 0x690c 621.3Sjmcneill#define DGRPH_SWAP_ENDIAN_SWAP __BITS(1, 0) 631.3Sjmcneill#define DGRPH_SWAP_ENDIAN_SWAP_8IN32 __SHIFTIN(2, DGRPH_SWAP_ENDIAN_SWAP) 641.3Sjmcneill#define D1CRTC_BLANK_CONTROL 0x6084 651.3Sjmcneill#define D2CRTC_BLANK_CONTROL 0x6884 661.3Sjmcneill#define DCRTC_BLANK_DATA_EN __BIT(8) 671.1Sjmcneill 681.1Sjmcneillstruct wiiufb_softc { 691.1Sjmcneill struct genfb_softc sc_gen; 701.1Sjmcneill 711.1Sjmcneill bus_space_tag_t sc_bst; 721.1Sjmcneill bus_space_handle_t sc_bsh; 731.1Sjmcneill 741.1Sjmcneill uint32_t sc_blank_ctrl; 751.1Sjmcneill}; 761.1Sjmcneill 771.1Sjmcneillstatic int wiiufb_match(device_t, cfdata_t, void *); 781.1Sjmcneillstatic void wiiufb_attach(device_t, device_t, void *); 791.1Sjmcneill 801.1Sjmcneillstatic bool wiiufb_shutdown(device_t, int); 811.1Sjmcneillstatic int wiiufb_ioctl(void *, void *, u_long, void *, int, lwp_t *); 821.1Sjmcneillstatic paddr_t wiiufb_mmap(void *, void *, off_t, int); 831.1Sjmcneill 841.1Sjmcneillstatic void wiiufb_gpu_write(uint16_t, uint32_t); 851.1Sjmcneill 861.1Sjmcneillvoid wiiufb_consinit(void); 871.1Sjmcneill 881.1Sjmcneillstatic struct genfb_ops wiiufb_ops = { 891.1Sjmcneill .genfb_ioctl = wiiufb_ioctl, 901.1Sjmcneill .genfb_mmap = wiiufb_mmap, 911.1Sjmcneill}; 921.1Sjmcneill 931.1Sjmcneillstruct vcons_screen wiiufb_console_screen; 941.1Sjmcneillstatic struct wsscreen_descr wiiufb_stdscreen = { 951.1Sjmcneill "std", 961.1Sjmcneill 0, 0, 971.1Sjmcneill 0, 981.1Sjmcneill 0, 0, 991.1Sjmcneill 0, 1001.1Sjmcneill NULL 1011.1Sjmcneill}; 1021.1Sjmcneill 1031.1SjmcneillCFATTACH_DECL_NEW(wiiufb, sizeof(struct wiiufb_softc), 1041.1Sjmcneill wiiufb_match, wiiufb_attach, NULL, NULL); 1051.1Sjmcneill 1061.1Sjmcneillstatic int 1071.1Sjmcneillwiiufb_match(device_t parent, cfdata_t cf, void *aux) 1081.1Sjmcneill{ 1091.1Sjmcneill struct mainbus_attach_args *maa = aux; 1101.1Sjmcneill 1111.1Sjmcneill return wiiu_native && strcmp(maa->maa_name, "genfb") == 0; 1121.1Sjmcneill} 1131.1Sjmcneill 1141.1Sjmcneillstatic void 1151.1Sjmcneillwiiufb_attach(device_t parent, device_t self, void *aux) 1161.1Sjmcneill{ 1171.1Sjmcneill struct wiiufb_softc *sc = device_private(self); 1181.1Sjmcneill prop_dictionary_t dict = device_properties(self); 1191.1Sjmcneill struct mainbus_attach_args *maa = aux; 1201.1Sjmcneill void *bits; 1211.1Sjmcneill 1221.1Sjmcneill sc->sc_gen.sc_dev = self; 1231.1Sjmcneill sc->sc_bst = maa->maa_bst; 1241.1Sjmcneill 1251.1Sjmcneill /* 1261.1Sjmcneill * powerpc bus_space_map will use the BAT mapping if present, 1271.1Sjmcneill * ignoring the map flags passed in. Just use mapiodev directly 1281.1Sjmcneill * to ensure that the FB is mapped by the kernel pmap. 1291.1Sjmcneill */ 1301.1Sjmcneill bits = mapiodev(WIIUFB_BASE, WIIUFB_SIZE, true); 1311.1Sjmcneill 1321.1Sjmcneill prop_dictionary_set_uint32(dict, "width", WIIUFB_WIDTH); 1331.1Sjmcneill prop_dictionary_set_uint32(dict, "height", WIIUFB_HEIGHT); 1341.1Sjmcneill prop_dictionary_set_uint8(dict, "depth", WIIUFB_BPP); 1351.1Sjmcneill prop_dictionary_set_uint16(dict, "linebytes", WIIUFB_STRIDE); 1361.1Sjmcneill prop_dictionary_set_uint32(dict, "address", WIIUFB_BASE); 1371.1Sjmcneill prop_dictionary_set_uint32(dict, "virtual_address", (uintptr_t)bits); 1381.1Sjmcneill 1391.1Sjmcneill genfb_init(&sc->sc_gen); 1401.1Sjmcneill 1411.1Sjmcneill aprint_naive("\n"); 1421.1Sjmcneill aprint_normal(": Wii U %s framebuffer (%ux%u %u-bpp @ 0x%08x)\n", 1431.1Sjmcneill wiiufb_drc ? "DRC" : "TV", WIIUFB_WIDTH, WIIUFB_HEIGHT, 1441.1Sjmcneill WIIUFB_BPP, WIIUFB_BASE); 1451.1Sjmcneill 1461.1Sjmcneill pmf_device_register1(self, NULL, NULL, wiiufb_shutdown); 1471.1Sjmcneill 1481.1Sjmcneill genfb_cnattach(); 1491.1Sjmcneill prop_dictionary_set_bool(dict, "is_console", true); 1501.1Sjmcneill genfb_attach(&sc->sc_gen, &wiiufb_ops); 1511.1Sjmcneill 1521.1Sjmcneill sc->sc_blank_ctrl = wiiufb_drc ? 1531.1Sjmcneill D2CRTC_BLANK_CONTROL : D1CRTC_BLANK_CONTROL; 1541.1Sjmcneill} 1551.1Sjmcneill 1561.1Sjmcneillstatic bool 1571.1Sjmcneillwiiufb_shutdown(device_t self, int flags) 1581.1Sjmcneill{ 1591.1Sjmcneill genfb_enable_polling(self); 1601.1Sjmcneill return true; 1611.1Sjmcneill} 1621.1Sjmcneill 1631.1Sjmcneillstatic int 1641.1Sjmcneillwiiufb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l) 1651.1Sjmcneill{ 1661.1Sjmcneill struct wiiufb_softc *sc = v; 1671.1Sjmcneill struct wsdisplayio_bus_id *busid; 1681.1Sjmcneill struct wsdisplayio_fbinfo *fbi; 1691.1Sjmcneill struct rasops_info *ri; 1701.1Sjmcneill u_int video; 1711.1Sjmcneill int error; 1721.1Sjmcneill 1731.1Sjmcneill switch (cmd) { 1741.1Sjmcneill case WSDISPLAYIO_GTYPE: 1751.1Sjmcneill *(u_int *)data = WSDISPLAY_TYPE_GENFB; 1761.1Sjmcneill return 0; 1771.1Sjmcneill case WSDISPLAYIO_GET_BUSID: 1781.1Sjmcneill busid = data; 1791.1Sjmcneill busid->bus_type = WSDISPLAYIO_BUS_SOC; 1801.1Sjmcneill return 0; 1811.1Sjmcneill case WSDISPLAYIO_GET_FBINFO: 1821.1Sjmcneill fbi = data; 1831.1Sjmcneill ri = &sc->sc_gen.vd.active->scr_ri; 1841.1Sjmcneill error = wsdisplayio_get_fbinfo(ri, fbi); 1851.1Sjmcneill if (error == 0) { 1861.1Sjmcneill fbi->fbi_flags |= WSFB_VRAM_IS_RAM; 1871.1Sjmcneill } 1881.1Sjmcneill return error; 1891.1Sjmcneill case WSDISPLAYIO_SVIDEO: 1901.1Sjmcneill video = *(u_int *)data; 1911.1Sjmcneill switch (video) { 1921.1Sjmcneill case WSDISPLAYIO_VIDEO_OFF: 1931.1Sjmcneill wiiufb_gpu_write(sc->sc_blank_ctrl, DCRTC_BLANK_DATA_EN); 1941.1Sjmcneill break; 1951.1Sjmcneill case WSDISPLAYIO_VIDEO_ON: 1961.1Sjmcneill wiiufb_gpu_write(sc->sc_blank_ctrl, 0); 1971.1Sjmcneill break; 1981.1Sjmcneill default: 1991.1Sjmcneill return EINVAL; 2001.1Sjmcneill } 2011.1Sjmcneill return 0; 2021.1Sjmcneill } 2031.1Sjmcneill 2041.1Sjmcneill return EPASSTHROUGH; 2051.1Sjmcneill} 2061.1Sjmcneill 2071.1Sjmcneillstatic paddr_t 2081.1Sjmcneillwiiufb_mmap(void *v, void *vs, off_t off, int prot) 2091.1Sjmcneill{ 2101.1Sjmcneill struct wiiufb_softc *sc = v; 2111.1Sjmcneill 2121.1Sjmcneill if (off < 0 || off >= WIIUFB_SIZE) { 2131.1Sjmcneill return -1; 2141.1Sjmcneill } 2151.1Sjmcneill 2161.1Sjmcneill return bus_space_mmap(sc->sc_bst, WIIUFB_BASE, off, prot, 2171.1Sjmcneill BUS_SPACE_MAP_LINEAR | BUS_DMA_PREFETCHABLE); 2181.1Sjmcneill} 2191.1Sjmcneill 2201.1Sjmcneillstatic void 2211.1Sjmcneillwiiufb_gpu_write(uint16_t reg, uint32_t data) 2221.1Sjmcneill{ 2231.1Sjmcneill out32(LT_GPUINDADDR, LT_GPUINDADDR_REGSPACE_GPU | reg); 2241.1Sjmcneill out32(LT_GPUINDDATA, data); 2251.1Sjmcneill in32(LT_GPUINDDATA); 2261.1Sjmcneill} 2271.1Sjmcneill 2281.1Sjmcneillvoid 2291.1Sjmcneillwiiufb_consinit(void) 2301.1Sjmcneill{ 2311.2Sjmcneill extern char wii_cmdline[]; 2321.1Sjmcneill struct rasops_info *ri = &wiiufb_console_screen.scr_ri; 2331.1Sjmcneill long defattr; 2341.1Sjmcneill void *bits; 2351.2Sjmcneill const char *cmdline = wii_cmdline; 2361.1Sjmcneill 2371.1Sjmcneill memset(&wiiufb_console_screen, 0, sizeof(wiiufb_console_screen)); 2381.1Sjmcneill 2391.2Sjmcneill while (*cmdline != '\0') { 2401.2Sjmcneill if (strcmp(cmdline, "video=drc") == 0) { 2411.2Sjmcneill /* Output to the gamepad instead of TV. */ 2421.2Sjmcneill wiiufb_drc = true; 2431.2Sjmcneill break; 2441.2Sjmcneill } 2451.2Sjmcneill cmdline += strlen(cmdline) + 1; 2461.2Sjmcneill } 2471.2Sjmcneill 2481.3Sjmcneill /* Blank the CRTC we are not using. */ 2491.3Sjmcneill if (wiiufb_drc) { 2501.3Sjmcneill wiiufb_gpu_write(D1CRTC_BLANK_CONTROL, DCRTC_BLANK_DATA_EN); 2511.3Sjmcneill } else { 2521.3Sjmcneill wiiufb_gpu_write(D2CRTC_BLANK_CONTROL, DCRTC_BLANK_DATA_EN); 2531.3Sjmcneill } 2541.3Sjmcneill 2551.3Sjmcneill /* Ensure that the ARGB8888 framebuffer is in a sane state. */ 2561.3Sjmcneill wiiufb_gpu_write(D1GRPH_SWAP_CNTL, DGRPH_SWAP_ENDIAN_SWAP_8IN32); 2571.3Sjmcneill wiiufb_gpu_write(D2GRPH_SWAP_CNTL, DGRPH_SWAP_ENDIAN_SWAP_8IN32); 2581.3Sjmcneill 2591.1Sjmcneill /* 2601.1Sjmcneill * Need to use the BAT mapping here as pmap isn't initialized yet. 2611.1Sjmcneill * 2621.1Sjmcneill * Unfortunately, we have a single large (256MB) BAT mapping to cover 2631.1Sjmcneill * both conventional memory and the framebuffer in MEM1, which means 2641.1Sjmcneill * the early FB is mapped cacheable. Better than nothing (it's 2651.1Sjmcneill * useful for debugging) and it's only like this until wiiufb is 2661.1Sjmcneill * attached later on. 2671.1Sjmcneill * 2681.1Sjmcneill * This could be enhanced in the future to hook in to rasops and 2691.1Sjmcneill * insert proper cache maintenance operations. Just don't flush the 2701.1Sjmcneill * whole framebuffer every time something changes, it will be very 2711.1Sjmcneill * slow. 2721.1Sjmcneill */ 2731.1Sjmcneill bits = (void *)WIIUFB_BASE; 2741.1Sjmcneill 2751.1Sjmcneill wsfont_init(); 2761.1Sjmcneill 2771.1Sjmcneill ri->ri_width = WIIUFB_WIDTH; 2781.1Sjmcneill ri->ri_height = WIIUFB_HEIGHT; 2791.1Sjmcneill ri->ri_depth = WIIUFB_BPP; 2801.1Sjmcneill ri->ri_stride = WIIUFB_STRIDE; 2811.1Sjmcneill ri->ri_bits = bits; 2821.1Sjmcneill ri->ri_flg = RI_NO_AUTO | RI_CLEAR | RI_FULLCLEAR | RI_CENTER; 2831.1Sjmcneill rasops_init(ri, ri->ri_height / 8, ri->ri_width / 8); 2841.1Sjmcneill 2851.1Sjmcneill ri->ri_caps = WSSCREEN_WSCOLORS; 2861.1Sjmcneill rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight, 2871.1Sjmcneill ri->ri_width / ri->ri_font->fontwidth); 2881.1Sjmcneill 2891.1Sjmcneill wiiufb_stdscreen.nrows = ri->ri_rows; 2901.1Sjmcneill wiiufb_stdscreen.ncols = ri->ri_cols; 2911.1Sjmcneill wiiufb_stdscreen.textops = &ri->ri_ops; 2921.1Sjmcneill wiiufb_stdscreen.capabilities = ri->ri_caps; 2931.1Sjmcneill 2941.1Sjmcneill ri->ri_ops.allocattr(ri, 0, 0, 0, &defattr); 2951.1Sjmcneill 2961.1Sjmcneill wsdisplay_preattach(&wiiufb_stdscreen, ri, 0, 0, defattr); 2971.1Sjmcneill} 298