wiiufb.c revision 1.1
11.1Sjmcneill/* $NetBSD: wiiufb.c,v 1.1 2026/01/09 22:54:30 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.1Sjmcneill__KERNEL_RCSID(0, "$NetBSD: wiiufb.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 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.1Sjmcneill#define D1CRTC_BLANK_CONTROL 0x6084 611.1Sjmcneill#define D2CRTC_BLANK_CONTROL 0x6884 621.1Sjmcneill#define DCRTC_BLANK_DATA_EN __BIT(8) 631.1Sjmcneill 641.1Sjmcneillstruct wiiufb_softc { 651.1Sjmcneill struct genfb_softc sc_gen; 661.1Sjmcneill 671.1Sjmcneill bus_space_tag_t sc_bst; 681.1Sjmcneill bus_space_handle_t sc_bsh; 691.1Sjmcneill 701.1Sjmcneill uint32_t sc_blank_ctrl; 711.1Sjmcneill}; 721.1Sjmcneill 731.1Sjmcneillstatic int wiiufb_match(device_t, cfdata_t, void *); 741.1Sjmcneillstatic void wiiufb_attach(device_t, device_t, void *); 751.1Sjmcneill 761.1Sjmcneillstatic bool wiiufb_shutdown(device_t, int); 771.1Sjmcneillstatic int wiiufb_ioctl(void *, void *, u_long, void *, int, lwp_t *); 781.1Sjmcneillstatic paddr_t wiiufb_mmap(void *, void *, off_t, int); 791.1Sjmcneill 801.1Sjmcneillstatic void wiiufb_gpu_write(uint16_t, uint32_t); 811.1Sjmcneill 821.1Sjmcneillvoid wiiufb_consinit(void); 831.1Sjmcneill 841.1Sjmcneillstatic struct genfb_ops wiiufb_ops = { 851.1Sjmcneill .genfb_ioctl = wiiufb_ioctl, 861.1Sjmcneill .genfb_mmap = wiiufb_mmap, 871.1Sjmcneill}; 881.1Sjmcneill 891.1Sjmcneillstruct vcons_screen wiiufb_console_screen; 901.1Sjmcneillstatic struct wsscreen_descr wiiufb_stdscreen = { 911.1Sjmcneill "std", 921.1Sjmcneill 0, 0, 931.1Sjmcneill 0, 941.1Sjmcneill 0, 0, 951.1Sjmcneill 0, 961.1Sjmcneill NULL 971.1Sjmcneill}; 981.1Sjmcneill 991.1SjmcneillCFATTACH_DECL_NEW(wiiufb, sizeof(struct wiiufb_softc), 1001.1Sjmcneill wiiufb_match, wiiufb_attach, NULL, NULL); 1011.1Sjmcneill 1021.1Sjmcneillstatic int 1031.1Sjmcneillwiiufb_match(device_t parent, cfdata_t cf, void *aux) 1041.1Sjmcneill{ 1051.1Sjmcneill struct mainbus_attach_args *maa = aux; 1061.1Sjmcneill 1071.1Sjmcneill return wiiu_native && strcmp(maa->maa_name, "genfb") == 0; 1081.1Sjmcneill} 1091.1Sjmcneill 1101.1Sjmcneillstatic void 1111.1Sjmcneillwiiufb_attach(device_t parent, device_t self, void *aux) 1121.1Sjmcneill{ 1131.1Sjmcneill struct wiiufb_softc *sc = device_private(self); 1141.1Sjmcneill prop_dictionary_t dict = device_properties(self); 1151.1Sjmcneill struct mainbus_attach_args *maa = aux; 1161.1Sjmcneill void *bits; 1171.1Sjmcneill 1181.1Sjmcneill sc->sc_gen.sc_dev = self; 1191.1Sjmcneill sc->sc_bst = maa->maa_bst; 1201.1Sjmcneill 1211.1Sjmcneill /* 1221.1Sjmcneill * powerpc bus_space_map will use the BAT mapping if present, 1231.1Sjmcneill * ignoring the map flags passed in. Just use mapiodev directly 1241.1Sjmcneill * to ensure that the FB is mapped by the kernel pmap. 1251.1Sjmcneill */ 1261.1Sjmcneill bits = mapiodev(WIIUFB_BASE, WIIUFB_SIZE, true); 1271.1Sjmcneill 1281.1Sjmcneill prop_dictionary_set_uint32(dict, "width", WIIUFB_WIDTH); 1291.1Sjmcneill prop_dictionary_set_uint32(dict, "height", WIIUFB_HEIGHT); 1301.1Sjmcneill prop_dictionary_set_uint8(dict, "depth", WIIUFB_BPP); 1311.1Sjmcneill prop_dictionary_set_uint16(dict, "linebytes", WIIUFB_STRIDE); 1321.1Sjmcneill prop_dictionary_set_uint32(dict, "address", WIIUFB_BASE); 1331.1Sjmcneill prop_dictionary_set_uint32(dict, "virtual_address", (uintptr_t)bits); 1341.1Sjmcneill prop_dictionary_set_bool(dict, "is_brg", true); 1351.1Sjmcneill 1361.1Sjmcneill genfb_init(&sc->sc_gen); 1371.1Sjmcneill 1381.1Sjmcneill aprint_naive("\n"); 1391.1Sjmcneill aprint_normal(": Wii U %s framebuffer (%ux%u %u-bpp @ 0x%08x)\n", 1401.1Sjmcneill wiiufb_drc ? "DRC" : "TV", WIIUFB_WIDTH, WIIUFB_HEIGHT, 1411.1Sjmcneill WIIUFB_BPP, WIIUFB_BASE); 1421.1Sjmcneill 1431.1Sjmcneill pmf_device_register1(self, NULL, NULL, wiiufb_shutdown); 1441.1Sjmcneill 1451.1Sjmcneill genfb_cnattach(); 1461.1Sjmcneill prop_dictionary_set_bool(dict, "is_console", true); 1471.1Sjmcneill genfb_attach(&sc->sc_gen, &wiiufb_ops); 1481.1Sjmcneill 1491.1Sjmcneill sc->sc_blank_ctrl = wiiufb_drc ? 1501.1Sjmcneill D2CRTC_BLANK_CONTROL : D1CRTC_BLANK_CONTROL; 1511.1Sjmcneill 1521.1Sjmcneill /* Blank the CRTC we are not using. */ 1531.1Sjmcneill if (wiiufb_drc) { 1541.1Sjmcneill wiiufb_gpu_write(D1CRTC_BLANK_CONTROL, DCRTC_BLANK_DATA_EN); 1551.1Sjmcneill } else { 1561.1Sjmcneill wiiufb_gpu_write(D2CRTC_BLANK_CONTROL, DCRTC_BLANK_DATA_EN); 1571.1Sjmcneill } 1581.1Sjmcneill} 1591.1Sjmcneill 1601.1Sjmcneillstatic bool 1611.1Sjmcneillwiiufb_shutdown(device_t self, int flags) 1621.1Sjmcneill{ 1631.1Sjmcneill genfb_enable_polling(self); 1641.1Sjmcneill return true; 1651.1Sjmcneill} 1661.1Sjmcneill 1671.1Sjmcneillstatic int 1681.1Sjmcneillwiiufb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l) 1691.1Sjmcneill{ 1701.1Sjmcneill struct wiiufb_softc *sc = v; 1711.1Sjmcneill struct wsdisplayio_bus_id *busid; 1721.1Sjmcneill struct wsdisplayio_fbinfo *fbi; 1731.1Sjmcneill struct rasops_info *ri; 1741.1Sjmcneill u_int video; 1751.1Sjmcneill int error; 1761.1Sjmcneill 1771.1Sjmcneill switch (cmd) { 1781.1Sjmcneill case WSDISPLAYIO_GTYPE: 1791.1Sjmcneill *(u_int *)data = WSDISPLAY_TYPE_GENFB; 1801.1Sjmcneill return 0; 1811.1Sjmcneill case WSDISPLAYIO_GET_BUSID: 1821.1Sjmcneill busid = data; 1831.1Sjmcneill busid->bus_type = WSDISPLAYIO_BUS_SOC; 1841.1Sjmcneill return 0; 1851.1Sjmcneill case WSDISPLAYIO_GET_FBINFO: 1861.1Sjmcneill fbi = data; 1871.1Sjmcneill ri = &sc->sc_gen.vd.active->scr_ri; 1881.1Sjmcneill error = wsdisplayio_get_fbinfo(ri, fbi); 1891.1Sjmcneill if (error == 0) { 1901.1Sjmcneill fbi->fbi_flags |= WSFB_VRAM_IS_RAM; 1911.1Sjmcneill } 1921.1Sjmcneill return error; 1931.1Sjmcneill case WSDISPLAYIO_SVIDEO: 1941.1Sjmcneill video = *(u_int *)data; 1951.1Sjmcneill switch (video) { 1961.1Sjmcneill case WSDISPLAYIO_VIDEO_OFF: 1971.1Sjmcneill wiiufb_gpu_write(sc->sc_blank_ctrl, DCRTC_BLANK_DATA_EN); 1981.1Sjmcneill break; 1991.1Sjmcneill case WSDISPLAYIO_VIDEO_ON: 2001.1Sjmcneill wiiufb_gpu_write(sc->sc_blank_ctrl, 0); 2011.1Sjmcneill break; 2021.1Sjmcneill default: 2031.1Sjmcneill return EINVAL; 2041.1Sjmcneill } 2051.1Sjmcneill return 0; 2061.1Sjmcneill } 2071.1Sjmcneill 2081.1Sjmcneill return EPASSTHROUGH; 2091.1Sjmcneill} 2101.1Sjmcneill 2111.1Sjmcneillstatic paddr_t 2121.1Sjmcneillwiiufb_mmap(void *v, void *vs, off_t off, int prot) 2131.1Sjmcneill{ 2141.1Sjmcneill struct wiiufb_softc *sc = v; 2151.1Sjmcneill 2161.1Sjmcneill if (off < 0 || off >= WIIUFB_SIZE) { 2171.1Sjmcneill return -1; 2181.1Sjmcneill } 2191.1Sjmcneill 2201.1Sjmcneill return bus_space_mmap(sc->sc_bst, WIIUFB_BASE, off, prot, 2211.1Sjmcneill BUS_SPACE_MAP_LINEAR | BUS_DMA_PREFETCHABLE); 2221.1Sjmcneill} 2231.1Sjmcneill 2241.1Sjmcneillstatic void 2251.1Sjmcneillwiiufb_gpu_write(uint16_t reg, uint32_t data) 2261.1Sjmcneill{ 2271.1Sjmcneill out32(LT_GPUINDADDR, LT_GPUINDADDR_REGSPACE_GPU | reg); 2281.1Sjmcneill out32(LT_GPUINDDATA, data); 2291.1Sjmcneill in32(LT_GPUINDDATA); 2301.1Sjmcneill} 2311.1Sjmcneill 2321.1Sjmcneillvoid 2331.1Sjmcneillwiiufb_consinit(void) 2341.1Sjmcneill{ 2351.1Sjmcneill struct rasops_info *ri = &wiiufb_console_screen.scr_ri; 2361.1Sjmcneill long defattr; 2371.1Sjmcneill void *bits; 2381.1Sjmcneill 2391.1Sjmcneill memset(&wiiufb_console_screen, 0, sizeof(wiiufb_console_screen)); 2401.1Sjmcneill 2411.1Sjmcneill /* 2421.1Sjmcneill * Need to use the BAT mapping here as pmap isn't initialized yet. 2431.1Sjmcneill * 2441.1Sjmcneill * Unfortunately, we have a single large (256MB) BAT mapping to cover 2451.1Sjmcneill * both conventional memory and the framebuffer in MEM1, which means 2461.1Sjmcneill * the early FB is mapped cacheable. Better than nothing (it's 2471.1Sjmcneill * useful for debugging) and it's only like this until wiiufb is 2481.1Sjmcneill * attached later on. 2491.1Sjmcneill * 2501.1Sjmcneill * This could be enhanced in the future to hook in to rasops and 2511.1Sjmcneill * insert proper cache maintenance operations. Just don't flush the 2521.1Sjmcneill * whole framebuffer every time something changes, it will be very 2531.1Sjmcneill * slow. 2541.1Sjmcneill */ 2551.1Sjmcneill bits = (void *)WIIUFB_BASE; 2561.1Sjmcneill 2571.1Sjmcneill wsfont_init(); 2581.1Sjmcneill 2591.1Sjmcneill ri->ri_width = WIIUFB_WIDTH; 2601.1Sjmcneill ri->ri_height = WIIUFB_HEIGHT; 2611.1Sjmcneill ri->ri_depth = WIIUFB_BPP; 2621.1Sjmcneill ri->ri_stride = WIIUFB_STRIDE; 2631.1Sjmcneill ri->ri_bits = bits; 2641.1Sjmcneill ri->ri_rnum = ri->ri_gnum = ri->ri_bnum = 8; 2651.1Sjmcneill ri->ri_bpos = 0; 2661.1Sjmcneill ri->ri_rpos = 8; 2671.1Sjmcneill ri->ri_gpos = 16; 2681.1Sjmcneill ri->ri_flg = RI_NO_AUTO | RI_CLEAR | RI_FULLCLEAR | RI_CENTER; 2691.1Sjmcneill rasops_init(ri, ri->ri_height / 8, ri->ri_width / 8); 2701.1Sjmcneill 2711.1Sjmcneill ri->ri_caps = WSSCREEN_WSCOLORS; 2721.1Sjmcneill rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight, 2731.1Sjmcneill ri->ri_width / ri->ri_font->fontwidth); 2741.1Sjmcneill 2751.1Sjmcneill wiiufb_stdscreen.nrows = ri->ri_rows; 2761.1Sjmcneill wiiufb_stdscreen.ncols = ri->ri_cols; 2771.1Sjmcneill wiiufb_stdscreen.textops = &ri->ri_ops; 2781.1Sjmcneill wiiufb_stdscreen.capabilities = ri->ri_caps; 2791.1Sjmcneill 2801.1Sjmcneill ri->ri_ops.allocattr(ri, 0, 0, 0, &defattr); 2811.1Sjmcneill 2821.1Sjmcneill wsdisplay_preattach(&wiiufb_stdscreen, ri, 0, 0, defattr); 2831.1Sjmcneill} 284