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