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