wiifb.c revision 1.1
1/* $NetBSD: wiifb.c,v 1.1 2026/01/09 22:54:30 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2024-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: wiifb.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#include <powerpc/spr.h>
40#include <powerpc/oea/spr.h>
41#include <powerpc/oea/hid.h>
42
43#include <dev/videomode/videomode.h>
44#include <dev/wsfb/genfbvar.h>
45
46#include "mainbus.h"
47#include "vireg.h"
48#include "gxreg.h"
49
50#define WIIFB_RGB_WIDTH			640
51#define WIIFB_RGB_HEIGHT		480
52#define WIIFB_RGB_BPP			32
53
54#define WIIFB_ERROR_BLINK_INTERVAL	1000000
55
56#define WIIFB_TOP_BOTTOM_BORDER		16
57#define WIIFB_EFFECTIVE_START(p, w)	\
58	((uintptr_t)(p) + WIIFB_TOP_BOTTOM_BORDER * (w) * 2)
59#define WIIFB_EFFECTIVE_HEIGHT(h)	\
60	((h) - WIIFB_TOP_BOTTOM_BORDER * 2)
61
62#define WIIFB_FIFO_SIZE			(256 * 1024)
63
64#define IBM750CL_SPR_HID2		920
65#define  IBM750CL_SPR_HID2_WPE		0x40000000	/* Write pipe enable */
66#define IBM750CL_SPR_WPAR		921
67
68struct wiifb_mode {
69	const char *		name;
70	u_int			width;
71	u_int			height;
72	u_int			lines;
73};
74
75static uint32_t wiifb_devcmap[16] = {
76	0x00800080,	/* Black */
77	0x1dff1d6b,	/* Blue */
78	0x4b554b4a,	/* Green */
79	0x80808080,	/* Cyan */
80	0x4c544cff,	/* Red */
81	0x3aaa34b5,	/* Magenta */
82	0x7140718a,	/* Brown */
83	0xff80ff80,	/* White */
84	0x80808080,	/* Gray */
85	0xc399c36a,	/* Bright Blue */
86	0xd076d074,	/* Bright Green */
87	0x80808080,	/* Bright Cyan */
88	0x4c544cff,	/* Bright Red */
89	0x3aaa34b5,	/* Bright Magenta */
90	0xe100e194,	/* Bright Yellow */
91	0xff80ff80	/* Bright White */
92};
93
94#define WIIFB_MODE_INDEX(fmt, interlaced)	((fmt << 1) | interlaced)
95
96static const struct wiifb_mode wiifb_modes[] = {
97	[WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 0)] = {
98		.name = "NTSC 480p",
99		.width = 640,
100		.height = 480,
101		.lines = 525,
102	},
103	[WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 1)] = {
104		.name = "NTSC 480i",
105		.width = 640,
106		.height = 480,
107		.lines = 525,
108	},
109	[WIIFB_MODE_INDEX(VI_DCR_FMT_PAL, 1)] = {
110		.name = "PAL 576i",
111		.width = 640,
112		.height = 574,
113		.lines = 625,
114	},
115
116};
117#define WIIFB_NMODES	__arraycount(wiifb_modes)
118
119struct wiifb_dma {
120	bus_dmamap_t		dma_map;
121	bus_dma_tag_t		dma_tag;
122	bus_size_t		dma_size;
123	bus_dma_segment_t	dma_segs[1];
124	int			dma_nsegs;
125	void			*dma_addr;
126};
127
128struct wiifb_softc {
129	struct genfb_softc	sc_gen;
130
131	bus_space_tag_t		sc_bst;
132	bus_space_handle_t	sc_bsh;
133	bus_dma_tag_t		sc_dmat;
134
135	void			*sc_bits;
136
137	uint8_t			sc_format;
138	bool			sc_interlaced;
139
140	const struct wiifb_mode	*sc_curmode;
141
142	u_int			sc_wsmode;
143
144	volatile uint32_t	*sc_efb;
145	volatile uint16_t	*sc_cp;
146	volatile uint16_t	*sc_pe;
147	volatile uint32_t	*sc_pi;
148	gx_wgpipe_t		*sc_wgpipe;
149
150	struct wiifb_dma	sc_rgb;
151	struct wiifb_dma	sc_fifo;
152
153	uint16_t		sc_token;
154};
155
156#define	RD2(sc, reg)		\
157	bus_space_read_2((sc)->sc_bst, (sc)->sc_bsh, (reg))
158#define	RD4(sc, reg)		\
159	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
160#define	WR2(sc, reg, val)	\
161	bus_space_write_2((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
162#define	WR4(sc, reg, val)	\
163	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
164
165#define CP_WRITE(sc, off, val)	(sc)->sc_cp[(off)] = (val)
166#define CP_READ(sc, off)	(sc)->sc_cp[(off)]
167
168#define PE_WRITE(sc, off, val)	(sc)->sc_pe[(off)] = (val)
169#define PE_READ(sc, off)	(sc)->sc_pe[(off)]
170
171#define PI_WRITE(sc, off, val)	(sc)->sc_pi[(off)] = (val)
172#define PI_READ(sc, off)	(sc)->sc_pi[(off)]
173
174static int	wiifb_match(device_t, cfdata_t, void *);
175static void	wiifb_attach(device_t, device_t, void *);
176
177static void	wiifb_accel_init(struct wiifb_softc *);
178static int	wiifb_vi_intr(void *);
179static void	wiifb_vi_refresh(void *);
180
181static void	wiifb_init(struct wiifb_softc *);
182static void	wiifb_set_mode(struct wiifb_softc *, uint8_t, bool);
183static void	wiifb_set_fb(struct wiifb_softc *);
184static void	wiifb_clear_xfb(struct wiifb_softc *);
185
186static int	wiifb_ioctl(void *, void *, u_long, void *, int, lwp_t *);
187static paddr_t	wiifb_mmap(void *, void *, off_t, int);
188
189static struct genfb_ops wiifb_ops = {
190	.genfb_ioctl = wiifb_ioctl,
191	.genfb_mmap = wiifb_mmap,
192};
193
194CFATTACH_DECL_NEW(wiifb, sizeof(struct wiifb_softc),
195	wiifb_match, wiifb_attach, NULL, NULL);
196
197static int
198wiifb_match(device_t parent, cfdata_t cf, void *aux)
199{
200	struct mainbus_attach_args *maa = aux;
201
202	return !wiiu_native && strcmp(maa->maa_name, "genfb") == 0;
203}
204
205static void
206wiifb_attach(device_t parent, device_t self, void *aux)
207{
208	struct wiifb_softc *sc = device_private(self);
209	prop_dictionary_t dict = device_properties(self);
210	struct mainbus_attach_args *maa = aux;
211	int error;
212
213	sc->sc_gen.sc_dev = self;
214	sc->sc_bst = maa->maa_bst;
215	error = bus_space_map(sc->sc_bst, VI_BASE, VI_SIZE, 0, &sc->sc_bsh);
216	if (error != 0) {
217		panic("couldn't map registers");
218	}
219	sc->sc_bits = mapiodev(XFB_START, XFB_SIZE, true);
220	sc->sc_wsmode = WSDISPLAYIO_MODE_EMUL;
221	sc->sc_dmat = maa->maa_dmat;
222
223	wiifb_clear_xfb(sc);
224
225	wiifb_init(sc);
226	wiifb_set_mode(sc, sc->sc_format, sc->sc_interlaced);
227
228	prop_dictionary_set_uint32(dict, "width", sc->sc_curmode->width);
229	prop_dictionary_set_uint32(dict, "height",
230	    WIIFB_EFFECTIVE_HEIGHT(sc->sc_curmode->height));
231	prop_dictionary_set_uint8(dict, "depth", 16);
232	prop_dictionary_set_uint32(dict, "address", XFB_START);
233	prop_dictionary_set_uint32(dict, "virtual_address",
234	    WIIFB_EFFECTIVE_START(sc->sc_bits, sc->sc_curmode->width));
235	prop_dictionary_set_uint64(dict, "devcmap", (uintptr_t)wiifb_devcmap);
236
237	genfb_init(&sc->sc_gen);
238
239	aprint_naive("\n");
240	aprint_normal(": %s\n", sc->sc_curmode->name);
241
242	genfb_cnattach();
243	prop_dictionary_set_bool(dict, "is_console", true);
244	genfb_attach(&sc->sc_gen, &wiifb_ops);
245
246	wiifb_accel_init(sc);
247}
248
249static void
250wiifb_clear_xfb(struct wiifb_softc *sc)
251{
252	u_int offset;
253	uint32_t *p;
254
255	/*
256	 * Paint the entire XFB black. Use 4-byte accesses as the Wii will
257	 * ignore 1- and 2- byte writes to uncached memory.
258	 */
259	for (p = sc->sc_bits, offset = 0;
260	     offset < XFB_SIZE;
261	     offset += 4, p++) {
262		*p = wiifb_devcmap[0];
263	}
264}
265
266static int
267wiifb_vi_init(struct wiifb_softc *sc)
268{
269	device_t dev = sc->sc_gen.sc_dev;
270	void *ih;
271
272	WR4(sc, VI_DI0, VI_DI_ENB |
273			__SHIFTIN(1, VI_DI_VCT) |
274			__SHIFTIN(1, VI_DI_HCT));
275	WR4(sc, VI_DI1, 0);
276	WR4(sc, VI_DI2, 0);
277	WR4(sc, VI_DI3, 0);
278
279	ih = intr_establish_xname(PI_IRQ_VI, IST_LEVEL, IPL_TTY,
280	    wiifb_vi_intr, sc, device_xname(dev));
281	if (ih == NULL) {
282		aprint_error_dev(dev, "failed to install VI intr handler\n");
283		return EIO;
284	}
285
286	return 0;
287}
288
289static int
290wiifb_dma_alloc(struct wiifb_softc *sc, bus_size_t size, bus_size_t align,
291    int flags, struct wiifb_dma *dma)
292{
293	bus_dma_tag_t dmat = sc->sc_dmat; // &wii_mem1_bus_dma_tag;
294	int error;
295
296	error = bus_dmamem_alloc(dmat, size, align, 0,
297	    dma->dma_segs, 1, &dma->dma_nsegs, BUS_DMA_WAITOK);
298	if (error)
299		return error;
300
301	error = bus_dmamem_map(dmat, dma->dma_segs, dma->dma_nsegs,
302	    size, &dma->dma_addr, BUS_DMA_WAITOK | flags);
303	if (error)
304		goto free;
305
306	error = bus_dmamap_create(dmat, size, dma->dma_nsegs,
307	    size, 0, BUS_DMA_WAITOK, &dma->dma_map);
308	if (error)
309		goto unmap;
310
311	error = bus_dmamap_load(dmat, dma->dma_map, dma->dma_addr,
312	    size, NULL, BUS_DMA_WAITOK);
313	if (error)
314	    goto destroy;
315
316	dma->dma_size = size;
317	dma->dma_tag = dmat;
318
319	memset(dma->dma_addr, 0, dma->dma_size);
320
321	return 0;
322
323destroy:
324	bus_dmamap_destroy(dmat, dma->dma_map);
325unmap:
326	bus_dmamem_unmap(dmat, dma->dma_addr, dma->dma_size);
327free:
328	bus_dmamem_free(dmat, dma->dma_segs, dma->dma_nsegs);
329
330	return error;
331}
332
333static void *
334wiifb_mapreg(struct wiifb_softc *sc, bus_addr_t base, bus_size_t size)
335{
336	bus_space_handle_t bsh;
337	int error;
338
339	error = bus_space_map(sc->sc_bst, base, size,
340	    BUS_SPACE_MAP_LINEAR, &bsh);
341	if (error != 0) {
342		panic("couldn't map 0x%x", base);
343	}
344	return bus_space_vaddr(sc->sc_bst, bsh);
345}
346
347static void
348wiifb_set_wgpipe(bus_addr_t base)
349{
350	uint32_t value;
351
352	if (base) {
353		mtspr(IBM750CL_SPR_WPAR, base);
354	}
355	value = mfspr(IBM750CL_SPR_HID2);
356	if (base) {
357		value |= IBM750CL_SPR_HID2_WPE;
358	} else {
359		value &= ~IBM750CL_SPR_HID2_WPE;
360	}
361	mtspr(IBM750CL_SPR_HID2, value);
362}
363
364static void
365wiifb_ppcsync(void)
366{
367	uint32_t value;
368
369	value = mfspr(SPR_HID0);
370	mtspr(SPR_HID0, value | 0x8);
371	asm volatile("isync" ::: "memory");
372	asm volatile("sync" ::: "memory");
373	mtspr(SPR_HID0, value);
374}
375
376static void
377wiifb_gx_bp_load(struct wiifb_softc *sc, uint32_t data)
378{
379	GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x61);
380	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data);
381}
382
383static void
384wiifb_gx_cp_load(struct wiifb_softc *sc, uint8_t addr, uint32_t data)
385{
386	GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x08);
387	GX_STRICT_ORDER(sc->sc_wgpipe->u8 = addr);
388	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data);
389}
390
391static void
392wiifb_gx_xf_load(struct wiifb_softc *sc, uint16_t addr, uint32_t data)
393{
394	GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x10);
395	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = addr);
396	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data);
397}
398
399static void
400wiifb_gx_xf_load_multi(struct wiifb_softc *sc, uint16_t addr,
401    uint16_t count, uint32_t *data)
402{
403	uint16_t n;
404
405	GX_STRICT_ORDER(sc->sc_wgpipe->u8 = 0x10);
406	GX_STRICT_ORDER(sc->sc_wgpipe->u32 =
407			(((uint32_t)count - 1) << 16) | addr);
408	for (n = 0; n < count; n++) {
409		GX_STRICT_ORDER(sc->sc_wgpipe->u32 = data[n]);
410	}
411}
412
413static void
414wiifb_gx_set_viewport(struct wiifb_softc *sc, u_int w, u_int h)
415{
416	uint32_t data[6];
417
418	KASSERT(w == 640);
419	KASSERT(h == 480);
420
421	data[0] = 0x00000140;
422	data[1] = 0xffffff10;
423	data[2] = 0x00ffffff;
424	data[3] = 0x00000296;
425	data[4] = 0x00000246;
426	data[5] = 0x00ffffff;
427
428	wiifb_gx_xf_load_multi(sc, GX_XF_VIEWPORT_X0,
429	    __arraycount(data), data);
430}
431
432static void
433wiifb_gx_set_scissor(struct wiifb_softc *sc, u_int x, u_int y,
434    u_int w, u_int h)
435{
436	uint32_t xo = x + 0x156;
437	uint32_t yo = y + 0x156;
438	uint32_t wo = xo + w - 1;
439	uint32_t ho = yo + h - 1;
440
441	wiifb_gx_bp_load(sc, 0x20000000 | yo | (xo << 12));
442	wiifb_gx_bp_load(sc, 0x21000000 | ho | (wo << 12));
443	wiifb_gx_bp_load(sc, 0x59000000 | GX_XY(xo >> 1, yo >> 1));
444}
445
446static void
447wiifb_gx_init(struct wiifb_softc *sc)
448{
449	const uint32_t fifo_start = sc->sc_fifo.dma_segs[0].ds_addr;
450	const uint32_t fifo_end = fifo_start + sc->sc_fifo.dma_size - 4;
451	const uint32_t fifo_hiwat = GX_FIFO_HIWAT(sc->sc_fifo.dma_size);
452	const uint32_t fifo_lowat = GX_FIFO_LOWAT(sc->sc_fifo.dma_size);
453
454	/* Disable WGPIPE and unlink CP FIFO before changing settings. */
455	wiifb_set_wgpipe(0);
456	CP_WRITE(sc, CP_CR, 0);
457	CP_WRITE(sc, CP_CLEAR, CP_CLEAR_UNDERFLOW | CP_CLEAR_OVERFLOW);
458
459	/* Setup GP FIFO */
460	CP_WRITE(sc, CP_FIFO_BASE_LO, LOWER_16_BITS(fifo_start));
461	CP_WRITE(sc, CP_FIFO_BASE_HI, UPPER_16_BITS(fifo_start));
462	CP_WRITE(sc, CP_FIFO_END_LO, LOWER_16_BITS(fifo_end));
463	CP_WRITE(sc, CP_FIFO_END_HI, UPPER_16_BITS(fifo_end));
464	CP_WRITE(sc, CP_FIFO_HIWAT_LO, LOWER_16_BITS(fifo_hiwat));
465	CP_WRITE(sc, CP_FIFO_HIWAT_HI, UPPER_16_BITS(fifo_hiwat));
466	CP_WRITE(sc, CP_FIFO_LOWAT_LO, LOWER_16_BITS(fifo_lowat));
467	CP_WRITE(sc, CP_FIFO_LOWAT_HI, UPPER_16_BITS(fifo_lowat));
468	CP_WRITE(sc, CP_FIFO_RW_DIST_LO, 0);
469	CP_WRITE(sc, CP_FIFO_RW_DIST_HI, 0);
470	CP_WRITE(sc, CP_FIFO_WRITE_PTR_LO, LOWER_16_BITS(fifo_start));
471	CP_WRITE(sc, CP_FIFO_WRITE_PTR_HI, UPPER_16_BITS(fifo_start));
472	CP_WRITE(sc, CP_FIFO_READ_PTR_LO, LOWER_16_BITS(fifo_start));
473	CP_WRITE(sc, CP_FIFO_READ_PTR_HI, UPPER_16_BITS(fifo_start));
474	wiifb_ppcsync();
475
476	/* Setup CPU FIFO */
477	PI_WRITE(sc, PI_FIFO_BASE_START, fifo_start);
478	PI_WRITE(sc, PI_FIFO_BASE_END, fifo_end);
479	PI_WRITE(sc, PI_FIFO_WRITE_PTR, fifo_start);
480	wiifb_ppcsync();
481
482	/* Link CP/PE FIFO and enable GP FIFO */
483	CP_WRITE(sc, CP_CR, CP_CR_GP_LINK_ENABLE);
484	CP_WRITE(sc, CP_CR, CP_READ(sc, CP_CR) | CP_CR_READ_ENABLE);
485
486	/* Init pixel engine */
487	PE_WRITE(sc, PE_ZCONF,
488	    PE_ZCONF_UPD_ENABLE |
489	    PE_ZCONF_FUNC_ALWAYS |
490	    PE_ZCONF_COMP_ENABLE);
491	PE_WRITE(sc, PE_ALPHA_CONF,
492	    PE_ALPHA_CONF_OP_SET |
493	    PE_ALPHA_CONF_SRC_1 |
494	    PE_ALPHA_CONF_DST_0 |
495	    PE_ALPHA_CONF_UPD_A |
496	    PE_ALPHA_CONF_UPD_C);
497	PE_WRITE(sc, PE_ALPHA_DEST, 0);
498	PE_WRITE(sc, PE_ALPHA_MODE, PE_ALPHA_MODE_ALWAYS);
499	PE_WRITE(sc, PE_ALPHA_READ,
500	    PE_ALPHA_READ_UNK | PE_ALPHA_READ_FF);
501
502	/* Enable WG pipe */
503	wiifb_set_wgpipe(WGPIPE_BASE);
504
505	/* Sanitize command processor registers */
506	for (int n = 0; n < 8; n++) {
507		wiifb_gx_cp_load(sc, 0x80 + n, 0x80000000);
508	}
509        wiifb_gx_cp_load(sc, 0x20, 0);
510
511	/* Sanitize transform unit registers */
512        wiifb_gx_xf_load(sc, 0x1000, 0x3f);
513        wiifb_gx_xf_load(sc, 0x1005, 0x01);
514        wiifb_gx_xf_load(sc, 0x1012, 0x01);
515        wiifb_gx_xf_load(sc, 0x1006, 0);
516
517	/* Initialize blitting processor */
518        wiifb_gx_bp_load(sc, 0x00000001);
519        wiifb_gx_bp_load(sc, 0x01666666);
520        wiifb_gx_bp_load(sc, 0x02666666);
521        wiifb_gx_bp_load(sc, 0x03666666);
522        wiifb_gx_bp_load(sc, 0x04666666);
523        wiifb_gx_bp_load(sc, 0x22000606);
524        wiifb_gx_bp_load(sc, 0x23000000);
525        wiifb_gx_bp_load(sc, 0x24000000);
526        wiifb_gx_bp_load(sc, 0x42000000);
527        wiifb_gx_bp_load(sc, 0x44000003);
528        wiifb_gx_bp_load(sc, 0x43000000);
529        wiifb_gx_bp_load(sc, 0x53595000);
530        wiifb_gx_bp_load(sc, 0x54000015);
531        wiifb_gx_bp_load(sc, 0x550003ff);
532        wiifb_gx_bp_load(sc, 0x560003ff);
533        wiifb_gx_bp_load(sc, 0x5800000f);
534        wiifb_gx_bp_load(sc, 0x67000000);
535
536	/* Set viewport and scissor parameters */
537	wiifb_gx_set_viewport(sc, WIIFB_RGB_WIDTH, WIIFB_RGB_HEIGHT);
538	wiifb_gx_set_scissor(sc, 0, 0, WIIFB_RGB_WIDTH, WIIFB_RGB_HEIGHT);
539
540	wiifb_gx_bp_load(sc, 0x4000001f);
541
542	if (sc->sc_curmode->height == 574) {
543		/* Scale 480 lines to PAL display height */
544		wiifb_gx_bp_load(sc, 0x4e000127);
545	}
546
547	/* Copy mode parameters */
548	wiifb_gx_bp_load(sc, 0x410004bc);
549
550	/* Copy source */
551	wiifb_gx_bp_load(sc, 0x49000000 | GX_XY(0, 0));
552	wiifb_gx_bp_load(sc, 0x4a000000 |
553	    GX_XY(WIIFB_RGB_WIDTH - 1, WIIFB_RGB_HEIGHT - 1));
554
555	/* Copy destination */
556	wiifb_gx_bp_load(sc, 0x4d000000 | (WIIFB_RGB_WIDTH >> 4));
557
558	/* XFB address */
559	wiifb_gx_bp_load(sc, 0x4b000000 | (XFB_START >> 5));
560
561	/* Copy clear settings */
562	wiifb_gx_bp_load(sc, 0x4f000000);
563	wiifb_gx_bp_load(sc, 0x50000000);
564	wiifb_gx_bp_load(sc, 0x5100ffff);
565}
566
567static void
568wiifb_gx_flush(struct wiifb_softc *sc)
569{
570	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
571	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
572	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
573	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
574	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
575	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
576	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
577	GX_STRICT_ORDER(sc->sc_wgpipe->u32 = 0);
578	wiifb_ppcsync();
579}
580
581static void
582wiifb_accel_init(struct wiifb_softc *sc)
583{
584	bus_size_t rgb_size;
585	bus_size_t fifo_size;
586
587	if (wiifb_vi_init(sc) != 0) {
588		panic("couldn't init VI");
589	}
590
591	rgb_size = WIIFB_RGB_WIDTH * WIIFB_RGB_HEIGHT;
592	rgb_size = rgb_size * WIIFB_RGB_BPP / NBBY;
593	rgb_size = roundup(rgb_size, PAGE_SIZE);
594	if (wiifb_dma_alloc(sc, rgb_size, PAGE_SIZE, BUS_DMA_PREFETCHABLE,
595			    &sc->sc_rgb) != 0) {
596		panic("couldn't alloc rgb fb");
597	}
598
599	fifo_size = WIIFB_FIFO_SIZE;
600	if (wiifb_dma_alloc(sc, fifo_size, GX_FIFO_ALIGN, BUS_DMA_NOCACHE,
601	    		    &sc->sc_fifo) != 0) {
602		panic("couldn't alloc gx fifo");
603	}
604
605	sc->sc_efb = wiifb_mapreg(sc, EFB_BASE, EFB_SIZE);
606	sc->sc_cp = wiifb_mapreg(sc, CP_BASE, CP_SIZE);
607	sc->sc_pe = wiifb_mapreg(sc, PE_BASE, PE_SIZE);
608	sc->sc_pi = wiifb_mapreg(sc, PI_BASE, PI_SIZE);
609	sc->sc_wgpipe = wiifb_mapreg(sc, WGPIPE_BASE, WGPIPE_SIZE);
610
611	wiifb_gx_init(sc);
612};
613
614static void
615wiifb_rgb_to_efb(struct wiifb_softc *sc)
616{
617	u_int y;
618	uint32_t *src = sc->sc_rgb.dma_addr;
619	uint32_t *dst = __UNVOLATILE(sc->sc_efb);
620	register_t hid0 = mfspr(SPR_HID0);
621
622	KASSERT(src != NULL);
623	KASSERT(dst != NULL);
624
625	/* Disable store gathering while writing to EFB. */
626	mtspr(SPR_HID0, hid0 & ~HID0_SGE);
627
628	for (y = 0; y < WIIFB_RGB_HEIGHT; y++) {
629		memcpy(dst, src, WIIFB_RGB_WIDTH * 4);
630		src += WIIFB_RGB_WIDTH;
631		dst += 1024;
632	}
633
634	/* Re-enable store gathering. */
635	mtspr(SPR_HID0, hid0);
636}
637
638static void
639wiifb_efb_to_xfb(struct wiifb_softc *sc)
640{
641	const uint32_t copy_mask = sc->sc_curmode->height == 574 ? 0x400 : 0x0;
642
643	/* Execute copy to XFB */
644	wiifb_gx_bp_load(sc, 0x52004803 | copy_mask);
645}
646
647static void
648wiifb_gx_draw_done(struct wiifb_softc *sc, uint16_t token)
649{
650	/* Draw done */
651	wiifb_gx_bp_load(sc, 0x45000002);
652	/* Write tokens */
653        wiifb_gx_bp_load(sc, 0x48000000 | token);
654        wiifb_gx_bp_load(sc, 0x47000000 | token);
655	/* Flush WG pipe */
656	wiifb_gx_flush(sc);
657}
658
659static void
660wiifb_vi_refresh(void *priv)
661{
662	struct wiifb_softc *sc = priv;
663
664	wiifb_rgb_to_efb(sc);
665	wiifb_efb_to_xfb(sc);
666	wiifb_gx_draw_done(sc, sc->sc_token++);
667}
668
669static int
670wiifb_vi_intr(void *priv)
671{
672	struct wiifb_softc *sc = priv;
673	uint32_t di0;
674	int ret = 0;
675
676	di0 = RD4(sc, VI_DI0);
677
678	WR4(sc, VI_DI0, RD4(sc, VI_DI0) & ~VI_DI_INT);
679	WR4(sc, VI_DI1, RD4(sc, VI_DI1) & ~VI_DI_INT);
680	WR4(sc, VI_DI2, RD4(sc, VI_DI2) & ~VI_DI_INT);
681	WR4(sc, VI_DI3, RD4(sc, VI_DI3) & ~VI_DI_INT);
682
683	if ((di0 & VI_DI_INT) != 0 &&
684	    sc->sc_wsmode != WSDISPLAYIO_MODE_EMUL) {
685		wiifb_vi_refresh(sc);
686		ret = 1;
687	}
688
689	return ret;
690}
691
692static void
693wiifb_init(struct wiifb_softc *sc)
694{
695	uint16_t dcr;
696	uint16_t visel;
697
698	/* Read current display format and interlaced settings. */
699	dcr = RD2(sc, VI_DCR);
700	if ((dcr & VI_DCR_ENB) != 0) {
701		sc->sc_format = __SHIFTOUT(dcr, VI_DCR_FMT);
702		sc->sc_interlaced = (dcr & VI_DCR_NIN) == 0;
703	} else {
704		visel = RD2(sc, VI_VISEL);
705		sc->sc_format = VI_DCR_FMT_NTSC;
706		sc->sc_interlaced = (visel & VI_VISEL_COMPONENT_CABLE) == 0;
707	}
708
709	/* Reset video interface. */
710	WR2(sc, VI_DCR, VI_DCR_RST);
711	delay(1000);
712
713	/* Initialize video format and interlace selector. */
714	dcr = __SHIFTIN(sc->sc_format, VI_DCR_FMT) |
715	      (sc->sc_interlaced ? 0 : VI_DCR_NIN);
716	WR2(sc, VI_DCR, dcr);
717}
718
719static void
720wiifb_set_mode(struct wiifb_softc *sc, uint8_t format, bool interlaced)
721{
722	u_int modeidx;
723	u_int strides, reads;
724
725	modeidx = WIIFB_MODE_INDEX(format, interlaced);
726	if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 1)) {
727		/* NTSC 480i Magic numbers from YAGCD. */
728		WR2(sc, VI_VTR, 0x0f06);
729		WR4(sc, VI_HTR0, 0x476901AD);
730		WR4(sc, VI_HTR1, 0x02EA5140);
731		WR4(sc, VI_VTO, 0x00030018);
732		WR4(sc, VI_VTE, 0x00020019);
733		WR4(sc, VI_BBOI, 0x410C410C);
734		WR4(sc, VI_BBEI, 0x40ED40ED);
735		WR2(sc, VI_DPV, 0x0000);
736		WR2(sc, VI_DPH, 0x0000);
737	} else if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 0)) {
738		/* NTSC 480p */
739		WR2(sc, VI_VTR, 0x1e0c);
740		WR4(sc, VI_HTR0, 0x476901ad);
741		WR4(sc, VI_HTR1, 0x030a4940);
742		WR4(sc, VI_VTO, 0x00060030);
743		WR4(sc, VI_VTE, 0x00060030);
744		WR4(sc, VI_BBOI, 0x81d881d8);
745		WR4(sc, VI_BBEI, 0x81d881d8);
746		WR2(sc, VI_DPV, 0x0000);
747		WR2(sc, VI_DPH, 0x0000);
748	} else if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_PAL, 1)) {
749		/* PAL 576i */
750		WR2(sc, VI_VTR, 0x11f5);
751		WR4(sc, VI_HTR0, 0x4b6a01b0);
752		WR4(sc, VI_HTR1, 0x02f85640);
753		WR4(sc, VI_VTO, 0x00010023);
754		WR4(sc, VI_VTE, 0x00000024);
755		WR4(sc, VI_BBOI, 0x4d2b4d6d);
756		WR4(sc, VI_BBEI, 0x4d8a4d4c);
757		WR2(sc, VI_DPV, 0x013c);
758		WR2(sc, VI_DPH, 0x0144);
759	} else {
760		/*
761		 * Display mode is not supported. Blink the slot LED to
762		 * indicate failure.
763		 */
764		wii_slot_led_blink(WIIFB_ERROR_BLINK_INTERVAL);
765	}
766
767	if (modeidx >= WIIFB_NMODES || wiifb_modes[modeidx].name == NULL) {
768		panic("Unsupported format (0x%x) / interlaced (%d) settings",
769		    sc->sc_format, sc->sc_interlaced);
770	}
771	sc->sc_curmode = &wiifb_modes[modeidx];
772
773	/* Filter coefficient table, values from YAGCD. */
774	WR4(sc, VI_FCT0, 0x1ae771f0);
775	WR4(sc, VI_FCT1, 0x0db4a574);
776	WR4(sc, VI_FCT2, 0x00c1188e);
777	WR4(sc, VI_FCT3, 0xc4c0cbe2);
778	WR4(sc, VI_FCT4, 0xfcecdecf);
779	WR4(sc, VI_FCT5, 0x13130f08);
780	WR4(sc, VI_FCT6, 0x00080C0f);
781
782	/* Unknown registers. */
783	WR4(sc, VI_UNKNOWN_68H, 0x00ff0000);
784	WR2(sc, VI_UNKNOWN_76H, 0x00ff);
785	WR4(sc, VI_UNKNOWN_78H, 0x00ff00ff);
786	WR4(sc, VI_UNKNOWN_7CH, 0x00ff00ff);
787
788	/* Picture configuration */
789	strides = (sc->sc_curmode->width * 2) / (interlaced ? 16 : 32);
790	reads = (sc->sc_curmode->width * 2) / 32;
791	WR2(sc, VI_PICCONF,
792	    __SHIFTIN(strides, VI_PICCONF_STRIDES) |
793	    __SHIFTIN(reads, VI_PICCONF_READS));
794
795	/* Horizontal scaler configuration */
796	if (interlaced) {
797		WR2(sc, VI_HSR, __SHIFTIN(256, VI_HSR_STP));
798	} else {
799		WR2(sc, VI_HSR, __SHIFTIN(244, VI_HSR_STP) | VI_HSR_HS_EN);
800	}
801
802	/* Video clock configuration */
803	WR2(sc, VI_VICLK,
804	    interlaced ? VI_VICLK_SEL_27MHZ : VI_VICLK_SEL_54MHZ);
805
806	/* Horizontal scaling width */
807	WR2(sc, VI_HSCALINGW, sc->sc_curmode->width);
808
809	/* Set framebuffer address */
810	wiifb_set_fb(sc);
811
812	/* Finally, enable the framebuffer */
813	WR2(sc, VI_DCR, RD2(sc, VI_DCR) | VI_DCR_ENB);
814}
815
816static void
817wiifb_set_fb(struct wiifb_softc *sc)
818{
819	uint32_t taddr = XFB_START;
820	uint32_t baddr = taddr + (sc->sc_interlaced ?
821				  sc->sc_curmode->width * 2 : 0);
822
823	WR4(sc, VI_TFBL,
824	    VI_TFBL_PGOFF |
825	    __SHIFTIN((taddr >> 5), VI_TFBL_FBB) |
826	    __SHIFTIN((taddr / 2) & 0xf, VI_TFBL_XOF));
827	WR4(sc, VI_TFBR, 0);
828
829	WR4(sc, VI_BFBL,
830	    VI_BFBL_PGOFF |
831	    __SHIFTIN((baddr >> 5), VI_BFBL_FBB) |
832	    __SHIFTIN((baddr / 2) & 0xf, VI_BFBL_XOF));
833	WR4(sc, VI_BFBR, 0);
834}
835
836static int
837wiifb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l)
838{
839	struct wiifb_softc *sc = v;
840	struct wsdisplayio_bus_id *busid;
841	struct wsdisplayio_fbinfo *fbi;
842	u_int video;
843	u_int wsmode;
844
845	switch (cmd) {
846	case WSDISPLAYIO_GTYPE:
847		*(u_int *)data = WSDISPLAY_TYPE_GENFB;
848		return 0;
849	case WSDISPLAYIO_GET_BUSID:
850		busid = data;
851		busid->bus_type = WSDISPLAYIO_BUS_SOC;
852		return 0;
853	case WSDISPLAYIO_GET_FBINFO:
854		fbi = data;
855		/*
856		 * rasops info does not match the pixel encoding due to our
857		 * devcmap, so fill out fbinfo manually instead of relying
858		 * on wsdisplayio_get_fbinfo.
859		 */
860		fbi->fbi_fboffset = 0;
861		fbi->fbi_width = WIIFB_RGB_WIDTH;
862		fbi->fbi_height = WIIFB_RGB_HEIGHT;
863		fbi->fbi_bitsperpixel = WIIFB_RGB_BPP;
864		fbi->fbi_stride = fbi->fbi_width * fbi->fbi_bitsperpixel / 8;
865		fbi->fbi_fbsize = fbi->fbi_height * fbi->fbi_stride;
866		fbi->fbi_pixeltype = WSFB_RGB;
867		fbi->fbi_flags = WSFB_VRAM_IS_RAM;
868		return 0;
869
870	case WSDISPLAYIO_SVIDEO:
871		video = *(u_int *)data;
872		switch (video) {
873		case WSDISPLAYIO_VIDEO_OFF:
874			out32(HW_VIDIM, __SHIFTIN(7, VIDIM_Y) |
875					__SHIFTIN(7, VIDIM_C) |
876					VIDIM_E);
877			return 0;
878		case WSDISPLAYIO_VIDEO_ON:
879			out32(HW_VIDIM, 0);
880			return 0;
881		default:
882			return EINVAL;
883		}
884
885	case WSDISPLAYIO_SMODE:
886		wsmode = *(u_int *)data;
887		if (wsmode != WSDISPLAYIO_MODE_EMUL) {
888			/* Blank the RGB FB when leaving text mode */
889			memset(sc->sc_rgb.dma_addr, 0, sc->sc_rgb.dma_size);
890		}
891		if (sc->sc_wsmode != wsmode) {
892			sc->sc_wsmode = wsmode;
893
894			if (wsmode == WSDISPLAYIO_MODE_EMUL) {
895				wiifb_clear_xfb(sc);
896			}
897		}
898		return EPASSTHROUGH;
899	}
900
901	return EPASSTHROUGH;
902}
903
904static paddr_t
905wiifb_mmap(void *v, void *vs, off_t off, int prot)
906{
907	struct wiifb_softc *sc = v;
908
909	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL || sc->sc_efb == NULL) {
910		return -1;
911	}
912
913	if (off < 0 || off >= sc->sc_rgb.dma_size) {
914		return -1;
915	}
916
917	return bus_dmamem_mmap(sc->sc_rgb.dma_tag,
918	    sc->sc_rgb.dma_segs, sc->sc_rgb.dma_nsegs,
919	    off, prot, BUS_DMA_PREFETCHABLE);
920}
921