wiiufb.c revision 1.3
1/* $NetBSD: wiiufb.c,v 1.3 2026/01/10 23:55:24 jmcneill Exp $ */ 2 3/*- 4 * Copyright (c) 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: wiiufb.c,v 1.3 2026/01/10 23:55:24 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 */ 50static 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 D1GRPH_SWAP_CNTL 0x610c 61#define D2GRPH_SWAP_CNTL 0x690c 62#define DGRPH_SWAP_ENDIAN_SWAP __BITS(1, 0) 63#define DGRPH_SWAP_ENDIAN_SWAP_8IN32 __SHIFTIN(2, DGRPH_SWAP_ENDIAN_SWAP) 64#define D1CRTC_BLANK_CONTROL 0x6084 65#define D2CRTC_BLANK_CONTROL 0x6884 66#define DCRTC_BLANK_DATA_EN __BIT(8) 67 68struct wiiufb_softc { 69 struct genfb_softc sc_gen; 70 71 bus_space_tag_t sc_bst; 72 bus_space_handle_t sc_bsh; 73 74 uint32_t sc_blank_ctrl; 75}; 76 77static int wiiufb_match(device_t, cfdata_t, void *); 78static void wiiufb_attach(device_t, device_t, void *); 79 80static bool wiiufb_shutdown(device_t, int); 81static int wiiufb_ioctl(void *, void *, u_long, void *, int, lwp_t *); 82static paddr_t wiiufb_mmap(void *, void *, off_t, int); 83 84static void wiiufb_gpu_write(uint16_t, uint32_t); 85 86void wiiufb_consinit(void); 87 88static struct genfb_ops wiiufb_ops = { 89 .genfb_ioctl = wiiufb_ioctl, 90 .genfb_mmap = wiiufb_mmap, 91}; 92 93struct vcons_screen wiiufb_console_screen; 94static struct wsscreen_descr wiiufb_stdscreen = { 95 "std", 96 0, 0, 97 0, 98 0, 0, 99 0, 100 NULL 101}; 102 103CFATTACH_DECL_NEW(wiiufb, sizeof(struct wiiufb_softc), 104 wiiufb_match, wiiufb_attach, NULL, NULL); 105 106static int 107wiiufb_match(device_t parent, cfdata_t cf, void *aux) 108{ 109 struct mainbus_attach_args *maa = aux; 110 111 return wiiu_native && strcmp(maa->maa_name, "genfb") == 0; 112} 113 114static void 115wiiufb_attach(device_t parent, device_t self, void *aux) 116{ 117 struct wiiufb_softc *sc = device_private(self); 118 prop_dictionary_t dict = device_properties(self); 119 struct mainbus_attach_args *maa = aux; 120 void *bits; 121 122 sc->sc_gen.sc_dev = self; 123 sc->sc_bst = maa->maa_bst; 124 125 /* 126 * powerpc bus_space_map will use the BAT mapping if present, 127 * ignoring the map flags passed in. Just use mapiodev directly 128 * to ensure that the FB is mapped by the kernel pmap. 129 */ 130 bits = mapiodev(WIIUFB_BASE, WIIUFB_SIZE, true); 131 132 prop_dictionary_set_uint32(dict, "width", WIIUFB_WIDTH); 133 prop_dictionary_set_uint32(dict, "height", WIIUFB_HEIGHT); 134 prop_dictionary_set_uint8(dict, "depth", WIIUFB_BPP); 135 prop_dictionary_set_uint16(dict, "linebytes", WIIUFB_STRIDE); 136 prop_dictionary_set_uint32(dict, "address", WIIUFB_BASE); 137 prop_dictionary_set_uint32(dict, "virtual_address", (uintptr_t)bits); 138 139 genfb_init(&sc->sc_gen); 140 141 aprint_naive("\n"); 142 aprint_normal(": Wii U %s framebuffer (%ux%u %u-bpp @ 0x%08x)\n", 143 wiiufb_drc ? "DRC" : "TV", WIIUFB_WIDTH, WIIUFB_HEIGHT, 144 WIIUFB_BPP, WIIUFB_BASE); 145 146 pmf_device_register1(self, NULL, NULL, wiiufb_shutdown); 147 148 genfb_cnattach(); 149 prop_dictionary_set_bool(dict, "is_console", true); 150 genfb_attach(&sc->sc_gen, &wiiufb_ops); 151 152 sc->sc_blank_ctrl = wiiufb_drc ? 153 D2CRTC_BLANK_CONTROL : D1CRTC_BLANK_CONTROL; 154} 155 156static bool 157wiiufb_shutdown(device_t self, int flags) 158{ 159 genfb_enable_polling(self); 160 return true; 161} 162 163static int 164wiiufb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l) 165{ 166 struct wiiufb_softc *sc = v; 167 struct wsdisplayio_bus_id *busid; 168 struct wsdisplayio_fbinfo *fbi; 169 struct rasops_info *ri; 170 u_int video; 171 int error; 172 173 switch (cmd) { 174 case WSDISPLAYIO_GTYPE: 175 *(u_int *)data = WSDISPLAY_TYPE_GENFB; 176 return 0; 177 case WSDISPLAYIO_GET_BUSID: 178 busid = data; 179 busid->bus_type = WSDISPLAYIO_BUS_SOC; 180 return 0; 181 case WSDISPLAYIO_GET_FBINFO: 182 fbi = data; 183 ri = &sc->sc_gen.vd.active->scr_ri; 184 error = wsdisplayio_get_fbinfo(ri, fbi); 185 if (error == 0) { 186 fbi->fbi_flags |= WSFB_VRAM_IS_RAM; 187 } 188 return error; 189 case WSDISPLAYIO_SVIDEO: 190 video = *(u_int *)data; 191 switch (video) { 192 case WSDISPLAYIO_VIDEO_OFF: 193 wiiufb_gpu_write(sc->sc_blank_ctrl, DCRTC_BLANK_DATA_EN); 194 break; 195 case WSDISPLAYIO_VIDEO_ON: 196 wiiufb_gpu_write(sc->sc_blank_ctrl, 0); 197 break; 198 default: 199 return EINVAL; 200 } 201 return 0; 202 } 203 204 return EPASSTHROUGH; 205} 206 207static paddr_t 208wiiufb_mmap(void *v, void *vs, off_t off, int prot) 209{ 210 struct wiiufb_softc *sc = v; 211 212 if (off < 0 || off >= WIIUFB_SIZE) { 213 return -1; 214 } 215 216 return bus_space_mmap(sc->sc_bst, WIIUFB_BASE, off, prot, 217 BUS_SPACE_MAP_LINEAR | BUS_DMA_PREFETCHABLE); 218} 219 220static void 221wiiufb_gpu_write(uint16_t reg, uint32_t data) 222{ 223 out32(LT_GPUINDADDR, LT_GPUINDADDR_REGSPACE_GPU | reg); 224 out32(LT_GPUINDDATA, data); 225 in32(LT_GPUINDDATA); 226} 227 228void 229wiiufb_consinit(void) 230{ 231 extern char wii_cmdline[]; 232 struct rasops_info *ri = &wiiufb_console_screen.scr_ri; 233 long defattr; 234 void *bits; 235 const char *cmdline = wii_cmdline; 236 237 memset(&wiiufb_console_screen, 0, sizeof(wiiufb_console_screen)); 238 239 while (*cmdline != '\0') { 240 if (strcmp(cmdline, "video=drc") == 0) { 241 /* Output to the gamepad instead of TV. */ 242 wiiufb_drc = true; 243 break; 244 } 245 cmdline += strlen(cmdline) + 1; 246 } 247 248 /* Blank the CRTC we are not using. */ 249 if (wiiufb_drc) { 250 wiiufb_gpu_write(D1CRTC_BLANK_CONTROL, DCRTC_BLANK_DATA_EN); 251 } else { 252 wiiufb_gpu_write(D2CRTC_BLANK_CONTROL, DCRTC_BLANK_DATA_EN); 253 } 254 255 /* Ensure that the ARGB8888 framebuffer is in a sane state. */ 256 wiiufb_gpu_write(D1GRPH_SWAP_CNTL, DGRPH_SWAP_ENDIAN_SWAP_8IN32); 257 wiiufb_gpu_write(D2GRPH_SWAP_CNTL, DGRPH_SWAP_ENDIAN_SWAP_8IN32); 258 259 /* 260 * Need to use the BAT mapping here as pmap isn't initialized yet. 261 * 262 * Unfortunately, we have a single large (256MB) BAT mapping to cover 263 * both conventional memory and the framebuffer in MEM1, which means 264 * the early FB is mapped cacheable. Better than nothing (it's 265 * useful for debugging) and it's only like this until wiiufb is 266 * attached later on. 267 * 268 * This could be enhanced in the future to hook in to rasops and 269 * insert proper cache maintenance operations. Just don't flush the 270 * whole framebuffer every time something changes, it will be very 271 * slow. 272 */ 273 bits = (void *)WIIUFB_BASE; 274 275 wsfont_init(); 276 277 ri->ri_width = WIIUFB_WIDTH; 278 ri->ri_height = WIIUFB_HEIGHT; 279 ri->ri_depth = WIIUFB_BPP; 280 ri->ri_stride = WIIUFB_STRIDE; 281 ri->ri_bits = bits; 282 ri->ri_flg = RI_NO_AUTO | RI_CLEAR | RI_FULLCLEAR | RI_CENTER; 283 rasops_init(ri, ri->ri_height / 8, ri->ri_width / 8); 284 285 ri->ri_caps = WSSCREEN_WSCOLORS; 286 rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight, 287 ri->ri_width / ri->ri_font->fontwidth); 288 289 wiiufb_stdscreen.nrows = ri->ri_rows; 290 wiiufb_stdscreen.ncols = ri->ri_cols; 291 wiiufb_stdscreen.textops = &ri->ri_ops; 292 wiiufb_stdscreen.capabilities = ri->ri_caps; 293 294 ri->ri_ops.allocattr(ri, 0, 0, 0, &defattr); 295 296 wsdisplay_preattach(&wiiufb_stdscreen, ri, 0, 0, defattr); 297} 298