11.1Sjmcneill/* $NetBSD: wiifb.c,v 1.1 2026/01/09 22:54:30 jmcneill Exp $ */
21.1Sjmcneill
31.1Sjmcneill/*-
41.1Sjmcneill * Copyright (c) 2024-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.1Sjmcneill__KERNEL_RCSID(0, "$NetBSD: wiifb.c,v 1.1 2026/01/09 22:54:30 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#include <powerpc/spr.h>
401.1Sjmcneill#include <powerpc/oea/spr.h>
411.1Sjmcneill#include <powerpc/oea/hid.h>
421.1Sjmcneill
431.1Sjmcneill#include <dev/videomode/videomode.h>
441.1Sjmcneill#include <dev/wsfb/genfbvar.h>
451.1Sjmcneill
461.1Sjmcneill#include "mainbus.h"
471.1Sjmcneill#include "vireg.h"
481.1Sjmcneill#include "gxreg.h"
491.1Sjmcneill
501.1Sjmcneill#define WIIFB_RGB_WIDTH			640
511.1Sjmcneill#define WIIFB_RGB_HEIGHT		480
521.1Sjmcneill#define WIIFB_RGB_BPP			32
531.1Sjmcneill
541.1Sjmcneill#define WIIFB_ERROR_BLINK_INTERVAL	1000000
551.1Sjmcneill
561.1Sjmcneill#define WIIFB_TOP_BOTTOM_BORDER		16
571.1Sjmcneill#define WIIFB_EFFECTIVE_START(p, w)	\
581.1Sjmcneill	((uintptr_t)(p) + WIIFB_TOP_BOTTOM_BORDER * (w) * 2)
591.1Sjmcneill#define WIIFB_EFFECTIVE_HEIGHT(h)	\
601.1Sjmcneill	((h) - WIIFB_TOP_BOTTOM_BORDER * 2)
611.1Sjmcneill
621.1Sjmcneill#define WIIFB_FIFO_SIZE			(256 * 1024)
631.1Sjmcneill
641.1Sjmcneill#define IBM750CL_SPR_HID2		920
651.1Sjmcneill#define  IBM750CL_SPR_HID2_WPE		0x40000000	/* Write pipe enable */
661.1Sjmcneill#define IBM750CL_SPR_WPAR		921
671.1Sjmcneill
681.1Sjmcneillstruct wiifb_mode {
691.1Sjmcneill	const char *		name;
701.1Sjmcneill	u_int			width;
711.1Sjmcneill	u_int			height;
721.1Sjmcneill	u_int			lines;
731.1Sjmcneill};
741.1Sjmcneill
751.1Sjmcneillstatic uint32_t wiifb_devcmap[16] = {
761.1Sjmcneill	0x00800080,	/* Black */
771.1Sjmcneill	0x1dff1d6b,	/* Blue */
781.1Sjmcneill	0x4b554b4a,	/* Green */
791.1Sjmcneill	0x80808080,	/* Cyan */
801.1Sjmcneill	0x4c544cff,	/* Red */
811.1Sjmcneill	0x3aaa34b5,	/* Magenta */
821.1Sjmcneill	0x7140718a,	/* Brown */
831.1Sjmcneill	0xff80ff80,	/* White */
841.1Sjmcneill	0x80808080,	/* Gray */
851.1Sjmcneill	0xc399c36a,	/* Bright Blue */
861.1Sjmcneill	0xd076d074,	/* Bright Green */
871.1Sjmcneill	0x80808080,	/* Bright Cyan */
881.1Sjmcneill	0x4c544cff,	/* Bright Red */
891.1Sjmcneill	0x3aaa34b5,	/* Bright Magenta */
901.1Sjmcneill	0xe100e194,	/* Bright Yellow */
911.1Sjmcneill	0xff80ff80	/* Bright White */
921.1Sjmcneill};
931.1Sjmcneill
941.1Sjmcneill#define WIIFB_MODE_INDEX(fmt, interlaced)	((fmt << 1) | interlaced)
951.1Sjmcneill
961.1Sjmcneillstatic const struct wiifb_mode wiifb_modes[] = {
971.1Sjmcneill	[WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 0)] = {
981.1Sjmcneill		.name = "NTSC 480p",
991.1Sjmcneill		.width = 640,
1001.1Sjmcneill		.height = 480,
1011.1Sjmcneill		.lines = 525,
1021.1Sjmcneill	},
1031.1Sjmcneill	[WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 1)] = {
1041.1Sjmcneill		.name = "NTSC 480i",
1051.1Sjmcneill		.width = 640,
1061.1Sjmcneill		.height = 480,
1071.1Sjmcneill		.lines = 525,
1081.1Sjmcneill	},
1091.1Sjmcneill	[WIIFB_MODE_INDEX(VI_DCR_FMT_PAL, 1)] = {
1101.1Sjmcneill		.name = "PAL 576i",
1111.1Sjmcneill		.width = 640,
1121.1Sjmcneill		.height = 574,
1131.1Sjmcneill		.lines = 625,
1141.1Sjmcneill	},
1151.1Sjmcneill
1161.1Sjmcneill};
1171.1Sjmcneill#define WIIFB_NMODES	__arraycount(wiifb_modes)
1181.1Sjmcneill
1191.1Sjmcneillstruct wiifb_dma {
1201.1Sjmcneill	bus_dmamap_t		dma_map;
1211.1Sjmcneill	bus_dma_tag_t		dma_tag;
1221.1Sjmcneill	bus_size_t		dma_size;
1231.1Sjmcneill	bus_dma_segment_t	dma_segs[1];
1241.1Sjmcneill	int			dma_nsegs;
1251.1Sjmcneill	void			*dma_addr;
1261.1Sjmcneill};
1271.1Sjmcneill
1281.1Sjmcneillstruct wiifb_softc {
1291.1Sjmcneill	struct genfb_softc	sc_gen;
1301.1Sjmcneill
1311.1Sjmcneill	bus_space_tag_t		sc_bst;
1321.1Sjmcneill	bus_space_handle_t	sc_bsh;
1331.1Sjmcneill	bus_dma_tag_t		sc_dmat;
1341.1Sjmcneill
1351.1Sjmcneill	void			*sc_bits;
1361.1Sjmcneill
1371.1Sjmcneill	uint8_t			sc_format;
1381.1Sjmcneill	bool			sc_interlaced;
1391.1Sjmcneill
1401.1Sjmcneill	const struct wiifb_mode	*sc_curmode;
1411.1Sjmcneill
1421.1Sjmcneill	u_int			sc_wsmode;
1431.1Sjmcneill
1441.1Sjmcneill	volatile uint32_t	*sc_efb;
1451.1Sjmcneill	volatile uint16_t	*sc_cp;
1461.1Sjmcneill	volatile uint16_t	*sc_pe;
1471.1Sjmcneill	volatile uint32_t	*sc_pi;
1481.1Sjmcneill	gx_wgpipe_t		*sc_wgpipe;
1491.1Sjmcneill
1501.1Sjmcneill	struct wiifb_dma	sc_rgb;
1511.1Sjmcneill	struct wiifb_dma	sc_fifo;
1521.1Sjmcneill
1531.1Sjmcneill	uint16_t		sc_token;
1541.1Sjmcneill};
1551.1Sjmcneill
1561.1Sjmcneill#define	RD2(sc, reg)		\
1571.1Sjmcneill	bus_space_read_2((sc)->sc_bst, (sc)->sc_bsh, (reg))
1581.1Sjmcneill#define	RD4(sc, reg)		\
1591.1Sjmcneill	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
1601.1Sjmcneill#define	WR2(sc, reg, val)	\
1611.1Sjmcneill	bus_space_write_2((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
1621.1Sjmcneill#define	WR4(sc, reg, val)	\
1631.1Sjmcneill	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
1641.1Sjmcneill
1651.1Sjmcneill#define CP_WRITE(sc, off, val)	(sc)->sc_cp[(off)] = (val)
1661.1Sjmcneill#define CP_READ(sc, off)	(sc)->sc_cp[(off)]
1671.1Sjmcneill
1681.1Sjmcneill#define PE_WRITE(sc, off, val)	(sc)->sc_pe[(off)] = (val)
1691.1Sjmcneill#define PE_READ(sc, off)	(sc)->sc_pe[(off)]
1701.1Sjmcneill
1711.1Sjmcneill#define PI_WRITE(sc, off, val)	(sc)->sc_pi[(off)] = (val)
1721.1Sjmcneill#define PI_READ(sc, off)	(sc)->sc_pi[(off)]
1731.1Sjmcneill
1741.1Sjmcneillstatic int	wiifb_match(device_t, cfdata_t, void *);
1751.1Sjmcneillstatic void	wiifb_attach(device_t, device_t, void *);
1761.1Sjmcneill
1771.1Sjmcneillstatic void	wiifb_accel_init(struct wiifb_softc *);
1781.1Sjmcneillstatic int	wiifb_vi_intr(void *);
1791.1Sjmcneillstatic void	wiifb_vi_refresh(void *);
1801.1Sjmcneill
1811.1Sjmcneillstatic void	wiifb_init(struct wiifb_softc *);
1821.1Sjmcneillstatic void	wiifb_set_mode(struct wiifb_softc *, uint8_t, bool);
1831.1Sjmcneillstatic void	wiifb_set_fb(struct wiifb_softc *);
1841.1Sjmcneillstatic void	wiifb_clear_xfb(struct wiifb_softc *);
1851.1Sjmcneill
1861.1Sjmcneillstatic int	wiifb_ioctl(void *, void *, u_long, void *, int, lwp_t *);
1871.1Sjmcneillstatic paddr_t	wiifb_mmap(void *, void *, off_t, int);
1881.1Sjmcneill
1891.1Sjmcneillstatic struct genfb_ops wiifb_ops = {
1901.1Sjmcneill	.genfb_ioctl = wiifb_ioctl,
1911.1Sjmcneill	.genfb_mmap = wiifb_mmap,
1921.1Sjmcneill};
1931.1Sjmcneill
1941.1SjmcneillCFATTACH_DECL_NEW(wiifb, sizeof(struct wiifb_softc),
1951.1Sjmcneill	wiifb_match, wiifb_attach, NULL, NULL);
1961.1Sjmcneill
1971.1Sjmcneillstatic int
1981.1Sjmcneillwiifb_match(device_t parent, cfdata_t cf, void *aux)
1991.1Sjmcneill{
2001.1Sjmcneill	struct mainbus_attach_args *maa = aux;
2011.1Sjmcneill
2021.1Sjmcneill	return !wiiu_native && strcmp(maa->maa_name, "genfb") == 0;
2031.1Sjmcneill}
2041.1Sjmcneill
2051.1Sjmcneillstatic void
2061.1Sjmcneillwiifb_attach(device_t parent, device_t self, void *aux)
2071.1Sjmcneill{
2081.1Sjmcneill	struct wiifb_softc *sc = device_private(self);
2091.1Sjmcneill	prop_dictionary_t dict = device_properties(self);
2101.1Sjmcneill	struct mainbus_attach_args *maa = aux;
2111.1Sjmcneill	int error;
2121.1Sjmcneill
2131.1Sjmcneill	sc->sc_gen.sc_dev = self;
2141.1Sjmcneill	sc->sc_bst = maa->maa_bst;
2151.1Sjmcneill	error = bus_space_map(sc->sc_bst, VI_BASE, VI_SIZE, 0, &sc->sc_bsh);
2161.1Sjmcneill	if (error != 0) {
2171.1Sjmcneill		panic("couldn't map registers");
2181.1Sjmcneill	}
2191.1Sjmcneill	sc->sc_bits = mapiodev(XFB_START, XFB_SIZE, true);
2201.1Sjmcneill	sc->sc_wsmode = WSDISPLAYIO_MODE_EMUL;
2211.1Sjmcneill	sc->sc_dmat = maa->maa_dmat;
2221.1Sjmcneill
2231.1Sjmcneill	wiifb_clear_xfb(sc);
2241.1Sjmcneill
2251.1Sjmcneill	wiifb_init(sc);
2261.1Sjmcneill	wiifb_set_mode(sc, sc->sc_format, sc->sc_interlaced);
2271.1Sjmcneill
2281.1Sjmcneill	prop_dictionary_set_uint32(dict, "width", sc->sc_curmode->width);
2291.1Sjmcneill	prop_dictionary_set_uint32(dict, "height",
2301.1Sjmcneill	    WIIFB_EFFECTIVE_HEIGHT(sc->sc_curmode->height));
2311.1Sjmcneill	prop_dictionary_set_uint8(dict, "depth", 16);
2321.1Sjmcneill	prop_dictionary_set_uint32(dict, "address", XFB_START);
2331.1Sjmcneill	prop_dictionary_set_uint32(dict, "virtual_address",
2341.1Sjmcneill	    WIIFB_EFFECTIVE_START(sc->sc_bits, sc->sc_curmode->width));
2351.1Sjmcneill	prop_dictionary_set_uint64(dict, "devcmap", (uintptr_t)wiifb_devcmap);
2361.1Sjmcneill
2371.1Sjmcneill	genfb_init(&sc->sc_gen);
2381.1Sjmcneill
2391.1Sjmcneill	aprint_naive("\n");
2401.1Sjmcneill	aprint_normal(": %s\n", sc->sc_curmode->name);
2411.1Sjmcneill
2421.1Sjmcneill	genfb_cnattach();
2431.1Sjmcneill	prop_dictionary_set_bool(dict, "is_console", true);
2441.1Sjmcneill	genfb_attach(&sc->sc_gen, &wiifb_ops);
2451.1Sjmcneill
2461.1Sjmcneill	wiifb_accel_init(sc);
2471.1Sjmcneill}
2481.1Sjmcneill
2491.1Sjmcneillstatic void
2501.1Sjmcneillwiifb_clear_xfb(struct wiifb_softc *sc)
2511.1Sjmcneill{
2521.1Sjmcneill	u_int offset;
2531.1Sjmcneill	uint32_t *p;
2541.1Sjmcneill
2551.1Sjmcneill	/*
2561.1Sjmcneill	 * Paint the entire XFB black. Use 4-byte accesses as the Wii will
2571.1Sjmcneill	 * ignore 1- and 2- byte writes to uncached memory.
2581.1Sjmcneill	 */
2591.1Sjmcneill	for (p = sc->sc_bits, offset = 0;
2601.1Sjmcneill	     offset < XFB_SIZE;
2611.1Sjmcneill	     offset += 4, p++) {
2621.1Sjmcneill		*p = wiifb_devcmap[0];
2631.1Sjmcneill	}
2641.1Sjmcneill}
2651.1Sjmcneill
2661.1Sjmcneillstatic int
2671.1Sjmcneillwiifb_vi_init(struct wiifb_softc *sc)
2681.1Sjmcneill{
2691.1Sjmcneill	device_t dev = sc->sc_gen.sc_dev;
2701.1Sjmcneill	void *ih;
2711.1Sjmcneill
2721.1Sjmcneill	WR4(sc, VI_DI0, VI_DI_ENB |
2731.1Sjmcneill			__SHIFTIN(1, VI_DI_VCT) |
2741.1Sjmcneill			__SHIFTIN(1, VI_DI_HCT));
2751.1Sjmcneill	WR4(sc, VI_DI1, 0);
2761.1Sjmcneill	WR4(sc, VI_DI2, 0);
2771.1Sjmcneill	WR4(sc, VI_DI3, 0);
2781.1Sjmcneill
2791.1Sjmcneill	ih = intr_establish_xname(PI_IRQ_VI, IST_LEVEL, IPL_TTY,
2801.1Sjmcneill	    wiifb_vi_intr, sc, device_xname(dev));
2811.1Sjmcneill	if (ih == NULL) {
2821.1Sjmcneill		aprint_error_dev(dev, "failed to install VI intr handler\n");
2831.1Sjmcneill		return EIO;
2841.1Sjmcneill	}
2851.1Sjmcneill
2861.1Sjmcneill	return 0;
2871.1Sjmcneill}
2881.1Sjmcneill
2891.1Sjmcneillstatic int
2901.1Sjmcneillwiifb_dma_alloc(struct wiifb_softc *sc, bus_size_t size, bus_size_t align,
2911.1Sjmcneill    int flags, struct wiifb_dma *dma)
2921.1Sjmcneill{
2931.1Sjmcneill	bus_dma_tag_t dmat = sc->sc_dmat; // &wii_mem1_bus_dma_tag;
2941.1Sjmcneill	int error;
2951.1Sjmcneill
2961.1Sjmcneill	error = bus_dmamem_alloc(dmat, size, align, 0,
2971.1Sjmcneill	    dma->dma_segs, 1, &dma->dma_nsegs, BUS_DMA_WAITOK);
2981.1Sjmcneill	if (error)
2991.1Sjmcneill		return error;
3001.1Sjmcneill
3011.1Sjmcneill	error = bus_dmamem_map(dmat, dma->dma_segs, dma->dma_nsegs,
3021.1Sjmcneill	    size, &dma->dma_addr, BUS_DMA_WAITOK | flags);
3031.1Sjmcneill	if (error)
3041.1Sjmcneill		goto free;
3051.1Sjmcneill
3061.1Sjmcneill	error = bus_dmamap_create(dmat, size, dma->dma_nsegs,
3071.1Sjmcneill	    size, 0, BUS_DMA_WAITOK, &dma->dma_map);
3081.1Sjmcneill	if (error)
3091.1Sjmcneill		goto unmap;
3101.1Sjmcneill
3111.1Sjmcneill	error = bus_dmamap_load(dmat, dma->dma_map, dma->dma_addr,
3121.1Sjmcneill	    size, NULL, BUS_DMA_WAITOK);
3131.1Sjmcneill	if (error)
3141.1Sjmcneill	    goto destroy;
3151.1Sjmcneill
3161.1Sjmcneill	dma->dma_size = size;
3171.1Sjmcneill	dma->dma_tag = dmat;
3181.1Sjmcneill
3191.1Sjmcneill	memset(dma->dma_addr, 0, dma->dma_size);
3201.1Sjmcneill
3211.1Sjmcneill	return 0;
3221.1Sjmcneill
3231.1Sjmcneilldestroy:
3241.1Sjmcneill	bus_dmamap_destroy(dmat, dma->dma_map);
3251.1Sjmcneillunmap:
3261.1Sjmcneill	bus_dmamem_unmap(dmat, dma->dma_addr, dma->dma_size);
3271.1Sjmcneillfree:
3281.1Sjmcneill	bus_dmamem_free(dmat, dma->dma_segs, dma->dma_nsegs);
3291.1Sjmcneill
3301.1Sjmcneill	return error;
3311.1Sjmcneill}
3321.1Sjmcneill
3331.1Sjmcneillstatic void *
3341.1Sjmcneillwiifb_mapreg(struct wiifb_softc *sc, bus_addr_t base, bus_size_t size)
3351.1Sjmcneill{
3361.1Sjmcneill	bus_space_handle_t bsh;
3371.1Sjmcneill	int error;
3381.1Sjmcneill
3391.1Sjmcneill	error = bus_space_map(sc->sc_bst, base, size,
3401.1Sjmcneill	    BUS_SPACE_MAP_LINEAR, &bsh);
3411.1Sjmcneill	if (error != 0) {
3421.1Sjmcneill		panic("couldn't map 0x%x", base);
3431.1Sjmcneill	}
3441.1Sjmcneill	return bus_space_vaddr(sc->sc_bst, bsh);
3451.1Sjmcneill}
3461.1Sjmcneill
3471.1Sjmcneillstatic void
3481.1Sjmcneillwiifb_set_wgpipe(bus_addr_t base)
3491.1Sjmcneill{
3501.1Sjmcneill	uint32_t value;
3511.1Sjmcneill
3521.1Sjmcneill	if (base) {
3531.1Sjmcneill		mtspr(IBM750CL_SPR_WPAR, base);
3541.1Sjmcneill	}
3551.1Sjmcneill	value = mfspr(IBM750CL_SPR_HID2);
3561.1Sjmcneill	if (base) {
3571.1Sjmcneill		value |= IBM750CL_SPR_HID2_WPE;
3581.1Sjmcneill	} else {
3591.1Sjmcneill		value &= ~IBM750CL_SPR_HID2_WPE;
3601.1Sjmcneill	}
3611.1Sjmcneill	mtspr(IBM750CL_SPR_HID2, value);
3621.1Sjmcneill}
3631.1Sjmcneill
3641.1Sjmcneillstatic void
3651.1Sjmcneillwiifb_ppcsync(void)
3661.1Sjmcneill{
3671.1Sjmcneill	uint32_t value;
3681.1Sjmcneill
3691.1Sjmcneill	value = mfspr(SPR_HID0);
3701.1Sjmcneill	mtspr(SPR_HID0, value | 0x8);
3711.1Sjmcneill	asm volatile("isync" ::: "memory");
3721.1Sjmcneill	asm volatile("sync" ::: "memory");
3731.1Sjmcneill	mtspr(SPR_HID0, value);
3741.1Sjmcneill}
3751.1Sjmcneill
3761.1Sjmcneillstatic void
3771.1Sjmcneillwiifb_gx_bp_load(struct wiifb_softc *sc, uint32_t data)
3781.1Sjmcneill{
3791.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x61);
3801.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data);
3811.1Sjmcneill}
3821.1Sjmcneill
3831.1Sjmcneillstatic void
3841.1Sjmcneillwiifb_gx_cp_load(struct wiifb_softc *sc, uint8_t addr, uint32_t data)
3851.1Sjmcneill{
3861.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x08);
3871.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u8 = addr);
3881.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data);
3891.1Sjmcneill}
3901.1Sjmcneill
3911.1Sjmcneillstatic void
3921.1Sjmcneillwiifb_gx_xf_load(struct wiifb_softc *sc, uint16_t addr, uint32_t data)
3931.1Sjmcneill{
3941.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x10);
3951.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = addr);
3961.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data);
3971.1Sjmcneill}
3981.1Sjmcneill
3991.1Sjmcneillstatic void
4001.1Sjmcneillwiifb_gx_xf_load_multi(struct wiifb_softc *sc, uint16_t addr,
4011.1Sjmcneill    uint16_t count, uint32_t *data)
4021.1Sjmcneill{
4031.1Sjmcneill	uint16_t n;
4041.1Sjmcneill
4051.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x10);
4061.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u32 =
4071.1Sjmcneill			(((uint32_t)count - 1) << 16) | addr);
4081.1Sjmcneill	for (n = 0; n < count; n++) {
4091.1Sjmcneill		GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data[n]);
4101.1Sjmcneill	}
4111.1Sjmcneill}
4121.1Sjmcneill
4131.1Sjmcneillstatic void
4141.1Sjmcneillwiifb_gx_set_viewport(struct wiifb_softc *sc, u_int w, u_int h)
4151.1Sjmcneill{
4161.1Sjmcneill	uint32_t data[6];
4171.1Sjmcneill
4181.1Sjmcneill	KASSERT(w == 640);
4191.1Sjmcneill	KASSERT(h == 480);
4201.1Sjmcneill
4211.1Sjmcneill	data[0] = 0x00000140;
4221.1Sjmcneill	data[1] = 0xffffff10;
4231.1Sjmcneill	data[2] = 0x00ffffff;
4241.1Sjmcneill	data[3] = 0x00000296;
4251.1Sjmcneill	data[4] = 0x00000246;
4261.1Sjmcneill	data[5] = 0x00ffffff;
4271.1Sjmcneill
4281.1Sjmcneill	wiifb_gx_xf_load_multi(sc, GX_XF_VIEWPORT_X0,
4291.1Sjmcneill	    __arraycount(data), data);
4301.1Sjmcneill}
4311.1Sjmcneill
4321.1Sjmcneillstatic void
4331.1Sjmcneillwiifb_gx_set_scissor(struct wiifb_softc *sc, u_int x, u_int y,
4341.1Sjmcneill    u_int w, u_int h)
4351.1Sjmcneill{
4361.1Sjmcneill	uint32_t xo = x + 0x156;
4371.1Sjmcneill	uint32_t yo = y + 0x156;
4381.1Sjmcneill	uint32_t wo = xo + w - 1;
4391.1Sjmcneill	uint32_t ho = yo + h - 1;
4401.1Sjmcneill
4411.1Sjmcneill	wiifb_gx_bp_load(sc, 0x20000000 | yo | (xo << 12));
4421.1Sjmcneill	wiifb_gx_bp_load(sc, 0x21000000 | ho | (wo << 12));
4431.1Sjmcneill	wiifb_gx_bp_load(sc, 0x59000000 | GX_XY(xo >> 1, yo >> 1));
4441.1Sjmcneill}
4451.1Sjmcneill
4461.1Sjmcneillstatic void
4471.1Sjmcneillwiifb_gx_init(struct wiifb_softc *sc)
4481.1Sjmcneill{
4491.1Sjmcneill	const uint32_t fifo_start = sc->sc_fifo.dma_segs[0].ds_addr;
4501.1Sjmcneill	const uint32_t fifo_end = fifo_start + sc->sc_fifo.dma_size - 4;
4511.1Sjmcneill	const uint32_t fifo_hiwat = GX_FIFO_HIWAT(sc->sc_fifo.dma_size);
4521.1Sjmcneill	const uint32_t fifo_lowat = GX_FIFO_LOWAT(sc->sc_fifo.dma_size);
4531.1Sjmcneill
4541.1Sjmcneill	/* Disable WGPIPE and unlink CP FIFO before changing settings. */
4551.1Sjmcneill	wiifb_set_wgpipe(0);
4561.1Sjmcneill	CP_WRITE(sc, CP_CR, 0);
4571.1Sjmcneill	CP_WRITE(sc, CP_CLEAR, CP_CLEAR_UNDERFLOW | CP_CLEAR_OVERFLOW);
4581.1Sjmcneill
4591.1Sjmcneill	/* Setup GP FIFO */
4601.1Sjmcneill	CP_WRITE(sc, CP_FIFO_BASE_LO, LOWER_16_BITS(fifo_start));
4611.1Sjmcneill	CP_WRITE(sc, CP_FIFO_BASE_HI, UPPER_16_BITS(fifo_start));
4621.1Sjmcneill	CP_WRITE(sc, CP_FIFO_END_LO, LOWER_16_BITS(fifo_end));
4631.1Sjmcneill	CP_WRITE(sc, CP_FIFO_END_HI, UPPER_16_BITS(fifo_end));
4641.1Sjmcneill	CP_WRITE(sc, CP_FIFO_HIWAT_LO, LOWER_16_BITS(fifo_hiwat));
4651.1Sjmcneill	CP_WRITE(sc, CP_FIFO_HIWAT_HI, UPPER_16_BITS(fifo_hiwat));
4661.1Sjmcneill	CP_WRITE(sc, CP_FIFO_LOWAT_LO, LOWER_16_BITS(fifo_lowat));
4671.1Sjmcneill	CP_WRITE(sc, CP_FIFO_LOWAT_HI, UPPER_16_BITS(fifo_lowat));
4681.1Sjmcneill	CP_WRITE(sc, CP_FIFO_RW_DIST_LO, 0);
4691.1Sjmcneill	CP_WRITE(sc, CP_FIFO_RW_DIST_HI, 0);
4701.1Sjmcneill	CP_WRITE(sc, CP_FIFO_WRITE_PTR_LO, LOWER_16_BITS(fifo_start));
4711.1Sjmcneill	CP_WRITE(sc, CP_FIFO_WRITE_PTR_HI, UPPER_16_BITS(fifo_start));
4721.1Sjmcneill	CP_WRITE(sc, CP_FIFO_READ_PTR_LO, LOWER_16_BITS(fifo_start));
4731.1Sjmcneill	CP_WRITE(sc, CP_FIFO_READ_PTR_HI, UPPER_16_BITS(fifo_start));
4741.1Sjmcneill	wiifb_ppcsync();
4751.1Sjmcneill
4761.1Sjmcneill	/* Setup CPU FIFO */
4771.1Sjmcneill	PI_WRITE(sc, PI_FIFO_BASE_START, fifo_start);
4781.1Sjmcneill	PI_WRITE(sc, PI_FIFO_BASE_END, fifo_end);
4791.1Sjmcneill	PI_WRITE(sc, PI_FIFO_WRITE_PTR, fifo_start);
4801.1Sjmcneill	wiifb_ppcsync();
4811.1Sjmcneill
4821.1Sjmcneill	/* Link CP/PE FIFO and enable GP FIFO */
4831.1Sjmcneill	CP_WRITE(sc, CP_CR, CP_CR_GP_LINK_ENABLE);
4841.1Sjmcneill	CP_WRITE(sc, CP_CR, CP_READ(sc, CP_CR) | CP_CR_READ_ENABLE);
4851.1Sjmcneill
4861.1Sjmcneill	/* Init pixel engine */
4871.1Sjmcneill	PE_WRITE(sc, PE_ZCONF,
4881.1Sjmcneill	    PE_ZCONF_UPD_ENABLE |
4891.1Sjmcneill	    PE_ZCONF_FUNC_ALWAYS |
4901.1Sjmcneill	    PE_ZCONF_COMP_ENABLE);
4911.1Sjmcneill	PE_WRITE(sc, PE_ALPHA_CONF,
4921.1Sjmcneill	    PE_ALPHA_CONF_OP_SET |
4931.1Sjmcneill	    PE_ALPHA_CONF_SRC_1 |
4941.1Sjmcneill	    PE_ALPHA_CONF_DST_0 |
4951.1Sjmcneill	    PE_ALPHA_CONF_UPD_A |
4961.1Sjmcneill	    PE_ALPHA_CONF_UPD_C);
4971.1Sjmcneill	PE_WRITE(sc, PE_ALPHA_DEST, 0);
4981.1Sjmcneill	PE_WRITE(sc, PE_ALPHA_MODE, PE_ALPHA_MODE_ALWAYS);
4991.1Sjmcneill	PE_WRITE(sc, PE_ALPHA_READ,
5001.1Sjmcneill	    PE_ALPHA_READ_UNK | PE_ALPHA_READ_FF);
5011.1Sjmcneill
5021.1Sjmcneill	/* Enable WG pipe */
5031.1Sjmcneill	wiifb_set_wgpipe(WGPIPE_BASE);
5041.1Sjmcneill
5051.1Sjmcneill	/* Sanitize command processor registers */
5061.1Sjmcneill	for (int n = 0; n < 8; n++) {
5071.1Sjmcneill		wiifb_gx_cp_load(sc, 0x80 + n, 0x80000000);
5081.1Sjmcneill	}
5091.1Sjmcneill        wiifb_gx_cp_load(sc, 0x20, 0);
5101.1Sjmcneill
5111.1Sjmcneill	/* Sanitize transform unit registers */
5121.1Sjmcneill        wiifb_gx_xf_load(sc, 0x1000, 0x3f);
5131.1Sjmcneill        wiifb_gx_xf_load(sc, 0x1005, 0x01);
5141.1Sjmcneill        wiifb_gx_xf_load(sc, 0x1012, 0x01);
5151.1Sjmcneill        wiifb_gx_xf_load(sc, 0x1006, 0);
5161.1Sjmcneill
5171.1Sjmcneill	/* Initialize blitting processor */
5181.1Sjmcneill        wiifb_gx_bp_load(sc, 0x00000001);
5191.1Sjmcneill        wiifb_gx_bp_load(sc, 0x01666666);
5201.1Sjmcneill        wiifb_gx_bp_load(sc, 0x02666666);
5211.1Sjmcneill        wiifb_gx_bp_load(sc, 0x03666666);
5221.1Sjmcneill        wiifb_gx_bp_load(sc, 0x04666666);
5231.1Sjmcneill        wiifb_gx_bp_load(sc, 0x22000606);
5241.1Sjmcneill        wiifb_gx_bp_load(sc, 0x23000000);
5251.1Sjmcneill        wiifb_gx_bp_load(sc, 0x24000000);
5261.1Sjmcneill        wiifb_gx_bp_load(sc, 0x42000000);
5271.1Sjmcneill        wiifb_gx_bp_load(sc, 0x44000003);
5281.1Sjmcneill        wiifb_gx_bp_load(sc, 0x43000000);
5291.1Sjmcneill        wiifb_gx_bp_load(sc, 0x53595000);
5301.1Sjmcneill        wiifb_gx_bp_load(sc, 0x54000015);
5311.1Sjmcneill        wiifb_gx_bp_load(sc, 0x550003ff);
5321.1Sjmcneill        wiifb_gx_bp_load(sc, 0x560003ff);
5331.1Sjmcneill        wiifb_gx_bp_load(sc, 0x5800000f);
5341.1Sjmcneill        wiifb_gx_bp_load(sc, 0x67000000);
5351.1Sjmcneill
5361.1Sjmcneill	/* Set viewport and scissor parameters */
5371.1Sjmcneill	wiifb_gx_set_viewport(sc, WIIFB_RGB_WIDTH, WIIFB_RGB_HEIGHT);
5381.1Sjmcneill	wiifb_gx_set_scissor(sc, 0, 0, WIIFB_RGB_WIDTH, WIIFB_RGB_HEIGHT);
5391.1Sjmcneill
5401.1Sjmcneill	wiifb_gx_bp_load(sc, 0x4000001f);
5411.1Sjmcneill
5421.1Sjmcneill	if (sc->sc_curmode->height == 574) {
5431.1Sjmcneill		/* Scale 480 lines to PAL display height */
5441.1Sjmcneill		wiifb_gx_bp_load(sc, 0x4e000127);
5451.1Sjmcneill	}
5461.1Sjmcneill
5471.1Sjmcneill	/* Copy mode parameters */
5481.1Sjmcneill	wiifb_gx_bp_load(sc, 0x410004bc);
5491.1Sjmcneill
5501.1Sjmcneill	/* Copy source */
5511.1Sjmcneill	wiifb_gx_bp_load(sc, 0x49000000 | GX_XY(0, 0));
5521.1Sjmcneill	wiifb_gx_bp_load(sc, 0x4a000000 |
5531.1Sjmcneill	    GX_XY(WIIFB_RGB_WIDTH - 1, WIIFB_RGB_HEIGHT - 1));
5541.1Sjmcneill
5551.1Sjmcneill	/* Copy destination */
5561.1Sjmcneill	wiifb_gx_bp_load(sc, 0x4d000000 | (WIIFB_RGB_WIDTH >> 4));
5571.1Sjmcneill
5581.1Sjmcneill	/* XFB address */
5591.1Sjmcneill	wiifb_gx_bp_load(sc, 0x4b000000 | (XFB_START >> 5));
5601.1Sjmcneill
5611.1Sjmcneill	/* Copy clear settings */
5621.1Sjmcneill	wiifb_gx_bp_load(sc, 0x4f000000);
5631.1Sjmcneill	wiifb_gx_bp_load(sc, 0x50000000);
5641.1Sjmcneill	wiifb_gx_bp_load(sc, 0x5100ffff);
5651.1Sjmcneill}
5661.1Sjmcneill
5671.1Sjmcneillstatic void
5681.1Sjmcneillwiifb_gx_flush(struct wiifb_softc *sc)
5691.1Sjmcneill{
5701.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
5711.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
5721.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
5731.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
5741.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
5751.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
5761.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
5771.1Sjmcneill	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
5781.1Sjmcneill	wiifb_ppcsync();
5791.1Sjmcneill}
5801.1Sjmcneill
5811.1Sjmcneillstatic void
5821.1Sjmcneillwiifb_accel_init(struct wiifb_softc *sc)
5831.1Sjmcneill{
5841.1Sjmcneill	bus_size_t rgb_size;
5851.1Sjmcneill	bus_size_t fifo_size;
5861.1Sjmcneill
5871.1Sjmcneill	if (wiifb_vi_init(sc) != 0) {
5881.1Sjmcneill		panic("couldn't init VI");
5891.1Sjmcneill	}
5901.1Sjmcneill
5911.1Sjmcneill	rgb_size = WIIFB_RGB_WIDTH * WIIFB_RGB_HEIGHT;
5921.1Sjmcneill	rgb_size = rgb_size * WIIFB_RGB_BPP / NBBY;
5931.1Sjmcneill	rgb_size = roundup(rgb_size, PAGE_SIZE);
5941.1Sjmcneill	if (wiifb_dma_alloc(sc, rgb_size, PAGE_SIZE, BUS_DMA_PREFETCHABLE,
5951.1Sjmcneill			    &sc->sc_rgb) != 0) {
5961.1Sjmcneill		panic("couldn't alloc rgb fb");
5971.1Sjmcneill	}
5981.1Sjmcneill
5991.1Sjmcneill	fifo_size = WIIFB_FIFO_SIZE;
6001.1Sjmcneill	if (wiifb_dma_alloc(sc, fifo_size, GX_FIFO_ALIGN, BUS_DMA_NOCACHE,
6011.1Sjmcneill	    		    &sc->sc_fifo) != 0) {
6021.1Sjmcneill		panic("couldn't alloc gx fifo");
6031.1Sjmcneill	}
6041.1Sjmcneill
6051.1Sjmcneill	sc->sc_efb = wiifb_mapreg(sc, EFB_BASE, EFB_SIZE);
6061.1Sjmcneill	sc->sc_cp = wiifb_mapreg(sc, CP_BASE, CP_SIZE);
6071.1Sjmcneill	sc->sc_pe = wiifb_mapreg(sc, PE_BASE, PE_SIZE);
6081.1Sjmcneill	sc->sc_pi = wiifb_mapreg(sc, PI_BASE, PI_SIZE);
6091.1Sjmcneill	sc->sc_wgpipe = wiifb_mapreg(sc, WGPIPE_BASE, WGPIPE_SIZE);
6101.1Sjmcneill
6111.1Sjmcneill	wiifb_gx_init(sc);
6121.1Sjmcneill};
6131.1Sjmcneill
6141.1Sjmcneillstatic void
6151.1Sjmcneillwiifb_rgb_to_efb(struct wiifb_softc *sc)
6161.1Sjmcneill{
6171.1Sjmcneill	u_int y;
6181.1Sjmcneill	uint32_t *src = sc->sc_rgb.dma_addr;
6191.1Sjmcneill	uint32_t *dst = __UNVOLATILE(sc->sc_efb);
6201.1Sjmcneill	register_t hid0 = mfspr(SPR_HID0);
6211.1Sjmcneill
6221.1Sjmcneill	KASSERT(src != NULL);
6231.1Sjmcneill	KASSERT(dst != NULL);
6241.1Sjmcneill
6251.1Sjmcneill	/* Disable store gathering while writing to EFB. */
6261.1Sjmcneill	mtspr(SPR_HID0, hid0 & ~HID0_SGE);
6271.1Sjmcneill
6281.1Sjmcneill	for (y = 0; y < WIIFB_RGB_HEIGHT; y++) {
6291.1Sjmcneill		memcpy(dst, src, WIIFB_RGB_WIDTH * 4);
6301.1Sjmcneill		src += WIIFB_RGB_WIDTH;
6311.1Sjmcneill		dst += 1024;
6321.1Sjmcneill	}
6331.1Sjmcneill
6341.1Sjmcneill	/* Re-enable store gathering. */
6351.1Sjmcneill	mtspr(SPR_HID0, hid0);
6361.1Sjmcneill}
6371.1Sjmcneill
6381.1Sjmcneillstatic void
6391.1Sjmcneillwiifb_efb_to_xfb(struct wiifb_softc *sc)
6401.1Sjmcneill{
6411.1Sjmcneill	const uint32_t copy_mask = sc->sc_curmode->height == 574 ? 0x400 : 0x0;
6421.1Sjmcneill
6431.1Sjmcneill	/* Execute copy to XFB */
6441.1Sjmcneill	wiifb_gx_bp_load(sc, 0x52004803 | copy_mask);
6451.1Sjmcneill}
6461.1Sjmcneill
6471.1Sjmcneillstatic void
6481.1Sjmcneillwiifb_gx_draw_done(struct wiifb_softc *sc, uint16_t token)
6491.1Sjmcneill{
6501.1Sjmcneill	/* Draw done */
6511.1Sjmcneill	wiifb_gx_bp_load(sc, 0x45000002);
6521.1Sjmcneill	/* Write tokens */
6531.1Sjmcneill        wiifb_gx_bp_load(sc, 0x48000000 | token);
6541.1Sjmcneill        wiifb_gx_bp_load(sc, 0x47000000 | token);
6551.1Sjmcneill	/* Flush WG pipe */
6561.1Sjmcneill	wiifb_gx_flush(sc);
6571.1Sjmcneill}
6581.1Sjmcneill
6591.1Sjmcneillstatic void
6601.1Sjmcneillwiifb_vi_refresh(void *priv)
6611.1Sjmcneill{
6621.1Sjmcneill	struct wiifb_softc *sc = priv;
6631.1Sjmcneill
6641.1Sjmcneill	wiifb_rgb_to_efb(sc);
6651.1Sjmcneill	wiifb_efb_to_xfb(sc);
6661.1Sjmcneill	wiifb_gx_draw_done(sc, sc->sc_token++);
6671.1Sjmcneill}
6681.1Sjmcneill
6691.1Sjmcneillstatic int
6701.1Sjmcneillwiifb_vi_intr(void *priv)
6711.1Sjmcneill{
6721.1Sjmcneill	struct wiifb_softc *sc = priv;
6731.1Sjmcneill	uint32_t di0;
6741.1Sjmcneill	int ret = 0;
6751.1Sjmcneill
6761.1Sjmcneill	di0 = RD4(sc, VI_DI0);
6771.1Sjmcneill
6781.1Sjmcneill	WR4(sc, VI_DI0, RD4(sc, VI_DI0) & ~VI_DI_INT);
6791.1Sjmcneill	WR4(sc, VI_DI1, RD4(sc, VI_DI1) & ~VI_DI_INT);
6801.1Sjmcneill	WR4(sc, VI_DI2, RD4(sc, VI_DI2) & ~VI_DI_INT);
6811.1Sjmcneill	WR4(sc, VI_DI3, RD4(sc, VI_DI3) & ~VI_DI_INT);
6821.1Sjmcneill
6831.1Sjmcneill	if ((di0 & VI_DI_INT) != 0 &&
6841.1Sjmcneill	    sc->sc_wsmode != WSDISPLAYIO_MODE_EMUL) {
6851.1Sjmcneill		wiifb_vi_refresh(sc);
6861.1Sjmcneill		ret = 1;
6871.1Sjmcneill	}
6881.1Sjmcneill
6891.1Sjmcneill	return ret;
6901.1Sjmcneill}
6911.1Sjmcneill
6921.1Sjmcneillstatic void
6931.1Sjmcneillwiifb_init(struct wiifb_softc *sc)
6941.1Sjmcneill{
6951.1Sjmcneill	uint16_t dcr;
6961.1Sjmcneill	uint16_t visel;
6971.1Sjmcneill
6981.1Sjmcneill	/* Read current display format and interlaced settings. */
6991.1Sjmcneill	dcr = RD2(sc, VI_DCR);
7001.1Sjmcneill	if ((dcr & VI_DCR_ENB) != 0) {
7011.1Sjmcneill		sc->sc_format = __SHIFTOUT(dcr, VI_DCR_FMT);
7021.1Sjmcneill		sc->sc_interlaced = (dcr & VI_DCR_NIN) == 0;
7031.1Sjmcneill	} else {
7041.1Sjmcneill		visel = RD2(sc, VI_VISEL);
7051.1Sjmcneill		sc->sc_format = VI_DCR_FMT_NTSC;
7061.1Sjmcneill		sc->sc_interlaced = (visel & VI_VISEL_COMPONENT_CABLE) == 0;
7071.1Sjmcneill	}
7081.1Sjmcneill
7091.1Sjmcneill	/* Reset video interface. */
7101.1Sjmcneill	WR2(sc, VI_DCR, VI_DCR_RST);
7111.1Sjmcneill	delay(1000);
7121.1Sjmcneill
7131.1Sjmcneill	/* Initialize video format and interlace selector. */
7141.1Sjmcneill	dcr = __SHIFTIN(sc->sc_format, VI_DCR_FMT) |
7151.1Sjmcneill	      (sc->sc_interlaced ? 0 : VI_DCR_NIN);
7161.1Sjmcneill	WR2(sc, VI_DCR, dcr);
7171.1Sjmcneill}
7181.1Sjmcneill
7191.1Sjmcneillstatic void
7201.1Sjmcneillwiifb_set_mode(struct wiifb_softc *sc, uint8_t format, bool interlaced)
7211.1Sjmcneill{
7221.1Sjmcneill	u_int modeidx;
7231.1Sjmcneill	u_int strides, reads;
7241.1Sjmcneill
7251.1Sjmcneill	modeidx = WIIFB_MODE_INDEX(format, interlaced);
7261.1Sjmcneill	if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 1)) {
7271.1Sjmcneill		/* NTSC 480i Magic numbers from YAGCD. */
7281.1Sjmcneill		WR2(sc, VI_VTR, 0x0f06);
7291.1Sjmcneill		WR4(sc, VI_HTR0, 0x476901AD);
7301.1Sjmcneill		WR4(sc, VI_HTR1, 0x02EA5140);
7311.1Sjmcneill		WR4(sc, VI_VTO, 0x00030018);
7321.1Sjmcneill		WR4(sc, VI_VTE, 0x00020019);
7331.1Sjmcneill		WR4(sc, VI_BBOI, 0x410C410C);
7341.1Sjmcneill		WR4(sc, VI_BBEI, 0x40ED40ED);
7351.1Sjmcneill		WR2(sc, VI_DPV, 0x0000);
7361.1Sjmcneill		WR2(sc, VI_DPH, 0x0000);
7371.1Sjmcneill	} else if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 0)) {
7381.1Sjmcneill		/* NTSC 480p */
7391.1Sjmcneill		WR2(sc, VI_VTR, 0x1e0c);
7401.1Sjmcneill		WR4(sc, VI_HTR0, 0x476901ad);
7411.1Sjmcneill		WR4(sc, VI_HTR1, 0x030a4940);
7421.1Sjmcneill		WR4(sc, VI_VTO, 0x00060030);
7431.1Sjmcneill		WR4(sc, VI_VTE, 0x00060030);
7441.1Sjmcneill		WR4(sc, VI_BBOI, 0x81d881d8);
7451.1Sjmcneill		WR4(sc, VI_BBEI, 0x81d881d8);
7461.1Sjmcneill		WR2(sc, VI_DPV, 0x0000);
7471.1Sjmcneill		WR2(sc, VI_DPH, 0x0000);
7481.1Sjmcneill	} else if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_PAL, 1)) {
7491.1Sjmcneill		/* PAL 576i */
7501.1Sjmcneill		WR2(sc, VI_VTR, 0x11f5);
7511.1Sjmcneill		WR4(sc, VI_HTR0, 0x4b6a01b0);
7521.1Sjmcneill		WR4(sc, VI_HTR1, 0x02f85640);
7531.1Sjmcneill		WR4(sc, VI_VTO, 0x00010023);
7541.1Sjmcneill		WR4(sc, VI_VTE, 0x00000024);
7551.1Sjmcneill		WR4(sc, VI_BBOI, 0x4d2b4d6d);
7561.1Sjmcneill		WR4(sc, VI_BBEI, 0x4d8a4d4c);
7571.1Sjmcneill		WR2(sc, VI_DPV, 0x013c);
7581.1Sjmcneill		WR2(sc, VI_DPH, 0x0144);
7591.1Sjmcneill	} else {
7601.1Sjmcneill		/*
7611.1Sjmcneill		 * Display mode is not supported. Blink the slot LED to
7621.1Sjmcneill		 * indicate failure.
7631.1Sjmcneill		 */
7641.1Sjmcneill		wii_slot_led_blink(WIIFB_ERROR_BLINK_INTERVAL);
7651.1Sjmcneill	}
7661.1Sjmcneill
7671.1Sjmcneill	if (modeidx >= WIIFB_NMODES || wiifb_modes[modeidx].name == NULL) {
7681.1Sjmcneill		panic("Unsupported format (0x%x) / interlaced (%d) settings",
7691.1Sjmcneill		    sc->sc_format, sc->sc_interlaced);
7701.1Sjmcneill	}
7711.1Sjmcneill	sc->sc_curmode = &wiifb_modes[modeidx];
7721.1Sjmcneill
7731.1Sjmcneill	/* Filter coefficient table, values from YAGCD. */
7741.1Sjmcneill	WR4(sc, VI_FCT0, 0x1ae771f0);
7751.1Sjmcneill	WR4(sc, VI_FCT1, 0x0db4a574);
7761.1Sjmcneill	WR4(sc, VI_FCT2, 0x00c1188e);
7771.1Sjmcneill	WR4(sc, VI_FCT3, 0xc4c0cbe2);
7781.1Sjmcneill	WR4(sc, VI_FCT4, 0xfcecdecf);
7791.1Sjmcneill	WR4(sc, VI_FCT5, 0x13130f08);
7801.1Sjmcneill	WR4(sc, VI_FCT6, 0x00080C0f);
7811.1Sjmcneill
7821.1Sjmcneill	/* Unknown registers. */
7831.1Sjmcneill	WR4(sc, VI_UNKNOWN_68H, 0x00ff0000);
7841.1Sjmcneill	WR2(sc, VI_UNKNOWN_76H, 0x00ff);
7851.1Sjmcneill	WR4(sc, VI_UNKNOWN_78H, 0x00ff00ff);
7861.1Sjmcneill	WR4(sc, VI_UNKNOWN_7CH, 0x00ff00ff);
7871.1Sjmcneill
7881.1Sjmcneill	/* Picture configuration */
7891.1Sjmcneill	strides = (sc->sc_curmode->width * 2) / (interlaced ? 16 : 32);
7901.1Sjmcneill	reads = (sc->sc_curmode->width * 2) / 32;
7911.1Sjmcneill	WR2(sc, VI_PICCONF,
7921.1Sjmcneill	    __SHIFTIN(strides, VI_PICCONF_STRIDES) |
7931.1Sjmcneill	    __SHIFTIN(reads, VI_PICCONF_READS));
7941.1Sjmcneill
7951.1Sjmcneill	/* Horizontal scaler configuration */
7961.1Sjmcneill	if (interlaced) {
7971.1Sjmcneill		WR2(sc, VI_HSR, __SHIFTIN(256, VI_HSR_STP));
7981.1Sjmcneill	} else {
7991.1Sjmcneill		WR2(sc, VI_HSR, __SHIFTIN(244, VI_HSR_STP) | VI_HSR_HS_EN);
8001.1Sjmcneill	}
8011.1Sjmcneill
8021.1Sjmcneill	/* Video clock configuration */
8031.1Sjmcneill	WR2(sc, VI_VICLK,
8041.1Sjmcneill	    interlaced ? VI_VICLK_SEL_27MHZ : VI_VICLK_SEL_54MHZ);
8051.1Sjmcneill
8061.1Sjmcneill	/* Horizontal scaling width */
8071.1Sjmcneill	WR2(sc, VI_HSCALINGW, sc->sc_curmode->width);
8081.1Sjmcneill
8091.1Sjmcneill	/* Set framebuffer address */
8101.1Sjmcneill	wiifb_set_fb(sc);
8111.1Sjmcneill
8121.1Sjmcneill	/* Finally, enable the framebuffer */
8131.1Sjmcneill	WR2(sc, VI_DCR, RD2(sc, VI_DCR) | VI_DCR_ENB);
8141.1Sjmcneill}
8151.1Sjmcneill
8161.1Sjmcneillstatic void
8171.1Sjmcneillwiifb_set_fb(struct wiifb_softc *sc)
8181.1Sjmcneill{
8191.1Sjmcneill	uint32_t taddr = XFB_START;
8201.1Sjmcneill	uint32_t baddr = taddr + (sc->sc_interlaced ?
8211.1Sjmcneill				  sc->sc_curmode->width * 2 : 0);
8221.1Sjmcneill
8231.1Sjmcneill	WR4(sc, VI_TFBL,
8241.1Sjmcneill	    VI_TFBL_PGOFF |
8251.1Sjmcneill	    __SHIFTIN((taddr >> 5), VI_TFBL_FBB) |
8261.1Sjmcneill	    __SHIFTIN((taddr / 2) & 0xf, VI_TFBL_XOF));
8271.1Sjmcneill	WR4(sc, VI_TFBR, 0);
8281.1Sjmcneill
8291.1Sjmcneill	WR4(sc, VI_BFBL,
8301.1Sjmcneill	    VI_BFBL_PGOFF |
8311.1Sjmcneill	    __SHIFTIN((baddr >> 5), VI_BFBL_FBB) |
8321.1Sjmcneill	    __SHIFTIN((baddr / 2) & 0xf, VI_BFBL_XOF));
8331.1Sjmcneill	WR4(sc, VI_BFBR, 0);
8341.1Sjmcneill}
8351.1Sjmcneill
8361.1Sjmcneillstatic int
8371.1Sjmcneillwiifb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l)
8381.1Sjmcneill{
8391.1Sjmcneill	struct wiifb_softc *sc = v;
8401.1Sjmcneill	struct wsdisplayio_bus_id *busid;
8411.1Sjmcneill	struct wsdisplayio_fbinfo *fbi;
8421.1Sjmcneill	u_int video;
8431.1Sjmcneill	u_int wsmode;
8441.1Sjmcneill
8451.1Sjmcneill	switch (cmd) {
8461.1Sjmcneill	case WSDISPLAYIO_GTYPE:
8471.1Sjmcneill		*(u_int *)data = WSDISPLAY_TYPE_GENFB;
8481.1Sjmcneill		return 0;
8491.1Sjmcneill	case WSDISPLAYIO_GET_BUSID:
8501.1Sjmcneill		busid = data;
8511.1Sjmcneill		busid->bus_type = WSDISPLAYIO_BUS_SOC;
8521.1Sjmcneill		return 0;
8531.1Sjmcneill	case WSDISPLAYIO_GET_FBINFO:
8541.1Sjmcneill		fbi = data;
8551.1Sjmcneill		/*
8561.1Sjmcneill		 * rasops info does not match the pixel encoding due to our
8571.1Sjmcneill		 * devcmap, so fill out fbinfo manually instead of relying
8581.1Sjmcneill		 * on wsdisplayio_get_fbinfo.
8591.1Sjmcneill		 */
8601.1Sjmcneill		fbi->fbi_fboffset = 0;
8611.1Sjmcneill		fbi->fbi_width = WIIFB_RGB_WIDTH;
8621.1Sjmcneill		fbi->fbi_height = WIIFB_RGB_HEIGHT;
8631.1Sjmcneill		fbi->fbi_bitsperpixel = WIIFB_RGB_BPP;
8641.1Sjmcneill		fbi->fbi_stride = fbi->fbi_width * fbi->fbi_bitsperpixel / 8;
8651.1Sjmcneill		fbi->fbi_fbsize = fbi->fbi_height * fbi->fbi_stride;
8661.1Sjmcneill		fbi->fbi_pixeltype = WSFB_RGB;
8671.1Sjmcneill		fbi->fbi_flags = WSFB_VRAM_IS_RAM;
8681.1Sjmcneill		return 0;
8691.1Sjmcneill
8701.1Sjmcneill	case WSDISPLAYIO_SVIDEO:
8711.1Sjmcneill		video = *(u_int *)data;
8721.1Sjmcneill		switch (video) {
8731.1Sjmcneill		case WSDISPLAYIO_VIDEO_OFF:
8741.1Sjmcneill			out32(HW_VIDIM, __SHIFTIN(7, VIDIM_Y) |
8751.1Sjmcneill					__SHIFTIN(7, VIDIM_C) |
8761.1Sjmcneill					VIDIM_E);
8771.1Sjmcneill			return 0;
8781.1Sjmcneill		case WSDISPLAYIO_VIDEO_ON:
8791.1Sjmcneill			out32(HW_VIDIM, 0);
8801.1Sjmcneill			return 0;
8811.1Sjmcneill		default:
8821.1Sjmcneill			return EINVAL;
8831.1Sjmcneill		}
8841.1Sjmcneill
8851.1Sjmcneill	case WSDISPLAYIO_SMODE:
8861.1Sjmcneill		wsmode = *(u_int *)data;
8871.1Sjmcneill		if (wsmode != WSDISPLAYIO_MODE_EMUL) {
8881.1Sjmcneill			/* Blank the RGB FB when leaving text mode */
8891.1Sjmcneill			memset(sc->sc_rgb.dma_addr, 0, sc->sc_rgb.dma_size);
8901.1Sjmcneill		}
8911.1Sjmcneill		if (sc->sc_wsmode != wsmode) {
8921.1Sjmcneill			sc->sc_wsmode = wsmode;
8931.1Sjmcneill
8941.1Sjmcneill			if (wsmode == WSDISPLAYIO_MODE_EMUL) {
8951.1Sjmcneill				wiifb_clear_xfb(sc);
8961.1Sjmcneill			}
8971.1Sjmcneill		}
8981.1Sjmcneill		return EPASSTHROUGH;
8991.1Sjmcneill	}
9001.1Sjmcneill
9011.1Sjmcneill	return EPASSTHROUGH;
9021.1Sjmcneill}
9031.1Sjmcneill
9041.1Sjmcneillstatic paddr_t
9051.1Sjmcneillwiifb_mmap(void *v, void *vs, off_t off, int prot)
9061.1Sjmcneill{
9071.1Sjmcneill	struct wiifb_softc *sc = v;
9081.1Sjmcneill
9091.1Sjmcneill	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL || sc->sc_efb == NULL) {
9101.1Sjmcneill		return -1;
9111.1Sjmcneill	}
9121.1Sjmcneill
9131.1Sjmcneill	if (off < 0 || off >= sc->sc_rgb.dma_size) {
9141.1Sjmcneill		return -1;
9151.1Sjmcneill	}
9161.1Sjmcneill
9171.1Sjmcneill	return bus_dmamem_mmap(sc->sc_rgb.dma_tag,
9181.1Sjmcneill	    sc->sc_rgb.dma_segs, sc->sc_rgb.dma_nsegs,
9191.1Sjmcneill	    off, prot, BUS_DMA_PREFETCHABLE);
9201.1Sjmcneill}
921