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