wiiufb.c revision 1.2
11.2Sjmcneill/* $NetBSD: wiiufb.c,v 1.2 2026/01/10 23:10:02 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.2Sjmcneill__KERNEL_RCSID(0, "$NetBSD: wiiufb.c,v 1.2 2026/01/10 23:10:02 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.2Sjmcneill	extern char wii_cmdline[];
2361.1Sjmcneill	struct rasops_info *ri = &wiiufb_console_screen.scr_ri;
2371.1Sjmcneill	long defattr;
2381.1Sjmcneill	void *bits;
2391.2Sjmcneill	const char *cmdline = wii_cmdline;
2401.1Sjmcneill
2411.1Sjmcneill	memset(&wiiufb_console_screen, 0, sizeof(wiiufb_console_screen));
2421.1Sjmcneill
2431.2Sjmcneill	while (*cmdline != '\0') {
2441.2Sjmcneill		if (strcmp(cmdline, "video=drc") == 0) {
2451.2Sjmcneill			/* Output to the gamepad instead of TV. */
2461.2Sjmcneill			wiiufb_drc = true;
2471.2Sjmcneill			break;
2481.2Sjmcneill		}
2491.2Sjmcneill		cmdline += strlen(cmdline) + 1;
2501.2Sjmcneill	}
2511.2Sjmcneill
2521.1Sjmcneill	/*
2531.1Sjmcneill	 * Need to use the BAT mapping here as pmap isn't initialized yet.
2541.1Sjmcneill	 *
2551.1Sjmcneill	 * Unfortunately, we have a single large (256MB) BAT mapping to cover
2561.1Sjmcneill	 * both conventional memory and the framebuffer in MEM1, which means
2571.1Sjmcneill	 * the early FB is mapped cacheable. Better than nothing (it's
2581.1Sjmcneill	 * useful for debugging) and it's only like this until wiiufb is
2591.1Sjmcneill	 * attached later on.
2601.1Sjmcneill	 *
2611.1Sjmcneill	 * This could be enhanced in the future to hook in to rasops and
2621.1Sjmcneill	 * insert proper cache maintenance operations. Just don't flush the
2631.1Sjmcneill	 * whole framebuffer every time something changes, it will be very
2641.1Sjmcneill	 * slow.
2651.1Sjmcneill	 */
2661.1Sjmcneill	bits = (void *)WIIUFB_BASE;
2671.1Sjmcneill
2681.1Sjmcneill	wsfont_init();
2691.1Sjmcneill
2701.1Sjmcneill	ri->ri_width = WIIUFB_WIDTH;
2711.1Sjmcneill	ri->ri_height = WIIUFB_HEIGHT;
2721.1Sjmcneill	ri->ri_depth = WIIUFB_BPP;
2731.1Sjmcneill	ri->ri_stride = WIIUFB_STRIDE;
2741.1Sjmcneill	ri->ri_bits = bits;
2751.1Sjmcneill	ri->ri_rnum = ri->ri_gnum = ri->ri_bnum = 8;
2761.1Sjmcneill	ri->ri_bpos = 0;
2771.1Sjmcneill	ri->ri_rpos = 8;
2781.1Sjmcneill	ri->ri_gpos = 16;
2791.1Sjmcneill	ri->ri_flg = RI_NO_AUTO | RI_CLEAR | RI_FULLCLEAR | RI_CENTER;
2801.1Sjmcneill	rasops_init(ri, ri->ri_height / 8, ri->ri_width / 8);
2811.1Sjmcneill
2821.1Sjmcneill	ri->ri_caps = WSSCREEN_WSCOLORS;
2831.1Sjmcneill	rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight,
2841.1Sjmcneill	    ri->ri_width / ri->ri_font->fontwidth);
2851.1Sjmcneill
2861.1Sjmcneill	wiiufb_stdscreen.nrows = ri->ri_rows;
2871.1Sjmcneill	wiiufb_stdscreen.ncols = ri->ri_cols;
2881.1Sjmcneill	wiiufb_stdscreen.textops = &ri->ri_ops;
2891.1Sjmcneill	wiiufb_stdscreen.capabilities = ri->ri_caps;
2901.1Sjmcneill
2911.1Sjmcneill	ri->ri_ops.allocattr(ri, 0, 0, 0, &defattr);
2921.1Sjmcneill
2931.1Sjmcneill	wsdisplay_preattach(&wiiufb_stdscreen, ri, 0, 0, defattr);
2941.1Sjmcneill}
295