Home | History | Annotate | Line # | Download | only in dev
      1 /* $NetBSD: wiiufb.c,v 1.5 2026/02/01 12:09:40 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2025 Jared McNeill <jmcneill (at) 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.5 2026/02/01 12:09:40 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 */
     50 static 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 WIIUFB_CURMAX		64
     61 #define WIIUFB_CURSOR_SIZE	(WIIUFB_CURMAX * WIIUFB_CURMAX * 4)
     62 #define WIIUFB_CURSOR_ALIGN	0x1000
     63 #define WIIUFB_CURSOR_BITS	(WIIUFB_CURMAX * WIIUFB_CURMAX / NBBY)
     64 
     65 #define REG_OFFSET(d, r)		((d) * 0x800 + (r))
     66 #define DGRPH_SWAP_CNTL(d)		REG_OFFSET(d, 0x610c)
     67 #define  DGRPH_SWAP_ENDIAN_SWAP		__BITS(1, 0)
     68 #define  DGRPH_SWAP_ENDIAN_SWAP_8IN32	__SHIFTIN(2, DGRPH_SWAP_ENDIAN_SWAP)
     69 #define DCRTC_BLANK_CONTROL(d)		REG_OFFSET(d, 0x6084)
     70 #define  DCRTC_BLANK_DATA_EN		__BIT(8)
     71 #define DCUR_CONTROL(d)			REG_OFFSET(d, 0x6400)
     72 #define  DCUR_CONTROL_EN		__BIT(0)
     73 #define  DCUR_CONTROL_MODE		__BITS(9, 8)
     74 #define  DCUR_CONTROL_MODE_ARGB_PM	__SHIFTIN(2, DCUR_CONTROL_MODE)
     75 #define DCUR_SURFACE_ADDRESS(d)		REG_OFFSET(d, 0x6408)
     76 #define DCUR_SIZE(d)			REG_OFFSET(d, 0x6410)
     77 #define  DCUR_SIZE_HEIGHT(h)		((h) - 1)
     78 #define  DCUR_SIZE_WIDTH(w)		(((w) - 1) << 16)
     79 #define DCUR_POSITION(d)		REG_OFFSET(d, 0x6414)
     80 #define  DCUR_POSITION_Y(y)		(y)
     81 #define  DCUR_POSITION_X(x)		((x) << 16)
     82 #define DCUR_HOT_SPOT(d)		REG_OFFSET(d, 0x6418)
     83 #define  DCUR_HOT_SPOT_Y(y)		(y)
     84 #define  DCUR_HOT_SPOT_X(x)		((x) << 16)
     85 #define DCUR_UPDATE(d)			REG_OFFSET(d, 0x6424)
     86 #define  DCUR_UPDATE_LOCK		__BIT(16)
     87 
     88 struct wiiufb_dma {
     89 	bus_dmamap_t		dma_map;
     90 	bus_dma_tag_t		dma_tag;
     91 	bus_size_t		dma_size;
     92 	bus_dma_segment_t	dma_segs[1];
     93 	int			dma_nsegs;
     94 	void			*dma_addr;
     95 };
     96 
     97 struct wiiufb_cursor {
     98 	bool			c_enable;
     99 	struct wsdisplay_curpos	c_pos;
    100 	struct wsdisplay_curpos	c_hot;
    101 	struct wsdisplay_curpos	c_size;
    102 	uint32_t		c_cmap[2];
    103 	uint8_t			c_image[WIIUFB_CURSOR_BITS];
    104 	uint8_t			c_mask[WIIUFB_CURSOR_BITS];
    105 };
    106 
    107 struct wiiufb_softc {
    108 	struct genfb_softc	sc_gen;
    109 
    110 	bus_space_tag_t		sc_bst;
    111 	bus_space_handle_t	sc_bsh;
    112 	bus_dma_tag_t		sc_dmat;
    113 
    114 	uint32_t		sc_disp;
    115 
    116 	struct wiiufb_cursor	sc_cursor;
    117 	struct wiiufb_cursor	sc_tempcursor;
    118 	struct wiiufb_dma	sc_curdma;
    119 };
    120 
    121 static struct wiiufb_softc *wiiufb_sc;
    122 
    123 static int	wiiufb_match(device_t, cfdata_t, void *);
    124 static void	wiiufb_attach(device_t, device_t, void *);
    125 
    126 static bool	wiiufb_shutdown(device_t, int);
    127 static int	wiiufb_ioctl(void *, void *, u_long, void *, int, lwp_t *);
    128 static paddr_t	wiiufb_mmap(void *, void *, off_t, int);
    129 
    130 static int	wiiufb_dma_alloc(struct wiiufb_softc *, bus_size_t, bus_size_t,
    131 				 int, struct wiiufb_dma *);
    132 static void	wiiufb_gpu_write(uint16_t, uint32_t);
    133 static uint32_t	wiiufb_gpu_read(uint16_t);
    134 static void	wiiufb_gpu_set(uint16_t, uint32_t);
    135 static void	wiiufb_gpu_clear(uint16_t, uint32_t);
    136 
    137 void		wiiufb_consinit(void);
    138 
    139 static struct genfb_ops wiiufb_ops = {
    140 	.genfb_ioctl = wiiufb_ioctl,
    141 	.genfb_mmap = wiiufb_mmap,
    142 };
    143 
    144 struct vcons_screen wiiufb_console_screen;
    145 static struct wsscreen_descr wiiufb_stdscreen = {
    146 	"std",
    147 	0, 0,
    148 	0,
    149 	0, 0,
    150 	0,
    151 	NULL
    152 };
    153 
    154 CFATTACH_DECL_NEW(wiiufb, sizeof(struct wiiufb_softc),
    155 	wiiufb_match, wiiufb_attach, NULL, NULL);
    156 
    157 static int
    158 wiiufb_match(device_t parent, cfdata_t cf, void *aux)
    159 {
    160 	struct mainbus_attach_args *maa = aux;
    161 
    162 	return wiiu_native && strcmp(maa->maa_name, "genfb") == 0;
    163 }
    164 
    165 static void
    166 wiiufb_attach(device_t parent, device_t self, void *aux)
    167 {
    168 	struct wiiufb_softc *sc = device_private(self);
    169 	prop_dictionary_t dict = device_properties(self);
    170 	struct mainbus_attach_args *maa = aux;
    171 	void *bits;
    172 	int error;
    173 
    174 	sc->sc_gen.sc_dev = self;
    175 	sc->sc_bst = maa->maa_bst;
    176 	error = bus_space_map(sc->sc_bst, maa->maa_addr, WIIU_GX2_SIZE, 0,
    177 	    &sc->sc_bsh);
    178 	if (error != 0) {
    179 		panic("couldn't map registers");
    180 	}
    181 	sc->sc_dmat = maa->maa_dmat;
    182 	sc->sc_disp = wiiufb_drc ? 1 : 0;
    183 
    184 	wiiufb_sc = sc;
    185 
    186 	/*
    187 	 * powerpc bus_space_map will use the BAT mapping if present,
    188 	 * ignoring the map flags passed in. Just use mapiodev directly
    189 	 * to ensure that the FB is mapped by the kernel pmap.
    190 	 */
    191 	bits = mapiodev(WIIUFB_BASE, WIIUFB_SIZE, true);
    192 
    193 	prop_dictionary_set_uint32(dict, "width", WIIUFB_WIDTH);
    194 	prop_dictionary_set_uint32(dict, "height", WIIUFB_HEIGHT);
    195 	prop_dictionary_set_uint8(dict, "depth", WIIUFB_BPP);
    196 	prop_dictionary_set_uint16(dict, "linebytes", WIIUFB_STRIDE);
    197 	prop_dictionary_set_uint32(dict, "address", WIIUFB_BASE);
    198 	prop_dictionary_set_uint32(dict, "virtual_address", (uintptr_t)bits);
    199 
    200 	genfb_init(&sc->sc_gen);
    201 
    202 	aprint_naive("\n");
    203 	aprint_normal(": Wii U %s framebuffer (%ux%u %u-bpp @ 0x%08x)\n",
    204 	    sc->sc_disp ? "DRC" : "TV", WIIUFB_WIDTH, WIIUFB_HEIGHT,
    205 	    WIIUFB_BPP, WIIUFB_BASE);
    206 
    207 	pmf_device_register1(self, NULL, NULL, wiiufb_shutdown);
    208 
    209 	error = wiiufb_dma_alloc(sc, WIIUFB_CURSOR_SIZE, WIIUFB_CURSOR_ALIGN,
    210 	    BUS_DMA_NOCACHE, &sc->sc_curdma);
    211 	if (error != 0) {
    212 		panic("couldn't alloc hardware cursor");
    213 	}
    214 	wiiufb_gpu_set(DCUR_UPDATE(sc->sc_disp), DCUR_UPDATE_LOCK);
    215 	wiiufb_gpu_set(DCUR_CONTROL(sc->sc_disp), 0);
    216 	wiiufb_gpu_clear(DCUR_UPDATE(sc->sc_disp), DCUR_UPDATE_LOCK);
    217 
    218 	genfb_cnattach();
    219 	prop_dictionary_set_bool(dict, "is_console", true);
    220 	genfb_attach(&sc->sc_gen, &wiiufb_ops);
    221 }
    222 
    223 static bool
    224 wiiufb_shutdown(device_t self, int flags)
    225 {
    226 	genfb_enable_polling(self);
    227 	return true;
    228 }
    229 
    230 static int
    231 wiiufb_dma_alloc(struct wiiufb_softc *sc, bus_size_t size, bus_size_t align,
    232     int flags, struct wiiufb_dma *dma)
    233 {
    234 	bus_dma_tag_t dmat = sc->sc_dmat;
    235 	int error;
    236 
    237 	error = bus_dmamem_alloc(dmat, size, align, 0,
    238 	    dma->dma_segs, 1, &dma->dma_nsegs, BUS_DMA_WAITOK);
    239 	if (error)
    240 		return error;
    241 
    242 	error = bus_dmamem_map(dmat, dma->dma_segs, dma->dma_nsegs,
    243 	    size, &dma->dma_addr, BUS_DMA_WAITOK | flags);
    244 	if (error)
    245 		goto free;
    246 
    247 	error = bus_dmamap_create(dmat, size, dma->dma_nsegs,
    248 	    size, 0, BUS_DMA_WAITOK, &dma->dma_map);
    249 	if (error)
    250 		goto unmap;
    251 
    252 	error = bus_dmamap_load(dmat, dma->dma_map, dma->dma_addr,
    253 	    size, NULL, BUS_DMA_WAITOK);
    254 	if (error)
    255 	    goto destroy;
    256 
    257 	dma->dma_size = size;
    258 	dma->dma_tag = dmat;
    259 
    260 	memset(dma->dma_addr, 0, dma->dma_size);
    261 
    262 	return 0;
    263 
    264 destroy:
    265 	bus_dmamap_destroy(dmat, dma->dma_map);
    266 unmap:
    267 	bus_dmamem_unmap(dmat, dma->dma_addr, dma->dma_size);
    268 free:
    269 	bus_dmamem_free(dmat, dma->dma_segs, dma->dma_nsegs);
    270 
    271 	return error;
    272 }
    273 
    274 static void
    275 wiiufb_cursor_shape(struct wiiufb_softc *sc)
    276 {
    277 	const uint8_t *msk = sc->sc_cursor.c_mask;
    278 	const uint8_t *img = sc->sc_cursor.c_image;
    279 	uint32_t *out = sc->sc_curdma.dma_addr;
    280 	uint8_t bit;
    281 	int i, j, px;
    282 
    283 	for (i = 0; i < WIIUFB_CURMAX * NBBY; i++) {
    284 		bit = 1;
    285 		for (j = 0; j < 8; j++) {
    286 			px = ((*msk & bit) ? 2 : 0) | ((*img & bit) ? 1 : 0);
    287 			switch (px) {
    288 			case 0:
    289 			case 1:
    290 				*out = 0;
    291 				break;
    292 			case 2:
    293 			case 3:
    294 				*out = htole32(
    295 				    0xff000000 | sc->sc_cursor.c_cmap[px - 2]);
    296 				break;
    297 			}
    298 			out++;
    299 			bit <<= 1;
    300 		}
    301 		msk++;
    302 		img++;
    303 	}
    304 }
    305 
    306 static void
    307 wiiufb_cursor_visible(struct wiiufb_softc *sc)
    308 {
    309 	uint32_t control;
    310 
    311 	wiiufb_gpu_set(DCUR_UPDATE(sc->sc_disp), DCUR_UPDATE_LOCK);
    312 
    313 	if (sc->sc_cursor.c_enable) {
    314 		control = DCUR_CONTROL_EN | DCUR_CONTROL_MODE_ARGB_PM;
    315 	} else {
    316 		control = 0;
    317 	}
    318 	wiiufb_gpu_write(DCUR_CONTROL(sc->sc_disp), control);
    319 
    320 	wiiufb_gpu_clear(DCUR_UPDATE(sc->sc_disp), DCUR_UPDATE_LOCK);
    321 }
    322 
    323 static void
    324 wiiufb_cursor_position(struct wiiufb_softc *sc)
    325 {
    326 	int x, y;
    327 
    328 	wiiufb_gpu_set(DCUR_UPDATE(sc->sc_disp), DCUR_UPDATE_LOCK);
    329 
    330 	x = uimin(sc->sc_cursor.c_pos.x, WIIUFB_WIDTH - 1);
    331 	y = uimin(sc->sc_cursor.c_pos.y, WIIUFB_HEIGHT - 1);
    332 
    333 	wiiufb_gpu_write(DCUR_SIZE(sc->sc_disp),
    334 	    DCUR_SIZE_WIDTH(WIIUFB_CURMAX) |
    335 	    DCUR_SIZE_HEIGHT(WIIUFB_CURMAX));
    336 	wiiufb_gpu_write(DCUR_SURFACE_ADDRESS(sc->sc_disp),
    337 	    sc->sc_curdma.dma_segs[0].ds_addr);
    338 	wiiufb_gpu_write(DCUR_POSITION(sc->sc_disp),
    339 	    DCUR_POSITION_X(x) |
    340 	    DCUR_POSITION_Y(y));
    341 	wiiufb_gpu_write(DCUR_HOT_SPOT(sc->sc_disp),
    342 	    DCUR_HOT_SPOT_X(sc->sc_cursor.c_hot.x) |
    343 	    DCUR_HOT_SPOT_Y(sc->sc_cursor.c_hot.y));
    344 
    345 	wiiufb_gpu_clear(DCUR_UPDATE(sc->sc_disp), DCUR_UPDATE_LOCK);
    346 }
    347 
    348 static void
    349 wiiufb_cursor_update(struct wiiufb_softc *sc, unsigned which)
    350 {
    351 	if ((which & (WSDISPLAY_CURSOR_DOCMAP|WSDISPLAY_CURSOR_DOSHAPE)) != 0) {
    352 		wiiufb_cursor_shape(sc);
    353 	}
    354 
    355 	if ((which & WSDISPLAY_CURSOR_DOCUR) != 0) {
    356 		wiiufb_cursor_visible(sc);
    357 	}
    358 
    359 	wiiufb_cursor_position(sc);
    360 }
    361 
    362 static int
    363 wiiufb_set_cursor(struct wiiufb_softc *sc, struct wsdisplay_cursor *wc)
    364 {
    365 	uint8_t r[2], g[2], b[2];
    366 	unsigned which, index, count;
    367 	int i, err, pitch, size;
    368 	struct wiiufb_cursor *nc = &sc->sc_tempcursor;
    369 
    370 	which = wc->which;
    371 	*nc = sc->sc_cursor;
    372 
    373 	if ((which & WSDISPLAY_CURSOR_DOCMAP) != 0) {
    374 		index = wc->cmap.index;
    375 		count = wc->cmap.count;
    376 
    377 		if (index >= 2 || count > 2 - index) {
    378 			return EINVAL;
    379 		}
    380 
    381 		err = copyin(wc->cmap.red, &r[index], count);
    382 		if (err != 0) {
    383 			return err;
    384 		}
    385 		err = copyin(wc->cmap.green, &g[index], count);
    386 		if (err != 0) {
    387 			return err;
    388 		}
    389 		err = copyin(wc->cmap.blue, &b[index], count);
    390 		if (err != 0) {
    391 			return err;
    392 		}
    393 
    394 		for (i = index; i < index + count; i++) {
    395 			nc->c_cmap[i] =
    396 			    (r[i] << 16) + (g[i] << 8) + (b[i] << 0);
    397 		}
    398 	}
    399 
    400 	if ((which & WSDISPLAY_CURSOR_DOSHAPE) != 0) {
    401 		if (wc->size.x > WIIUFB_CURMAX || wc->size.y > WIIUFB_CURMAX) {
    402 			return EINVAL;
    403 		}
    404 
    405 		pitch = (wc->size.x + 7) / 8;
    406 		size = pitch * wc->size.y;
    407 
    408 		memset(nc->c_image, 0, sizeof(nc->c_image));
    409 		memset(nc->c_mask, 0, sizeof(nc->c_mask));
    410 		nc->c_size = wc->size;
    411 
    412 		if ((err = copyin(wc->image, nc->c_image, size)) != 0) {
    413 			return err;
    414 		}
    415 		if ((err = copyin(wc->mask, nc->c_mask, size)) != 0) {
    416 			return err;
    417 		}
    418 	}
    419 
    420 	if ((which & WSDISPLAY_CURSOR_DOHOT) != 0) {
    421 		nc->c_hot = wc->hot;
    422 		if (nc->c_hot.x >= nc->c_size.x) {
    423 			nc->c_hot.x = nc->c_size.x - 1;
    424 		}
    425 		if (nc->c_hot.y >= nc->c_size.y) {
    426 			nc->c_hot.y = nc->c_size.y - 1;
    427 		}
    428 	}
    429 
    430 	if ((which & WSDISPLAY_CURSOR_DOPOS) != 0) {
    431 		nc->c_pos = wc->pos;
    432 		if (nc->c_pos.x >= WIIUFB_WIDTH) {
    433 			nc->c_pos.x = WIIUFB_WIDTH - 1;
    434 		}
    435 		if (nc->c_pos.y >= WIIUFB_HEIGHT) {
    436 			nc->c_pos.y = WIIUFB_HEIGHT - 1;
    437 		}
    438 	}
    439 
    440 	if ((which & WSDISPLAY_CURSOR_DOCUR) != 0) {
    441 		nc->c_enable = wc->enable;
    442 	}
    443 
    444 	sc->sc_cursor = *nc;
    445 	wiiufb_cursor_update(sc, wc->which);
    446 
    447 	return 0;
    448 }
    449 
    450 static int
    451 wiiufb_set_curpos(struct wiiufb_softc *sc, struct wsdisplay_curpos *pos)
    452 {
    453 	sc->sc_cursor.c_pos.x = uimin(uimax(pos->x, 0), WIIUFB_WIDTH - 1);
    454 	sc->sc_cursor.c_pos.y = uimin(uimax(pos->y, 0), WIIUFB_HEIGHT - 1);
    455 
    456 	wiiufb_cursor_position(sc);
    457 
    458 	return 0;
    459 }
    460 
    461 static int
    462 wiiufb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l)
    463 {
    464 	struct wiiufb_softc *sc = v;
    465 	struct wsdisplayio_bus_id *busid;
    466 	struct wsdisplayio_fbinfo *fbi;
    467 	struct wsdisplay_curpos *cp;
    468 	struct rasops_info *ri;
    469 	u_int video;
    470 	int error;
    471 
    472 	switch (cmd) {
    473 	case WSDISPLAYIO_GTYPE:
    474 		*(u_int *)data = WSDISPLAY_TYPE_GENFB;
    475 		return 0;
    476 	case WSDISPLAYIO_GET_BUSID:
    477 		busid = data;
    478 		busid->bus_type = WSDISPLAYIO_BUS_SOC;
    479 		return 0;
    480 	case WSDISPLAYIO_GET_FBINFO:
    481 		fbi = data;
    482 		ri = &sc->sc_gen.vd.active->scr_ri;
    483 		error = wsdisplayio_get_fbinfo(ri, fbi);
    484 		if (error == 0) {
    485 			fbi->fbi_flags |= WSFB_VRAM_IS_RAM;
    486 		}
    487 		return error;
    488 	case WSDISPLAYIO_SVIDEO:
    489 		video = *(u_int *)data;
    490 		switch (video) {
    491 		case WSDISPLAYIO_VIDEO_OFF:
    492 			wiiufb_gpu_write(DCRTC_BLANK_CONTROL(sc->sc_disp),
    493 			    DCRTC_BLANK_DATA_EN);
    494 			break;
    495 		case WSDISPLAYIO_VIDEO_ON:
    496 			wiiufb_gpu_write(DCRTC_BLANK_CONTROL(sc->sc_disp), 0);
    497 			break;
    498 		default:
    499 			return EINVAL;
    500 		}
    501 		return 0;
    502 	case WSDISPLAYIO_GCURMAX:
    503 		cp = data;
    504 		cp->x = WIIUFB_CURMAX;
    505 		cp->y = WIIUFB_CURMAX;
    506 		return 0;
    507 	case WSDISPLAYIO_GCURPOS:
    508 		cp = data;
    509 		cp->x = sc->sc_cursor.c_pos.x;
    510 		cp->y = sc->sc_cursor.c_pos.y;
    511 		return 0;
    512 	case WSDISPLAYIO_SCURPOS:
    513 		return wiiufb_set_curpos(sc, data);
    514 	case WSDISPLAYIO_SCURSOR:
    515 		return wiiufb_set_cursor(sc, data);
    516 	}
    517 
    518 	return EPASSTHROUGH;
    519 }
    520 
    521 static paddr_t
    522 wiiufb_mmap(void *v, void *vs, off_t off, int prot)
    523 {
    524 	struct wiiufb_softc *sc = v;
    525 
    526 	if (off < 0 || off >= WIIUFB_SIZE) {
    527 		return -1;
    528 	}
    529 
    530 	return bus_space_mmap(sc->sc_bst, WIIUFB_BASE, off, prot,
    531 	    BUS_SPACE_MAP_LINEAR | BUS_DMA_PREFETCHABLE);
    532 }
    533 
    534 static void
    535 wiiufb_gpu_write(uint16_t reg, uint32_t data)
    536 {
    537 	if (wiiufb_sc != NULL) {
    538 		bus_space_write_4(wiiufb_sc->sc_bst, wiiufb_sc->sc_bsh,
    539 		    reg, data);
    540 	} else {
    541 		out32(LT_GPUINDADDR, LT_GPUINDADDR_REGSPACE_GPU | reg);
    542 		out32(LT_GPUINDDATA, data);
    543 		in32(LT_GPUINDDATA);
    544 	}
    545 }
    546 
    547 static uint32_t
    548 wiiufb_gpu_read(uint16_t reg)
    549 {
    550 	if (wiiufb_sc != NULL) {
    551 		return bus_space_read_4(wiiufb_sc->sc_bst, wiiufb_sc->sc_bsh,
    552 		    reg);
    553 	} else {
    554 		out32(LT_GPUINDADDR, LT_GPUINDADDR_REGSPACE_GPU | reg);
    555 		return in32(LT_GPUINDDATA);
    556 	}
    557 }
    558 
    559 static void
    560 wiiufb_gpu_set(uint16_t reg, uint32_t mask)
    561 {
    562 	wiiufb_gpu_write(reg, wiiufb_gpu_read(reg) | mask);
    563 }
    564 
    565 static void
    566 wiiufb_gpu_clear(uint16_t reg, uint32_t mask)
    567 {
    568 	wiiufb_gpu_write(reg, wiiufb_gpu_read(reg) & ~mask);
    569 }
    570 
    571 void
    572 wiiufb_consinit(void)
    573 {
    574 	extern char wii_cmdline[];
    575 	struct rasops_info *ri = &wiiufb_console_screen.scr_ri;
    576 	long defattr;
    577 	void *bits;
    578 	const char *cmdline = wii_cmdline;
    579 
    580 	memset(&wiiufb_console_screen, 0, sizeof(wiiufb_console_screen));
    581 
    582 	while (*cmdline != '\0') {
    583 		if (strcmp(cmdline, "video=drc") == 0) {
    584 			/* Output to the gamepad instead of TV. */
    585 			wiiufb_drc = true;
    586 			break;
    587 		}
    588 		cmdline += strlen(cmdline) + 1;
    589 	}
    590 
    591 	/* Blank the CRTC we are not using. */
    592 	wiiufb_gpu_write(DCRTC_BLANK_CONTROL(wiiufb_drc ? 0 : 1),
    593 	    DCRTC_BLANK_DATA_EN);
    594 
    595 	/* Ensure that the ARGB8888 framebuffer is in a sane state. */
    596 	wiiufb_gpu_write(DGRPH_SWAP_CNTL(0), DGRPH_SWAP_ENDIAN_SWAP_8IN32);
    597 	wiiufb_gpu_write(DGRPH_SWAP_CNTL(1), DGRPH_SWAP_ENDIAN_SWAP_8IN32);
    598 
    599 	/*
    600 	 * Need to use the BAT mapping here as pmap isn't initialized yet.
    601 	 *
    602 	 * Unfortunately, we have a single large (256MB) BAT mapping to cover
    603 	 * both conventional memory and the framebuffer in MEM1, which means
    604 	 * the early FB is mapped cacheable. Better than nothing (it's
    605 	 * useful for debugging) and it's only like this until wiiufb is
    606 	 * attached later on.
    607 	 *
    608 	 * This could be enhanced in the future to hook in to rasops and
    609 	 * insert proper cache maintenance operations. Just don't flush the
    610 	 * whole framebuffer every time something changes, it will be very
    611 	 * slow.
    612 	 */
    613 	bits = (void *)WIIUFB_BASE;
    614 
    615 	wsfont_init();
    616 
    617 	ri->ri_width = WIIUFB_WIDTH;
    618 	ri->ri_height = WIIUFB_HEIGHT;
    619 	ri->ri_depth = WIIUFB_BPP;
    620 	ri->ri_stride = WIIUFB_STRIDE;
    621 	ri->ri_bits = bits;
    622 	ri->ri_flg = RI_NO_AUTO | RI_CLEAR | RI_FULLCLEAR | RI_CENTER;
    623 	rasops_init(ri, ri->ri_height / 8, ri->ri_width / 8);
    624 
    625 	ri->ri_caps = WSSCREEN_WSCOLORS;
    626 	rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight,
    627 	    ri->ri_width / ri->ri_font->fontwidth);
    628 
    629 	wiiufb_stdscreen.nrows = ri->ri_rows;
    630 	wiiufb_stdscreen.ncols = ri->ri_cols;
    631 	wiiufb_stdscreen.textops = &ri->ri_ops;
    632 	wiiufb_stdscreen.capabilities = ri->ri_caps;
    633 
    634 	ri->ri_ops.allocattr(ri, 0, 0, 0, &defattr);
    635 
    636 	wsdisplay_preattach(&wiiufb_stdscreen, ri, 0, 0, defattr);
    637 }
    638