xref: /openbsd/sys/arch/sparc64/dev/creator.c (revision 1c00236e)
1*1c00236eSmiod /*	$OpenBSD: creator.c,v 1.57 2022/10/21 18:55:42 miod Exp $	*/
2e719d43bSjason 
3e719d43bSjason /*
4e719d43bSjason  * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
5e719d43bSjason  * All rights reserved.
6e719d43bSjason  *
7e719d43bSjason  * Redistribution and use in source and binary forms, with or without
8e719d43bSjason  * modification, are permitted provided that the following conditions
9e719d43bSjason  * are met:
10e719d43bSjason  * 1. Redistributions of source code must retain the above copyright
11e719d43bSjason  *    notice, this list of conditions and the following disclaimer.
12e719d43bSjason  * 2. Redistributions in binary form must reproduce the above copyright
13e719d43bSjason  *    notice, this list of conditions and the following disclaimer in the
14e719d43bSjason  *    documentation and/or other materials provided with the distribution.
15e719d43bSjason  *
16e719d43bSjason  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17e719d43bSjason  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18e719d43bSjason  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19e719d43bSjason  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20e719d43bSjason  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21e719d43bSjason  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22e719d43bSjason  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23e719d43bSjason  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24e719d43bSjason  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25e719d43bSjason  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26e719d43bSjason  * POSSIBILITY OF SUCH DAMAGE.
27e719d43bSjason  */
28e719d43bSjason 
29e719d43bSjason #include <sys/param.h>
30e719d43bSjason #include <sys/systm.h>
31e719d43bSjason #include <sys/kernel.h>
32e719d43bSjason #include <sys/device.h>
33e719d43bSjason #include <sys/conf.h>
34b697b291Skettenis #include <sys/malloc.h>
35e719d43bSjason 
36e719d43bSjason #include <machine/autoconf.h>
37*1c00236eSmiod #include <machine/bus.h>
38*1c00236eSmiod #include <machine/fsr.h>
39e719d43bSjason #include <machine/openfirm.h>
40e719d43bSjason 
41e719d43bSjason #include <dev/wscons/wsconsio.h>
42e719d43bSjason #include <dev/wscons/wsdisplayvar.h>
43e719d43bSjason #include <dev/rasops/rasops.h>
442628683eSmiod #include <machine/fbvar.h>
45e719d43bSjason 
4624da7ca2Sjason #include <sparc64/dev/creatorreg.h>
471a226b7bSfgsch #include <sparc64/dev/creatorvar.h>
48e719d43bSjason 
49b9ac102eSmiod int	creator_match(struct device *, void *, void *);
50b9ac102eSmiod void	creator_attach(struct device *, struct device *, void *);
51e719d43bSjason int	creator_ioctl(void *, u_long, caddr_t, int, struct proc *);
52e719d43bSjason paddr_t creator_mmap(void *, off_t, int);
53b697b291Skettenis 
5424da7ca2Sjason void	creator_ras_fifo_wait(struct creator_softc *, int);
5524da7ca2Sjason void	creator_ras_wait(struct creator_softc *);
56bf2654a9Sjason void	creator_ras_init(struct creator_softc *);
57072953e3Smiod int	creator_ras_copyrows(void *, int, int, int);
58e0c3e559Sjsg int	creator_ras_erasecols(void *, int, int, int, uint32_t);
59e0c3e559Sjsg int	creator_ras_eraserows(void *, int, int, uint32_t);
60bf2654a9Sjason void	creator_ras_fill(struct creator_softc *);
61d7a30e89Sjason void	creator_ras_setfg(struct creator_softc *, int32_t);
62b697b291Skettenis 
635193c8f3Sjason int	creator_setcursor(struct creator_softc *, struct wsdisplay_cursor *);
645193c8f3Sjason int	creator_updatecursor(struct creator_softc *, u_int);
655193c8f3Sjason void	creator_curs_enable(struct creator_softc *, u_int);
66e719d43bSjason 
678e79cc55Sderaadt #ifndef SMALL_KERNEL
68ef89f9e6Smpi void	creator_load_firmware(struct device *);
698e79cc55Sderaadt #endif /* SMALL_KERNEL */
70b697b291Skettenis void	creator_load_sram(struct creator_softc *, u_int32_t *, u_int32_t);
71b697b291Skettenis 
72e719d43bSjason struct wsdisplay_accessops creator_accessops = {
7387eec248Smiod 	.ioctl = creator_ioctl,
7487eec248Smiod 	.mmap = creator_mmap
75e719d43bSjason };
76e719d43bSjason 
77e719d43bSjason struct cfdriver creator_cd = {
78e719d43bSjason 	NULL, "creator", DV_DULL
79e719d43bSjason };
80e719d43bSjason 
81eb7eaf8dSmpi const struct cfattach creator_ca = {
82b9ac102eSmiod 	sizeof(struct creator_softc), creator_match, creator_attach
83b9ac102eSmiod };
84b9ac102eSmiod 
85b9ac102eSmiod int
creator_match(struct device * parent,void * match,void * aux)8647c47feaSjsg creator_match(struct device *parent, void *match, void *aux)
87e719d43bSjason {
88b9ac102eSmiod 	struct mainbus_attach_args *ma = aux;
89b9ac102eSmiod 
90b9ac102eSmiod 	if (strcmp(ma->ma_name, "SUNW,ffb") == 0 ||
91b9ac102eSmiod 	    strcmp(ma->ma_name, "SUNW,afb") == 0)
92b9ac102eSmiod 		return (1);
93b9ac102eSmiod 	return (0);
94b9ac102eSmiod }
95b9ac102eSmiod 
96b9ac102eSmiod void
creator_attach(struct device * parent,struct device * self,void * aux)9747c47feaSjsg creator_attach(struct device *parent, struct device *self, void *aux)
98b9ac102eSmiod {
99b9ac102eSmiod 	struct creator_softc *sc = (struct creator_softc *)self;
100b9ac102eSmiod 	struct mainbus_attach_args *ma = aux;
101b9ac102eSmiod 	extern int fbnode;
102b9ac102eSmiod 	int i, nregs;
103ad150696Sfgsch 	char *model;
1041a226b7bSfgsch 	int btype;
105e719d43bSjason 
106b9ac102eSmiod 	sc->sc_bt = ma->ma_bustag;
107b9ac102eSmiod 
108b9ac102eSmiod 	nregs = min(ma->ma_nreg, FFB_NREGS);
109b9ac102eSmiod 
110b9ac102eSmiod 	if (nregs <= FFB_REG_DFB24) {
111b9ac102eSmiod 		printf(": no dfb24 regs found\n");
112b9ac102eSmiod 		return;
113b9ac102eSmiod 	}
114b9ac102eSmiod 
115b9ac102eSmiod 	if (bus_space_map(sc->sc_bt, ma->ma_reg[FFB_REG_DFB24].ur_paddr,
116b9ac102eSmiod 	    ma->ma_reg[FFB_REG_DFB24].ur_len, BUS_SPACE_MAP_LINEAR,
117b9ac102eSmiod 	    &sc->sc_pixel_h)) {
118b9ac102eSmiod 		printf(": failed to map dfb24\n");
119b9ac102eSmiod 		return;
120b9ac102eSmiod 	}
121b9ac102eSmiod 
122b9ac102eSmiod 	if (bus_space_map(sc->sc_bt, ma->ma_reg[FFB_REG_FBC].ur_paddr,
123b9ac102eSmiod 	    ma->ma_reg[FFB_REG_FBC].ur_len, 0, &sc->sc_fbc_h)) {
124b9ac102eSmiod 		printf(": failed to map fbc\n");
125b9ac102eSmiod 		goto unmap_dfb24;
126b9ac102eSmiod 	}
127b9ac102eSmiod 
128b9ac102eSmiod 	if (bus_space_map(sc->sc_bt, ma->ma_reg[FFB_REG_DAC].ur_paddr,
129b9ac102eSmiod 	    ma->ma_reg[FFB_REG_DAC].ur_len, 0, &sc->sc_dac_h)) {
130b9ac102eSmiod 		printf(": failed to map dac\n");
131b9ac102eSmiod 		goto unmap_fbc;
132b9ac102eSmiod 	}
133b9ac102eSmiod 
134b9ac102eSmiod 	for (i = 0; i < nregs; i++) {
135b9ac102eSmiod 		sc->sc_addrs[i] = ma->ma_reg[i].ur_paddr;
136b9ac102eSmiod 		sc->sc_sizes[i] = ma->ma_reg[i].ur_len;
137b9ac102eSmiod 	}
138b9ac102eSmiod 	sc->sc_nreg = nregs;
139b9ac102eSmiod 
140b9ac102eSmiod 	sc->sc_console = (fbnode == ma->ma_node);
141b9ac102eSmiod 	sc->sc_node = ma->ma_node;
142b9ac102eSmiod 
143b9ac102eSmiod 	if (strcmp(ma->ma_name, "SUNW,afb") == 0)
144b9ac102eSmiod 		sc->sc_type = FFB_AFB;
145e719d43bSjason 
1465193c8f3Sjason 	/*
1475193c8f3Sjason 	 * Prom reports only the length of the fcode header, we need
1485193c8f3Sjason 	 * the whole thing.
1495193c8f3Sjason 	 */
1505193c8f3Sjason 	sc->sc_sizes[0] = 0x00400000;
1515193c8f3Sjason 
1521a226b7bSfgsch 	if (sc->sc_type == FFB_CREATOR) {
1531a226b7bSfgsch 		btype = getpropint(sc->sc_node, "board_type", 0);
154e719d43bSjason 		if ((btype & 7) == 3)
155b9ac102eSmiod 			printf(": Creator3D");
156e719d43bSjason 		else
157b9ac102eSmiod 			printf(": Creator");
158cca139b8Sfgsch 	} else
159b9ac102eSmiod 		printf(": Elite3D");
160e719d43bSjason 
1611a226b7bSfgsch 	model = getpropstring(sc->sc_node, "model");
162ad150696Sfgsch 	if (model == NULL || strlen(model) == 0)
163ad150696Sfgsch 		model = "unknown";
164e719d43bSjason 
1655193c8f3Sjason 	DAC_WRITE(sc, FFB_DAC_TYPE, DAC_TYPE_GETREV);
1665193c8f3Sjason 	sc->sc_dacrev = DAC_READ(sc, FFB_DAC_VALUE) >> 28;
1675193c8f3Sjason 
1687ef7c58cSmiod 	printf(", model %s, dac %u", model, sc->sc_dacrev);
1695193c8f3Sjason 
1705193c8f3Sjason 	if (sc->sc_type == FFB_AFB)
1715193c8f3Sjason 		sc->sc_dacrev = 10;
172ad150696Sfgsch 
1732628683eSmiod 	fb_setsize(&sc->sc_sunfb, 32, 1152, 900, sc->sc_node, 0);
1742628683eSmiod 	/* linesize has a fixed value, compensate */
1752628683eSmiod 	sc->sc_sunfb.sf_linebytes = 8192;
1762628683eSmiod 	sc->sc_sunfb.sf_fbsize = sc->sc_sunfb.sf_height * 8192;
177e719d43bSjason 
1787ef7c58cSmiod 	printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height);
1797ef7c58cSmiod 
1802628683eSmiod 	sc->sc_sunfb.sf_ro.ri_bits = (void *)bus_space_vaddr(sc->sc_bt,
1811eee2636Sjason 	    sc->sc_pixel_h);
1822628683eSmiod 	sc->sc_sunfb.sf_ro.ri_hw = sc;
183607827a8Smiod 	fbwscons_init(&sc->sc_sunfb, 0, sc->sc_console);
1841eee2636Sjason 
1852628683eSmiod 	if ((sc->sc_sunfb.sf_dev.dv_cfdata->cf_flags & CREATOR_CFFLAG_NOACCEL)
1862628683eSmiod 	    == 0) {
1872628683eSmiod 		sc->sc_sunfb.sf_ro.ri_ops.eraserows = creator_ras_eraserows;
1882628683eSmiod 		sc->sc_sunfb.sf_ro.ri_ops.erasecols = creator_ras_erasecols;
1892628683eSmiod 		sc->sc_sunfb.sf_ro.ri_ops.copyrows = creator_ras_copyrows;
190bf2654a9Sjason 		creator_ras_init(sc);
191b697b291Skettenis 
1928e79cc55Sderaadt #ifndef SMALL_KERNEL
193b697b291Skettenis 		/*
194b697b291Skettenis 		 * Elite3D cards need a firmware for accelerated X to
195b697b291Skettenis 		 * work.  Console framebuffer acceleration will work
196b697b291Skettenis 		 * without it though, so doing this late should be
197b697b291Skettenis 		 * fine.
198b697b291Skettenis 		 */
199b697b291Skettenis 		if (sc->sc_type == FFB_AFB)
200ef89f9e6Smpi 			config_mountroot(self, creator_load_firmware);
2018e79cc55Sderaadt #endif /* SMALL_KERNEL */
2028fc30623Sjason 	}
203e719d43bSjason 
204607827a8Smiod 	if (sc->sc_console)
205aca70fa6Smiod 		fbwscons_console_init(&sc->sc_sunfb, -1);
206bac2f4e5Sjason 
207de9ae89dSmiod 	fbwscons_attach(&sc->sc_sunfb, &creator_accessops, sc->sc_console);
208b9ac102eSmiod 	return;
209b9ac102eSmiod 
210b9ac102eSmiod unmap_fbc:
211b9ac102eSmiod 	bus_space_unmap(sc->sc_bt, sc->sc_fbc_h,
212b9ac102eSmiod 	    ma->ma_reg[FFB_REG_FBC].ur_len);
213b9ac102eSmiod unmap_dfb24:
214b9ac102eSmiod 	bus_space_unmap(sc->sc_bt, sc->sc_pixel_h,
215b9ac102eSmiod 	    ma->ma_reg[FFB_REG_DFB24].ur_len);
216e719d43bSjason }
217e719d43bSjason 
218e719d43bSjason int
creator_ioctl(void * v,u_long cmd,caddr_t data,int flags,struct proc * p)21947c47feaSjsg creator_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
220e719d43bSjason {
221e719d43bSjason 	struct creator_softc *sc = v;
2225193c8f3Sjason 	struct wsdisplay_cursor *curs;
223e719d43bSjason 	struct wsdisplay_fbinfo *wdf;
2245193c8f3Sjason 	struct wsdisplay_curpos *pos;
2255193c8f3Sjason 	u_char r[2], g[2], b[2];
2265193c8f3Sjason 	int error;
227e719d43bSjason 
228e719d43bSjason 	switch (cmd) {
229e719d43bSjason 	case WSDISPLAYIO_GTYPE:
23039bd4da8Sjason 		*(u_int *)data = WSDISPLAY_TYPE_SUNFFB;
231e719d43bSjason 		break;
2326a204d04Sjason 	case WSDISPLAYIO_SMODE:
2336a204d04Sjason 		sc->sc_mode = *(u_int *)data;
23410d10c11Skettenis 		if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
23510d10c11Skettenis 			struct rasops_info *ri = &sc->sc_sunfb.sf_ro;
236e0c3e559Sjsg 			uint32_t attr;
23710d10c11Skettenis 
23810d10c11Skettenis 			if ((sc->sc_sunfb.sf_dev.dv_cfdata->cf_flags &
23910d10c11Skettenis 			    CREATOR_CFFLAG_NOACCEL) == 0)
24010d10c11Skettenis 				creator_ras_init(sc);
24110d10c11Skettenis 
24210d10c11Skettenis 			/* Clear screen. */
243fc223b23Sjsg 			ri->ri_ops.pack_attr(ri,
24410d10c11Skettenis 			    WSCOL_BLACK, WSCOL_WHITE, WSATTR_WSCOLORS, &attr);
24510d10c11Skettenis 			ri->ri_ops.eraserows(ri, 0, ri->ri_rows, attr);
24610d10c11Skettenis 		}
2476a204d04Sjason 		break;
248e719d43bSjason 	case WSDISPLAYIO_GINFO:
249e719d43bSjason 		wdf = (void *)data;
2502628683eSmiod 		wdf->height = sc->sc_sunfb.sf_height;
2512628683eSmiod 		wdf->width  = sc->sc_sunfb.sf_width;
252ad150696Sfgsch 		wdf->depth  = 32;
25363294167Skettenis 		wdf->stride = sc->sc_sunfb.sf_linebytes;
25463294167Skettenis 		wdf->offset = 0;
2552628683eSmiod 		wdf->cmsize = 0;
256e719d43bSjason 		break;
2575daf83aeSkettenis 	case WSDISPLAYIO_GETSUPPORTEDDEPTH:
2585daf83aeSkettenis 		*(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
2595daf83aeSkettenis 		break;
260e719d43bSjason 	case WSDISPLAYIO_LINEBYTES:
2612628683eSmiod 		*(u_int *)data = sc->sc_sunfb.sf_linebytes;
262e719d43bSjason 		break;
2635193c8f3Sjason 	case WSDISPLAYIO_GCURSOR:
2645193c8f3Sjason 		curs = (struct wsdisplay_cursor *)data;
2655193c8f3Sjason 		if (curs->which & WSDISPLAY_CURSOR_DOCUR)
2665193c8f3Sjason 			curs->enable = sc->sc_curs_enabled;
2675193c8f3Sjason 		if (curs->which & WSDISPLAY_CURSOR_DOPOS) {
2685193c8f3Sjason 			curs->pos.x = sc->sc_curs_pos.x;
2695193c8f3Sjason 			curs->pos.y = sc->sc_curs_pos.y;
2705193c8f3Sjason 		}
2715193c8f3Sjason 		if (curs->which & WSDISPLAY_CURSOR_DOHOT) {
2725193c8f3Sjason 			curs->hot.x = sc->sc_curs_hot.x;
2735193c8f3Sjason 			curs->hot.y = sc->sc_curs_hot.y;
2745193c8f3Sjason 		}
2755193c8f3Sjason 		if (curs->which & WSDISPLAY_CURSOR_DOCMAP) {
2765193c8f3Sjason 			curs->cmap.index = 0;
2775193c8f3Sjason 			curs->cmap.count = 2;
2785193c8f3Sjason 			r[0] = sc->sc_curs_fg >> 0;
2795193c8f3Sjason 			g[0] = sc->sc_curs_fg >> 8;
2805193c8f3Sjason 			b[0] = sc->sc_curs_fg >> 16;
2815193c8f3Sjason 			r[1] = sc->sc_curs_bg >> 0;
2825193c8f3Sjason 			g[1] = sc->sc_curs_bg >> 8;
2835193c8f3Sjason 			b[1] = sc->sc_curs_bg >> 16;
2845193c8f3Sjason 			error = copyout(r, curs->cmap.red, sizeof(r));
2855193c8f3Sjason 			if (error)
2865193c8f3Sjason 				return (error);
2875193c8f3Sjason 			error = copyout(g, curs->cmap.green, sizeof(g));
2885193c8f3Sjason 			if (error)
2895193c8f3Sjason 				return (error);
2905193c8f3Sjason 			error = copyout(b, curs->cmap.blue, sizeof(b));
2915193c8f3Sjason 			if (error)
2925193c8f3Sjason 				return (error);
2935193c8f3Sjason 		}
2945193c8f3Sjason 		if (curs->which & WSDISPLAY_CURSOR_DOSHAPE) {
2955193c8f3Sjason 			size_t l;
296e719d43bSjason 
2975193c8f3Sjason 			curs->size.x = sc->sc_curs_size.x;
2985193c8f3Sjason 			curs->size.y = sc->sc_curs_size.y;
2995193c8f3Sjason 			l = (sc->sc_curs_size.x * sc->sc_curs_size.y) / NBBY;
3005193c8f3Sjason 			error = copyout(sc->sc_curs_image, curs->image, l);
3015193c8f3Sjason 			if (error)
3025193c8f3Sjason 				return (error);
3035193c8f3Sjason 			error = copyout(sc->sc_curs_mask, curs->mask, l);
3045193c8f3Sjason 			if (error)
3055193c8f3Sjason 				return (error);
3065193c8f3Sjason 		}
3075193c8f3Sjason 		break;
3085193c8f3Sjason 	case WSDISPLAYIO_SCURPOS:
3095193c8f3Sjason 		pos = (struct wsdisplay_curpos *)data;
3105193c8f3Sjason 		sc->sc_curs_pos.x = pos->x;
3115193c8f3Sjason 		sc->sc_curs_pos.y = pos->y;
3125193c8f3Sjason 		creator_updatecursor(sc, WSDISPLAY_CURSOR_DOPOS);
3135193c8f3Sjason 		break;
3145193c8f3Sjason 	case WSDISPLAYIO_GCURPOS:
3155193c8f3Sjason 		pos = (struct wsdisplay_curpos *)data;
3165193c8f3Sjason 		pos->x = sc->sc_curs_pos.x;
3175193c8f3Sjason 		pos->y = sc->sc_curs_pos.y;
3185193c8f3Sjason 		break;
3195193c8f3Sjason 	case WSDISPLAYIO_SCURSOR:
3205193c8f3Sjason 		curs = (struct wsdisplay_cursor *)data;
3215193c8f3Sjason 		return (creator_setcursor(sc, curs));
3225193c8f3Sjason 	case WSDISPLAYIO_GCURMAX:
3235193c8f3Sjason 		pos = (struct wsdisplay_curpos *)data;
3245193c8f3Sjason 		pos->x = CREATOR_CURS_MAX;
3255193c8f3Sjason 		pos->y = CREATOR_CURS_MAX;
3265193c8f3Sjason 		break;
327e719d43bSjason 	case WSDISPLAYIO_SVIDEO:
328e719d43bSjason 	case WSDISPLAYIO_GVIDEO:
3294231db0aSmiod 		break;
3304231db0aSmiod 
3315193c8f3Sjason 	case WSDISPLAYIO_GETCMAP:
3325193c8f3Sjason 	case WSDISPLAYIO_PUTCMAP:
333e719d43bSjason 	default:
334e719d43bSjason 		return -1; /* not supported yet */
335e719d43bSjason         }
336e719d43bSjason 
337e719d43bSjason 	return (0);
338e719d43bSjason }
339e719d43bSjason 
340e719d43bSjason int
creator_setcursor(struct creator_softc * sc,struct wsdisplay_cursor * curs)3415193c8f3Sjason creator_setcursor(struct creator_softc *sc, struct wsdisplay_cursor *curs)
3425193c8f3Sjason {
3435193c8f3Sjason 	u_int8_t r[2], g[2], b[2], image[128], mask[128];
3445193c8f3Sjason 	int error;
3455193c8f3Sjason 	size_t imcount;
3465193c8f3Sjason 
3475193c8f3Sjason 	/*
3485193c8f3Sjason 	 * Do stuff that can generate errors first, then we'll blast it
3495193c8f3Sjason 	 * all at once.
3505193c8f3Sjason 	 */
3515193c8f3Sjason 	if (curs->which & WSDISPLAY_CURSOR_DOCMAP) {
3525193c8f3Sjason 		if (curs->cmap.count < 2)
3535193c8f3Sjason 			return (EINVAL);
3545193c8f3Sjason 		error = copyin(curs->cmap.red, r, sizeof(r));
3555193c8f3Sjason 		if (error)
3565193c8f3Sjason 			return (error);
3575193c8f3Sjason 		error = copyin(curs->cmap.green, g, sizeof(g));
3585193c8f3Sjason 		if (error)
3595193c8f3Sjason 			return (error);
3605193c8f3Sjason 		error = copyin(curs->cmap.blue, b, sizeof(b));
3615193c8f3Sjason 		if (error)
3625193c8f3Sjason 			return (error);
3635193c8f3Sjason 	}
3645193c8f3Sjason 
3655193c8f3Sjason 	if (curs->which & WSDISPLAY_CURSOR_DOSHAPE) {
3665193c8f3Sjason 		if (curs->size.x > CREATOR_CURS_MAX ||
3675193c8f3Sjason 		    curs->size.y > CREATOR_CURS_MAX)
3685193c8f3Sjason 			return (EINVAL);
3695193c8f3Sjason 		imcount = (curs->size.x * curs->size.y) / NBBY;
3705193c8f3Sjason 		error = copyin(curs->image, image, imcount);
3715193c8f3Sjason 		if (error)
3725193c8f3Sjason 			return (error);
3735193c8f3Sjason 		error = copyin(curs->mask, mask, imcount);
3745193c8f3Sjason 		if (error)
3755193c8f3Sjason 			return (error);
3765193c8f3Sjason 	}
3775193c8f3Sjason 
3785193c8f3Sjason 	/*
3795193c8f3Sjason 	 * Ok, everything is in kernel space and sane, update state.
3805193c8f3Sjason 	 */
3815193c8f3Sjason 
3825193c8f3Sjason 	if (curs->which & WSDISPLAY_CURSOR_DOCUR)
3835193c8f3Sjason 		sc->sc_curs_enabled = curs->enable;
3845193c8f3Sjason 	if (curs->which & WSDISPLAY_CURSOR_DOPOS) {
3855193c8f3Sjason 		sc->sc_curs_pos.x = curs->pos.x;
3865193c8f3Sjason 		sc->sc_curs_pos.y = curs->pos.y;
3875193c8f3Sjason 	}
3885193c8f3Sjason 	if (curs->which & WSDISPLAY_CURSOR_DOHOT) {
3895193c8f3Sjason 		sc->sc_curs_hot.x = curs->hot.x;
3905193c8f3Sjason 		sc->sc_curs_hot.y = curs->hot.y;
3915193c8f3Sjason 	}
3925193c8f3Sjason 	if (curs->which & WSDISPLAY_CURSOR_DOCMAP) {
3935193c8f3Sjason 		sc->sc_curs_fg = ((r[0] << 0) | (g[0] << 8) | (b[0] << 16));
3945193c8f3Sjason 		sc->sc_curs_bg = ((r[1] << 0) | (g[1] << 8) | (b[1] << 16));
3955193c8f3Sjason 	}
3965193c8f3Sjason 	if (curs->which & WSDISPLAY_CURSOR_DOSHAPE) {
3975193c8f3Sjason 		sc->sc_curs_size.x = curs->size.x;
3985193c8f3Sjason 		sc->sc_curs_size.y = curs->size.y;
3995193c8f3Sjason 		bcopy(image, sc->sc_curs_image, imcount);
4005193c8f3Sjason 		bcopy(mask, sc->sc_curs_mask, imcount);
4015193c8f3Sjason 	}
4025193c8f3Sjason 
4035193c8f3Sjason 	creator_updatecursor(sc, curs->which);
4045193c8f3Sjason 
4055193c8f3Sjason 	return (0);
4065193c8f3Sjason }
4075193c8f3Sjason 
4085193c8f3Sjason void
creator_curs_enable(struct creator_softc * sc,u_int ena)4095193c8f3Sjason creator_curs_enable(struct creator_softc *sc, u_int ena)
4105193c8f3Sjason {
4115193c8f3Sjason 	u_int32_t v;
4125193c8f3Sjason 
4135193c8f3Sjason 	DAC_WRITE(sc, FFB_DAC_TYPE2, DAC_TYPE2_CURSENAB);
4145193c8f3Sjason 	if (sc->sc_dacrev <= 2)
4155193c8f3Sjason 		v = ena ? 3 : 0;
4165193c8f3Sjason 	else
4175193c8f3Sjason 		v = ena ? 0 : 3;
4185193c8f3Sjason 	DAC_WRITE(sc, FFB_DAC_VALUE2, v);
4195193c8f3Sjason }
4205193c8f3Sjason 
4215193c8f3Sjason int
creator_updatecursor(struct creator_softc * sc,u_int which)4225193c8f3Sjason creator_updatecursor(struct creator_softc *sc, u_int which)
4235193c8f3Sjason {
4245193c8f3Sjason 	creator_curs_enable(sc, 0);
4255193c8f3Sjason 
4265193c8f3Sjason 	if (which & WSDISPLAY_CURSOR_DOCMAP) {
4275193c8f3Sjason 		DAC_WRITE(sc, FFB_DAC_TYPE2, DAC_TYPE2_CURSCMAP);
4285193c8f3Sjason 		DAC_WRITE(sc, FFB_DAC_VALUE2, sc->sc_curs_fg);
4295193c8f3Sjason 		DAC_WRITE(sc, FFB_DAC_VALUE2, sc->sc_curs_bg);
4305193c8f3Sjason 	}
4315193c8f3Sjason 
4325193c8f3Sjason 	if (which & (WSDISPLAY_CURSOR_DOPOS | WSDISPLAY_CURSOR_DOHOT)) {
4335193c8f3Sjason 		u_int32_t x, y;
4345193c8f3Sjason 
4355193c8f3Sjason 		x = sc->sc_curs_pos.x + CREATOR_CURS_MAX - sc->sc_curs_hot.x;
4365193c8f3Sjason 		y = sc->sc_curs_pos.y + CREATOR_CURS_MAX - sc->sc_curs_hot.y;
4375193c8f3Sjason 		DAC_WRITE(sc, FFB_DAC_TYPE2, DAC_TYPE2_CURSPOS);
4385193c8f3Sjason 		DAC_WRITE(sc, FFB_DAC_VALUE2,
4395193c8f3Sjason 		    ((x & 0xffff) << 16) | (y & 0xffff));
4405193c8f3Sjason 	}
4415193c8f3Sjason 
4425193c8f3Sjason 	if (which & WSDISPLAY_CURSOR_DOCUR)
4435193c8f3Sjason 		creator_curs_enable(sc, sc->sc_curs_enabled);
4445193c8f3Sjason 
4455193c8f3Sjason 	return (0);
4465193c8f3Sjason }
4475193c8f3Sjason 
4485193c8f3Sjason const struct creator_mappings {
4495193c8f3Sjason 	bus_addr_t uoff;
4505193c8f3Sjason 	bus_addr_t poff;
4515193c8f3Sjason 	bus_size_t ulen;
4525193c8f3Sjason } creator_map[] = {
4535193c8f3Sjason 	{ FFB_VOFF_SFB8R, FFB_POFF_SFB8R, FFB_VLEN_SFB8R },
4545193c8f3Sjason 	{ FFB_VOFF_SFB8G, FFB_POFF_SFB8G, FFB_VLEN_SFB8G },
4555193c8f3Sjason 	{ FFB_VOFF_SFB8B, FFB_POFF_SFB8B, FFB_VLEN_SFB8B },
4565193c8f3Sjason 	{ FFB_VOFF_SFB8X, FFB_POFF_SFB8X, FFB_VLEN_SFB8X },
4575193c8f3Sjason 	{ FFB_VOFF_SFB32, FFB_POFF_SFB32, FFB_VLEN_SFB32 },
4585193c8f3Sjason 	{ FFB_VOFF_SFB64, FFB_POFF_SFB64, FFB_VLEN_SFB64 },
4595193c8f3Sjason 	{ FFB_VOFF_FBC_REGS, FFB_POFF_FBC_REGS, FFB_VLEN_FBC_REGS },
4605193c8f3Sjason 	{ FFB_VOFF_BM_FBC_REGS, FFB_POFF_BM_FBC_REGS, FFB_VLEN_BM_FBC_REGS },
4615193c8f3Sjason 	{ FFB_VOFF_DFB8R, FFB_POFF_DFB8R, FFB_VLEN_DFB8R },
4625193c8f3Sjason 	{ FFB_VOFF_DFB8G, FFB_POFF_DFB8G, FFB_VLEN_DFB8G },
4635193c8f3Sjason 	{ FFB_VOFF_DFB8B, FFB_POFF_DFB8B, FFB_VLEN_DFB8B },
4645193c8f3Sjason 	{ FFB_VOFF_DFB8X, FFB_POFF_DFB8X, FFB_VLEN_DFB8X },
4655193c8f3Sjason 	{ FFB_VOFF_DFB24, FFB_POFF_DFB24, FFB_VLEN_DFB24 },
4665193c8f3Sjason 	{ FFB_VOFF_DFB32, FFB_POFF_DFB32, FFB_VLEN_DFB32 },
4675193c8f3Sjason 	{ FFB_VOFF_DFB422A, FFB_POFF_DFB422A, FFB_VLEN_DFB422A },
4685193c8f3Sjason 	{ FFB_VOFF_DFB422AD, FFB_POFF_DFB422AD, FFB_VLEN_DFB422AD },
4695193c8f3Sjason 	{ FFB_VOFF_DFB24B, FFB_POFF_DFB24B, FFB_VLEN_DFB24B },
4705193c8f3Sjason 	{ FFB_VOFF_DFB422B, FFB_POFF_DFB422B, FFB_VLEN_DFB422B },
4715193c8f3Sjason 	{ FFB_VOFF_DFB422BD, FFB_POFF_DFB422BD, FFB_VLEN_DFB422BD },
4725193c8f3Sjason 	{ FFB_VOFF_SFB16Z, FFB_POFF_SFB16Z, FFB_VLEN_SFB16Z },
4735193c8f3Sjason 	{ FFB_VOFF_SFB8Z, FFB_POFF_SFB8Z, FFB_VLEN_SFB8Z },
4745193c8f3Sjason 	{ FFB_VOFF_SFB422, FFB_POFF_SFB422, FFB_VLEN_SFB422 },
4755193c8f3Sjason 	{ FFB_VOFF_SFB422D, FFB_POFF_SFB422D, FFB_VLEN_SFB422D },
4765193c8f3Sjason 	{ FFB_VOFF_FBC_KREGS, FFB_POFF_FBC_KREGS, FFB_VLEN_FBC_KREGS },
4775193c8f3Sjason 	{ FFB_VOFF_DAC, FFB_POFF_DAC, FFB_VLEN_DAC },
4785193c8f3Sjason 	{ FFB_VOFF_PROM, FFB_POFF_PROM, FFB_VLEN_PROM },
4795193c8f3Sjason 	{ FFB_VOFF_EXP, FFB_POFF_EXP, FFB_VLEN_EXP },
4805193c8f3Sjason };
481f9e6718aSjasper #define	CREATOR_NMAPPINGS       nitems(creator_map)
4825193c8f3Sjason 
483e719d43bSjason paddr_t
creator_mmap(void * vsc,off_t off,int prot)48447c47feaSjsg creator_mmap(void *vsc, off_t off, int prot)
485e719d43bSjason {
4865193c8f3Sjason 	paddr_t x;
487e719d43bSjason 	struct creator_softc *sc = vsc;
488e719d43bSjason 	int i;
489e719d43bSjason 
4906a204d04Sjason 	switch (sc->sc_mode) {
4916a204d04Sjason 	case WSDISPLAYIO_MODE_MAPPED:
4925193c8f3Sjason 		/* Turn virtual offset into physical offset */
4935193c8f3Sjason 		for (i = 0; i < CREATOR_NMAPPINGS; i++) {
4945193c8f3Sjason 			if (off >= creator_map[i].uoff &&
4955193c8f3Sjason 			    off < (creator_map[i].uoff + creator_map[i].ulen))
4965193c8f3Sjason 				break;
4975193c8f3Sjason 		}
4985decc9e4Sjason 		if (i == CREATOR_NMAPPINGS)
4995193c8f3Sjason 			break;
5005193c8f3Sjason 
5015193c8f3Sjason 		off -= creator_map[i].uoff;
5025193c8f3Sjason 		off += creator_map[i].poff;
5035193c8f3Sjason 		off += sc->sc_addrs[0];
5045193c8f3Sjason 
5055193c8f3Sjason 		/* Map based on physical offset */
506020ef9e4Sjason 		for (i = 0; i < sc->sc_nreg; i++) {
507e719d43bSjason 			/* Before this set? */
508e719d43bSjason 			if (off < sc->sc_addrs[i])
509e719d43bSjason 				continue;
510e719d43bSjason 			/* After this set? */
511e719d43bSjason 			if (off >= (sc->sc_addrs[i] + sc->sc_sizes[i]))
512e719d43bSjason 				continue;
513e719d43bSjason 
5145193c8f3Sjason 			x = bus_space_mmap(sc->sc_bt, 0, off, prot,
5155193c8f3Sjason 			    BUS_SPACE_MAP_LINEAR);
5165193c8f3Sjason 			return (x);
517e719d43bSjason 		}
5186a204d04Sjason 		break;
5196a204d04Sjason 	case WSDISPLAYIO_MODE_DUMBFB:
5204848fbc9Smiod 		if (sc->sc_nreg <= FFB_REG_DFB24)
5216a204d04Sjason 			break;
5226a204d04Sjason 		if (off >= 0 && off < sc->sc_sizes[FFB_REG_DFB24])
5236a204d04Sjason 			return (bus_space_mmap(sc->sc_bt,
5246a204d04Sjason 			    sc->sc_addrs[FFB_REG_DFB24], off, prot,
5256a204d04Sjason 			    BUS_SPACE_MAP_LINEAR));
5266a204d04Sjason 		break;
5276a204d04Sjason 	}
528e719d43bSjason 
529e719d43bSjason 	return (-1);
530e719d43bSjason }
531e719d43bSjason 
53224da7ca2Sjason void
creator_ras_fifo_wait(struct creator_softc * sc,int n)53347c47feaSjsg creator_ras_fifo_wait(struct creator_softc *sc, int n)
53424da7ca2Sjason {
53524da7ca2Sjason 	int32_t cache = sc->sc_fifo_cache;
53624da7ca2Sjason 
53724da7ca2Sjason 	if (cache < n) {
53824da7ca2Sjason 		do {
53924da7ca2Sjason 			cache = FBC_READ(sc, FFB_FBC_UCSR);
54024da7ca2Sjason 			cache = (cache & FBC_UCSR_FIFO_MASK) - 8;
54124da7ca2Sjason 		} while (cache < n);
54224da7ca2Sjason 	}
54324da7ca2Sjason 	sc->sc_fifo_cache = cache - n;
54424da7ca2Sjason }
54524da7ca2Sjason 
54624da7ca2Sjason void
creator_ras_wait(struct creator_softc * sc)54747c47feaSjsg creator_ras_wait(struct creator_softc *sc)
54824da7ca2Sjason {
54924da7ca2Sjason 	u_int32_t ucsr, r;
55024da7ca2Sjason 
55124da7ca2Sjason 	while (1) {
55224da7ca2Sjason 		ucsr = FBC_READ(sc, FFB_FBC_UCSR);
55324da7ca2Sjason 		if ((ucsr & (FBC_UCSR_FB_BUSY|FBC_UCSR_RP_BUSY)) == 0)
55424da7ca2Sjason 			break;
555bf2654a9Sjason 		r = ucsr & (FBC_UCSR_READ_ERR | FBC_UCSR_FIFO_OVFL);
55624da7ca2Sjason 		if (r != 0)
55724da7ca2Sjason 			FBC_WRITE(sc, FFB_FBC_UCSR, r);
55824da7ca2Sjason 	}
55924da7ca2Sjason }
560bf2654a9Sjason 
561bf2654a9Sjason void
creator_ras_init(struct creator_softc * sc)56247c47feaSjsg creator_ras_init(struct creator_softc *sc)
563bf2654a9Sjason {
564d7a30e89Sjason 	creator_ras_fifo_wait(sc, 7);
565d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_PPC,
566d7a30e89Sjason 	    FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE |
567d7a30e89Sjason 	    FBC_PPC_APE_DIS | FBC_PPC_CS_CONST);
568d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_FBC,
569d7a30e89Sjason 	    FFB_FBC_WB_A | FFB_FBC_RB_A | FFB_FBC_SB_BOTH |
570d7a30e89Sjason 	    FFB_FBC_XE_OFF | FFB_FBC_RGBE_MASK);
571d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_NEW);
572d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_RECTANGLE);
573d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_PMASK, 0xffffffff);
574d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_FONTINC, 0x10000);
575d7a30e89Sjason 	sc->sc_fg_cache = 0;
576d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_FG, sc->sc_fg_cache);
577d7a30e89Sjason 	creator_ras_wait(sc);
578bf2654a9Sjason }
579bf2654a9Sjason 
580072953e3Smiod int
creator_ras_eraserows(void * cookie,int row,int n,uint32_t attr)58147c47feaSjsg creator_ras_eraserows(void *cookie, int row, int n, uint32_t attr)
582bf2654a9Sjason {
583bf2654a9Sjason 	struct rasops_info *ri = cookie;
584bf2654a9Sjason 	struct creator_softc *sc = ri->ri_hw;
58533877efbSmiod 	int bg, fg;
586bf2654a9Sjason 
587bf2654a9Sjason 	if (row < 0) {
588bf2654a9Sjason 		n += row;
589bf2654a9Sjason 		row = 0;
590bf2654a9Sjason 	}
591bf2654a9Sjason 	if (row + n > ri->ri_rows)
592bf2654a9Sjason 		n = ri->ri_rows - row;
593bf2654a9Sjason 	if (n <= 0)
594072953e3Smiod 		return 0;
595bf2654a9Sjason 
59633877efbSmiod 	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
597bf2654a9Sjason 	creator_ras_fill(sc);
59833877efbSmiod 	creator_ras_setfg(sc, ri->ri_devcmap[bg]);
599d7a30e89Sjason 	creator_ras_fifo_wait(sc, 4);
600bf2654a9Sjason 	if ((n == ri->ri_rows) && (ri->ri_flg & RI_FULLCLEAR)) {
601bf2654a9Sjason 		FBC_WRITE(sc, FFB_FBC_BY, 0);
602bf2654a9Sjason 		FBC_WRITE(sc, FFB_FBC_BX, 0);
603bf2654a9Sjason 		FBC_WRITE(sc, FFB_FBC_BH, ri->ri_height);
604bf2654a9Sjason 		FBC_WRITE(sc, FFB_FBC_BW, ri->ri_width);
605bf2654a9Sjason 	} else {
606bf2654a9Sjason 		row *= ri->ri_font->fontheight;
607bf2654a9Sjason 		FBC_WRITE(sc, FFB_FBC_BY, ri->ri_yorigin + row);
608bf2654a9Sjason 		FBC_WRITE(sc, FFB_FBC_BX, ri->ri_xorigin);
609bf2654a9Sjason 		FBC_WRITE(sc, FFB_FBC_BH, n * ri->ri_font->fontheight);
610bf2654a9Sjason 		FBC_WRITE(sc, FFB_FBC_BW, ri->ri_emuwidth);
611bf2654a9Sjason 	}
612bf2654a9Sjason 	creator_ras_wait(sc);
613072953e3Smiod 
614072953e3Smiod 	return 0;
615bf2654a9Sjason }
616bf2654a9Sjason 
617072953e3Smiod int
creator_ras_erasecols(void * cookie,int row,int col,int n,uint32_t attr)61847c47feaSjsg creator_ras_erasecols(void *cookie, int row, int col, int n, uint32_t attr)
619bf2654a9Sjason {
620bf2654a9Sjason 	struct rasops_info *ri = cookie;
621bf2654a9Sjason 	struct creator_softc *sc = ri->ri_hw;
62233877efbSmiod 	int fg, bg;
623bf2654a9Sjason 
624bf2654a9Sjason 	if ((row < 0) || (row >= ri->ri_rows))
625072953e3Smiod 		return 0;
626bf2654a9Sjason 	if (col < 0) {
627bf2654a9Sjason 		n += col;
628bf2654a9Sjason 		col = 0;
629bf2654a9Sjason 	}
630bf2654a9Sjason 	if (col + n > ri->ri_cols)
631bf2654a9Sjason 		n = ri->ri_cols - col;
632bf2654a9Sjason 	if (n <= 0)
633072953e3Smiod 		return 0;
634bf2654a9Sjason 	n *= ri->ri_font->fontwidth;
635bf2654a9Sjason 	col *= ri->ri_font->fontwidth;
636bf2654a9Sjason 	row *= ri->ri_font->fontheight;
637bf2654a9Sjason 
63833877efbSmiod 	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
639bf2654a9Sjason 	creator_ras_fill(sc);
64033877efbSmiod 	creator_ras_setfg(sc, ri->ri_devcmap[bg]);
641d7a30e89Sjason 	creator_ras_fifo_wait(sc, 4);
642bf2654a9Sjason 	FBC_WRITE(sc, FFB_FBC_BY, ri->ri_yorigin + row);
643bf2654a9Sjason 	FBC_WRITE(sc, FFB_FBC_BX, ri->ri_xorigin + col);
644bf2654a9Sjason 	FBC_WRITE(sc, FFB_FBC_BH, ri->ri_font->fontheight);
645bf2654a9Sjason 	FBC_WRITE(sc, FFB_FBC_BW, n - 1);
646bf2654a9Sjason 	creator_ras_wait(sc);
647072953e3Smiod 
648072953e3Smiod 	return 0;
649bf2654a9Sjason }
650bf2654a9Sjason 
651bf2654a9Sjason void
creator_ras_fill(struct creator_softc * sc)65247c47feaSjsg creator_ras_fill(struct creator_softc *sc)
653bf2654a9Sjason {
654b95e1facSjason 	creator_ras_fifo_wait(sc, 2);
655b95e1facSjason 	FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_NEW);
656bf2654a9Sjason 	FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_RECTANGLE);
657d7a30e89Sjason 	creator_ras_wait(sc);
658d7a30e89Sjason }
659d7a30e89Sjason 
660072953e3Smiod int
creator_ras_copyrows(void * cookie,int src,int dst,int n)66147c47feaSjsg creator_ras_copyrows(void *cookie, int src, int dst, int n)
662d7a30e89Sjason {
663d7a30e89Sjason 	struct rasops_info *ri = cookie;
664d7a30e89Sjason 	struct creator_softc *sc = ri->ri_hw;
665d7a30e89Sjason 
666d7a30e89Sjason 	if (dst == src)
667072953e3Smiod 		return 0;
668d7a30e89Sjason 	if (src < 0) {
669d7a30e89Sjason 		n += src;
670d7a30e89Sjason 		src = 0;
671d7a30e89Sjason 	}
672d7a30e89Sjason 	if ((src + n) > ri->ri_rows)
673d7a30e89Sjason 		n = ri->ri_rows - src;
674d7a30e89Sjason 	if (dst < 0) {
675d7a30e89Sjason 		n += dst;
676d7a30e89Sjason 		dst = 0;
677d7a30e89Sjason 	}
678d7a30e89Sjason 	if ((dst + n) > ri->ri_rows)
679d7a30e89Sjason 		n = ri->ri_rows - dst;
680d7a30e89Sjason 	if (n <= 0)
681072953e3Smiod 		return 0;
682d7a30e89Sjason 	n *= ri->ri_font->fontheight;
683d7a30e89Sjason 	src *= ri->ri_font->fontheight;
684d7a30e89Sjason 	dst *= ri->ri_font->fontheight;
685d7a30e89Sjason 
686b95e1facSjason 	creator_ras_fifo_wait(sc, 8);
687b95e1facSjason 	FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_OLD | (FBC_ROP_OLD << 8));
688d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_VSCROLL);
689d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_BY, ri->ri_yorigin + src);
690d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_BX, ri->ri_xorigin);
691d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_DY, ri->ri_yorigin + dst);
692d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_DX, ri->ri_xorigin);
693d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_BH, n);
694d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_BW, ri->ri_emuwidth);
695d7a30e89Sjason 	creator_ras_wait(sc);
696072953e3Smiod 
697072953e3Smiod 	return 0;
698d7a30e89Sjason }
699d7a30e89Sjason 
700d7a30e89Sjason void
creator_ras_setfg(struct creator_softc * sc,int32_t fg)70147c47feaSjsg creator_ras_setfg(struct creator_softc *sc, int32_t fg)
702d7a30e89Sjason {
703d7a30e89Sjason 	creator_ras_fifo_wait(sc, 1);
704d7a30e89Sjason 	if (fg == sc->sc_fg_cache)
705d7a30e89Sjason 		return;
706d7a30e89Sjason 	sc->sc_fg_cache = fg;
707d7a30e89Sjason 	FBC_WRITE(sc, FFB_FBC_FG, fg);
708bf2654a9Sjason 	creator_ras_wait(sc);
709bf2654a9Sjason }
710b697b291Skettenis 
7118e79cc55Sderaadt #ifndef SMALL_KERNEL
712b697b291Skettenis struct creator_firmware {
713b697b291Skettenis 	char		fw_ident[8];
714b697b291Skettenis 	u_int32_t	fw_size;
715b697b291Skettenis 	u_int32_t	fw_reserved[2];
716b697b291Skettenis 	u_int32_t	fw_ucode[0];
717b697b291Skettenis };
718b697b291Skettenis 
719b697b291Skettenis #define CREATOR_FIRMWARE_REV	0x101
720b697b291Skettenis 
721b697b291Skettenis void
creator_load_firmware(struct device * self)722ef89f9e6Smpi creator_load_firmware(struct device *self)
723b697b291Skettenis {
724ef89f9e6Smpi 	struct creator_softc *sc = (struct creator_softc *)self;
725b697b291Skettenis 	struct creator_firmware *fw;
726b697b291Skettenis 	u_int32_t ascr;
727b697b291Skettenis 	size_t buflen;
728b697b291Skettenis 	u_char *buf;
729b697b291Skettenis 	int error;
730b697b291Skettenis 
731b697b291Skettenis 	error = loadfirmware("afb", &buf, &buflen);
732b697b291Skettenis 	if (error) {
733b697b291Skettenis 		printf("%s: error %d, could not read firmware %s\n",
734b697b291Skettenis 		       sc->sc_sunfb.sf_dev.dv_xname, error, "afb");
735b697b291Skettenis 		return;
736b697b291Skettenis 	}
737b697b291Skettenis 
738b697b291Skettenis 	fw = (struct creator_firmware *)buf;
739b697b291Skettenis 	if (sizeof(*fw) > buflen ||
740b697b291Skettenis 	    fw->fw_size * sizeof(u_int32_t) > (buflen - sizeof(*fw))) {
741b697b291Skettenis 		printf("%s: corrupt firmware\n", sc->sc_sunfb.sf_dev.dv_xname);
742f8e6c425Stedu 		free(buf, M_DEVBUF, 0);
743b697b291Skettenis 		return;
744b697b291Skettenis 	}
745b697b291Skettenis 
746b697b291Skettenis 	printf("%s: firmware rev %d.%d.%d\n", sc->sc_sunfb.sf_dev.dv_xname,
747b697b291Skettenis 	       (fw->fw_ucode[CREATOR_FIRMWARE_REV] >> 16) & 0xff,
748b697b291Skettenis 	       (fw->fw_ucode[CREATOR_FIRMWARE_REV] >> 8) & 0xff,
749b697b291Skettenis 	       fw->fw_ucode[CREATOR_FIRMWARE_REV] & 0xff);
750b697b291Skettenis 
751b697b291Skettenis 	ascr = FBC_READ(sc, FFB_FBC_ASCR);
752b697b291Skettenis 
753b697b291Skettenis 	/* Stop all floats. */
754b697b291Skettenis 	FBC_WRITE(sc, FFB_FBC_FEM, ascr & 0x3f);
755b697b291Skettenis 	FBC_WRITE(sc, FFB_FBC_ASCR, FBC_ASCR_STOP);
756b697b291Skettenis 
757b697b291Skettenis 	creator_ras_wait(sc);
758b697b291Skettenis 
759b697b291Skettenis 	/* Load firmware into all secondary floats. */
760b697b291Skettenis 	if (ascr & 0x3e) {
761b697b291Skettenis 		FBC_WRITE(sc, FFB_FBC_FEM, ascr & 0x3e);
762b697b291Skettenis 		creator_load_sram(sc, fw->fw_ucode, fw->fw_size);
763b697b291Skettenis 	}
764b697b291Skettenis 
765b697b291Skettenis 	/* Load firmware into primary float. */
766b697b291Skettenis 	FBC_WRITE(sc, FFB_FBC_FEM, ascr & 0x01);
767b697b291Skettenis 	creator_load_sram(sc, fw->fw_ucode, fw->fw_size);
768b697b291Skettenis 
769b697b291Skettenis 	/* Restart all floats. */
770b697b291Skettenis 	FBC_WRITE(sc, FFB_FBC_FEM, ascr & 0x3f);
771b697b291Skettenis 	FBC_WRITE(sc, FFB_FBC_ASCR, FBC_ASCR_RESTART);
772b697b291Skettenis 
773b697b291Skettenis 	creator_ras_wait(sc);
774b697b291Skettenis 
775f8e6c425Stedu 	free(buf, M_DEVBUF, 0);
776b697b291Skettenis }
7778e79cc55Sderaadt #endif /* SMALL_KERNEL */
778b697b291Skettenis 
779b697b291Skettenis void
creator_load_sram(struct creator_softc * sc,u_int32_t * ucode,u_int32_t size)780b697b291Skettenis creator_load_sram(struct creator_softc *sc, u_int32_t *ucode, u_int32_t size)
781b697b291Skettenis {
782b697b291Skettenis 	uint64_t pstate, fprs;
783b697b291Skettenis 	caddr_t sram;
784b697b291Skettenis 
785b697b291Skettenis 	sram = bus_space_vaddr(sc->sc_bt, sc->sc_fbc_h) + FFB_FBC_SRAM36;
786b697b291Skettenis 
787b697b291Skettenis 	/*
788b697b291Skettenis 	 * Apparently, loading the firmware into SRAM needs to be done
789b697b291Skettenis 	 * using block copies.  And block copies use the
790b697b291Skettenis 	 * floating-point registers.  Generally, using the FPU in the
791b697b291Skettenis 	 * kernel is verboten.  But since we load the firmware before
792b697b291Skettenis 	 * userland processes are started, thrashing the
793b697b291Skettenis 	 * floating-point registers is fine.  We do need to enable the
794b697b291Skettenis 	 * FPU before we access them though, otherwise we'll trap.
795b697b291Skettenis 	 */
796b697b291Skettenis 	pstate = sparc_rdpr(pstate);
797b697b291Skettenis 	sparc_wrpr(pstate, pstate | PSTATE_PEF, 0);
798b697b291Skettenis 	fprs = sparc_rd(fprs);
799b697b291Skettenis 	sparc_wr(fprs, FPRS_FEF, 0);
800b697b291Skettenis 
801b697b291Skettenis 	FBC_WRITE(sc, FFB_FBC_SRAMAR, 0);
802b697b291Skettenis 
803b697b291Skettenis 	while (size > 0) {
804b697b291Skettenis 		creator_ras_fifo_wait(sc, 16);
805b697b291Skettenis 
8062df76cc2Sguenther 		__asm__ volatile("ld	[%0 + 0x00], %%f1\n\t"
807b697b291Skettenis 				     "ld	[%0 + 0x04], %%f0\n\t"
808b697b291Skettenis 				     "ld	[%0 + 0x08], %%f3\n\t"
809b697b291Skettenis 				     "ld	[%0 + 0x0c], %%f2\n\t"
810b697b291Skettenis 				     "ld	[%0 + 0x10], %%f5\n\t"
811b697b291Skettenis 				     "ld	[%0 + 0x14], %%f4\n\t"
812b697b291Skettenis 				     "ld	[%0 + 0x18], %%f7\n\t"
813b697b291Skettenis 				     "ld	[%0 + 0x1c], %%f6\n\t"
814b697b291Skettenis 				     "ld	[%0 + 0x20], %%f9\n\t"
815b697b291Skettenis 				     "ld	[%0 + 0x24], %%f8\n\t"
816b697b291Skettenis 				     "ld	[%0 + 0x28], %%f11\n\t"
817b697b291Skettenis 				     "ld	[%0 + 0x2c], %%f10\n\t"
818b697b291Skettenis 				     "ld	[%0 + 0x30], %%f13\n\t"
819b697b291Skettenis 				     "ld	[%0 + 0x34], %%f12\n\t"
820b697b291Skettenis 				     "ld	[%0 + 0x38], %%f15\n\t"
821b697b291Skettenis 				     "ld	[%0 + 0x3c], %%f14\n\t"
822b697b291Skettenis 				     "membar	#Sync\n\t"
823b697b291Skettenis 				     "stda	%%f0, [%1] 240\n\t"
824b697b291Skettenis 				     "membar	#Sync"
825b697b291Skettenis 				     : : "r" (ucode), "r" (sram));
826b697b291Skettenis 
827b697b291Skettenis 		ucode += 16;
828b697b291Skettenis 		size -= 16;
829b697b291Skettenis 	}
830b697b291Skettenis 
831b697b291Skettenis 	sparc_wr(fprs, fprs, 0);
832b697b291Skettenis 	sparc_wrpr(pstate, pstate, 0);
833b697b291Skettenis 
834b697b291Skettenis 	creator_ras_wait(sc);
835b697b291Skettenis }
836