xref: /netbsd-src/sys/arch/sparc64/dev/ffb.c (revision 1dc652ef5a0bffbd0917f95e0797bad8c6fc8efd)
1*1dc652efSthorpej /*	$NetBSD: ffb.c,v 1.68 2023/12/20 05:33:58 thorpej Exp $	*/
249f3f8ccSpetrov /*	$OpenBSD: creator.c,v 1.20 2002/07/30 19:48:15 jason Exp $	*/
349f3f8ccSpetrov 
449f3f8ccSpetrov /*
549f3f8ccSpetrov  * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
649f3f8ccSpetrov  * All rights reserved.
749f3f8ccSpetrov  *
849f3f8ccSpetrov  * Redistribution and use in source and binary forms, with or without
949f3f8ccSpetrov  * modification, are permitted provided that the following conditions
1049f3f8ccSpetrov  * are met:
1149f3f8ccSpetrov  * 1. Redistributions of source code must retain the above copyright
1249f3f8ccSpetrov  *    notice, this list of conditions and the following disclaimer.
1349f3f8ccSpetrov  * 2. Redistributions in binary form must reproduce the above copyright
1449f3f8ccSpetrov  *    notice, this list of conditions and the following disclaimer in the
1549f3f8ccSpetrov  *    documentation and/or other materials provided with the distribution.
1649f3f8ccSpetrov  * 3. All advertising materials mentioning features or use of this software
1749f3f8ccSpetrov  *    must display the following acknowledgement:
1849f3f8ccSpetrov  *	This product includes software developed by Jason L. Wright
1949f3f8ccSpetrov  * 4. The name of the author may not be used to endorse or promote products
2049f3f8ccSpetrov  *    derived from this software without specific prior written permission.
2149f3f8ccSpetrov  *
2249f3f8ccSpetrov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2349f3f8ccSpetrov  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2449f3f8ccSpetrov  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2549f3f8ccSpetrov  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2649f3f8ccSpetrov  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2749f3f8ccSpetrov  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2849f3f8ccSpetrov  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2949f3f8ccSpetrov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3049f3f8ccSpetrov  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
3149f3f8ccSpetrov  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3249f3f8ccSpetrov  * POSSIBILITY OF SUCH DAMAGE.
3349f3f8ccSpetrov  */
3449f3f8ccSpetrov 
35ed517291Slukem #include <sys/cdefs.h>
36*1dc652efSthorpej __KERNEL_RCSID(0, "$NetBSD: ffb.c,v 1.68 2023/12/20 05:33:58 thorpej Exp $");
37ed517291Slukem 
3849f3f8ccSpetrov #include <sys/types.h>
3949f3f8ccSpetrov #include <sys/param.h>
4049f3f8ccSpetrov #include <sys/systm.h>
4149f3f8ccSpetrov #include <sys/kernel.h>
4249f3f8ccSpetrov #include <sys/device.h>
4349f3f8ccSpetrov #include <sys/conf.h>
44d2c9943bSmacallan #include <sys/ioctl.h>
45d2c9943bSmacallan #include <sys/mman.h>
4649f3f8ccSpetrov 
47b6584574Sdyoung #include <sys/bus.h>
4849f3f8ccSpetrov #include <machine/autoconf.h>
4949f3f8ccSpetrov #include <machine/openfirm.h>
5021d2a323Smartin #include <machine/vmparam.h>
5149f3f8ccSpetrov 
5249f3f8ccSpetrov #include <dev/wscons/wsconsio.h>
5321d2a323Smartin #include <dev/sun/fbio.h>
5421d2a323Smartin #include <dev/sun/fbvar.h>
5549f3f8ccSpetrov 
5602fbfe26Smartin #include <dev/wsfont/wsfont.h>
5702fbfe26Smartin #include <dev/wscons/wsdisplay_vconsvar.h>
5802fbfe26Smartin 
59d436f96fSjdc #include <prop/proplib.h>
60d436f96fSjdc 
61342707d5Sjdc #include <dev/i2c/i2cvar.h>
62342707d5Sjdc #include <dev/i2c/i2c_bitbang.h>
63342707d5Sjdc #include <dev/i2c/ddcvar.h>
64342707d5Sjdc 
6549f3f8ccSpetrov #include <sparc64/dev/ffbreg.h>
6649f3f8ccSpetrov #include <sparc64/dev/ffbvar.h>
6749f3f8ccSpetrov 
689912ffbbSmacallan #include "opt_wsdisplay_compat.h"
699912ffbbSmacallan #include "opt_ffb.h"
709912ffbbSmacallan 
71d2c9943bSmacallan #ifndef WS_DEFAULT_BG
72d2c9943bSmacallan /* Sun -> background should be white */
73d2c9943bSmacallan #define WS_DEFAULT_BG 0xf
74d2c9943bSmacallan #endif
75d2c9943bSmacallan 
769912ffbbSmacallan #ifdef FFB_SYNC
779912ffbbSmacallan #define SYNC ffb_ras_wait(sc)
789912ffbbSmacallan #else
799912ffbbSmacallan #define SYNC
809912ffbbSmacallan #endif
819912ffbbSmacallan 
82342707d5Sjdc /* Debugging */
83342707d5Sjdc #if !defined FFB_DEBUG
84342707d5Sjdc #define FFB_DEBUG 0
85342707d5Sjdc #endif
86342707d5Sjdc #define DPRINTF(x)	if (ffb_debug) printf x
87342707d5Sjdc /* Patchable */
88342707d5Sjdc extern int ffb_debug;
89342707d5Sjdc #if FFB_DEBUG > 0
90342707d5Sjdc int ffb_debug = 1;
91342707d5Sjdc #else
92342707d5Sjdc int ffb_debug = 0;
93342707d5Sjdc #endif
94342707d5Sjdc 
9521d2a323Smartin extern struct cfdriver ffb_cd;
9621d2a323Smartin 
9749f3f8ccSpetrov struct wsscreen_descr ffb_stdscreen = {
98de28b235Sheas 	"sunffb",
9949f3f8ccSpetrov 	0, 0,	/* will be filled in -- XXX shouldn't, it's global. */
10049f3f8ccSpetrov 	0,
10149f3f8ccSpetrov 	0, 0,
102733da76fSmacallan 	WSSCREEN_REVERSE | WSSCREEN_WSCOLORS | WSSCREEN_UNDERLINE |
103733da76fSmacallan 	    WSSCREEN_RESIZE,
1049384ee0dSmartin 	NULL	/* modecookie */
10549f3f8ccSpetrov };
10649f3f8ccSpetrov 
10749f3f8ccSpetrov const struct wsscreen_descr *ffb_scrlist[] = {
10849f3f8ccSpetrov 	&ffb_stdscreen,
10949f3f8ccSpetrov 	/* XXX other formats? */
11049f3f8ccSpetrov };
11149f3f8ccSpetrov 
11249f3f8ccSpetrov struct wsscreen_list ffb_screenlist = {
11349f3f8ccSpetrov 	sizeof(ffb_scrlist) / sizeof(struct wsscreen_descr *),
11449f3f8ccSpetrov 	    ffb_scrlist
11549f3f8ccSpetrov };
11649f3f8ccSpetrov 
11702fbfe26Smartin static struct vcons_screen ffb_console_screen;
118d2c9943bSmacallan 
11953524e44Schristos int	ffb_ioctl(void *, void *, u_long, void *, int, struct lwp *);
120a60b59ccSheas static int ffb_blank(struct ffb_softc *, u_long, u_int *);
1217a51d4ddSjmmv paddr_t ffb_mmap(void *, void *, off_t, int);
12249f3f8ccSpetrov void	ffb_ras_fifo_wait(struct ffb_softc *, int);
12349f3f8ccSpetrov void	ffb_ras_wait(struct ffb_softc *);
12449f3f8ccSpetrov void	ffb_ras_init(struct ffb_softc *);
12549f3f8ccSpetrov void	ffb_ras_copyrows(void *, int, int, int);
12649f3f8ccSpetrov void	ffb_ras_erasecols(void *, int, int, int, long int);
12749f3f8ccSpetrov void	ffb_ras_eraserows(void *, int, int, long int);
12849f3f8ccSpetrov void	ffb_ras_fill(struct ffb_softc *);
129b1dfce18Smacallan void	ffb_ras_invert(struct ffb_softc *);
1309912ffbbSmacallan static void	ffb_ras_setfg(struct ffb_softc *, int32_t);
1319912ffbbSmacallan static void	ffb_ras_setbg(struct ffb_softc *, int32_t);
13249f3f8ccSpetrov 
133d2c9943bSmacallan void	ffb_clearscreen(struct ffb_softc *);
13402fbfe26Smartin void	ffb_init_screen(void *, struct vcons_screen *, int,
135d2c9943bSmacallan 	    long *);
136d2c9943bSmacallan int	ffb_allocattr(void *, int, int, int, long *);
137b1dfce18Smacallan void	ffb_putchar_mono(void *, int, int, u_int, long);
138b1dfce18Smacallan void	ffb_putchar_aa(void *, int, int, u_int, long);
139d2c9943bSmacallan void	ffb_cursor(void *, int, int, int);
140d2c9943bSmacallan 
14121d2a323Smartin /* frame buffer generic driver */
142817999e9Schristos static void ffbfb_unblank(device_t);
14321d2a323Smartin dev_type_open(ffbfb_open);
14421d2a323Smartin dev_type_close(ffbfb_close);
14521d2a323Smartin dev_type_ioctl(ffbfb_ioctl);
14621d2a323Smartin dev_type_mmap(ffbfb_mmap);
147d2c9943bSmacallan 
14821d2a323Smartin static struct fbdriver ffb_fbdriver = {
14921d2a323Smartin         ffbfb_unblank, ffbfb_open, ffbfb_close, ffbfb_ioctl, nopoll,
15021d2a323Smartin 	ffbfb_mmap, nokqfilter
15121d2a323Smartin };
15221d2a323Smartin 
15349f3f8ccSpetrov struct wsdisplay_accessops ffb_accessops = {
15402fbfe26Smartin 	.ioctl = ffb_ioctl,
15502fbfe26Smartin 	.mmap = ffb_mmap,
15649f3f8ccSpetrov };
15749f3f8ccSpetrov 
158342707d5Sjdc /* I2C glue */
159342707d5Sjdc static int ffb_i2c_send_start(void *, int);
160342707d5Sjdc static int ffb_i2c_send_stop(void *, int);
161342707d5Sjdc static int ffb_i2c_initiate_xfer(void *, i2c_addr_t, int);
162342707d5Sjdc static int ffb_i2c_read_byte(void *, uint8_t *, int);
163342707d5Sjdc static int ffb_i2c_write_byte(void *, uint8_t, int);
164342707d5Sjdc 
165342707d5Sjdc /* I2C bitbang glue */
166342707d5Sjdc static void ffb_i2cbb_set_bits(void *, uint32_t);
167342707d5Sjdc static void ffb_i2cbb_set_dir(void *, uint32_t);
168342707d5Sjdc static uint32_t ffb_i2cbb_read(void *);
169342707d5Sjdc 
170342707d5Sjdc static const struct i2c_bitbang_ops ffb_i2cbb_ops = {
171342707d5Sjdc 	ffb_i2cbb_set_bits,
172342707d5Sjdc 	ffb_i2cbb_set_dir,
173342707d5Sjdc 	ffb_i2cbb_read,
174342707d5Sjdc 	{
175342707d5Sjdc 		FFB_DAC_CFG_MPDATA_SDA,
176342707d5Sjdc 		FFB_DAC_CFG_MPDATA_SCL,
177342707d5Sjdc 		0,
178342707d5Sjdc 		0
179342707d5Sjdc 	}
180342707d5Sjdc };
181342707d5Sjdc 
182342707d5Sjdc void ffb_attach_i2c(struct ffb_softc *);
183342707d5Sjdc 
184342707d5Sjdc /* Video mode setting */
185342707d5Sjdc int ffb_tgc_disable(struct ffb_softc *);
186342707d5Sjdc void ffb_get_pclk(int, uint32_t *, int *);
187342707d5Sjdc int ffb_set_vmode(struct ffb_softc *, struct videomode *, int, int *, int *);
188342707d5Sjdc 
189342707d5Sjdc 
19049f3f8ccSpetrov void
ffb_attach(device_t self)191d436f96fSjdc ffb_attach(device_t self)
19249f3f8ccSpetrov {
193d436f96fSjdc 	struct ffb_softc *sc = device_private(self);
19449f3f8ccSpetrov 	struct wsemuldisplaydev_attach_args waa;
195d2c9943bSmacallan 	struct rasops_info *ri;
196d2c9943bSmacallan 	long defattr;
197342707d5Sjdc 	const char *model, *out_dev;
19849f3f8ccSpetrov 	int btype;
19902fbfe26Smartin 	uint32_t dac;
200880e9bcaSmartin 	int maxrow;
201a60b59ccSheas 	u_int blank = WSDISPLAYIO_VIDEO_ON;
2026c325ce2Spk 	char buf[6+1];
203342707d5Sjdc 	int i, try_edid;
204d436f96fSjdc 	prop_data_t data;
20549f3f8ccSpetrov 
20649f3f8ccSpetrov 	printf(":");
20749f3f8ccSpetrov 
20849f3f8ccSpetrov 	if (sc->sc_type == FFB_CREATOR) {
209ea53363eSpk 		btype = prom_getpropint(sc->sc_node, "board_type", 0);
21049f3f8ccSpetrov 		if ((btype & 7) == 3)
21149f3f8ccSpetrov 			printf(" Creator3D");
21249f3f8ccSpetrov 		else
21349f3f8ccSpetrov 			printf(" Creator");
214342707d5Sjdc 	} else {
21549f3f8ccSpetrov 		printf(" Elite3D");
216342707d5Sjdc 		btype = 0;
217342707d5Sjdc 	}
21849f3f8ccSpetrov 
219ea53363eSpk 	model = prom_getpropstring(sc->sc_node, "model");
22049f3f8ccSpetrov 	if (model == NULL || strlen(model) == 0)
22149f3f8ccSpetrov 		model = "unknown";
22249f3f8ccSpetrov 
22349f3f8ccSpetrov 	sc->sc_depth = 24;
22449f3f8ccSpetrov 	sc->sc_linebytes = 8192;
225342707d5Sjdc 	/* We might alter these during EDID mode setting */
226ea53363eSpk 	sc->sc_height = prom_getpropint(sc->sc_node, "height", 0);
227ea53363eSpk 	sc->sc_width = prom_getpropint(sc->sc_node, "width", 0);
22849f3f8ccSpetrov 
22902fbfe26Smartin 	sc->sc_locked = 0;
23002fbfe26Smartin 	sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
23102fbfe26Smartin 
23202fbfe26Smartin 	maxrow = (prom_getoption("screen-#rows", buf, sizeof buf) != 0)
2336c325ce2Spk 		? strtoul(buf, NULL, 10)
2346c325ce2Spk 		: 34;
2356c325ce2Spk 
23642027578Sheas 	/* collect DAC version, as Elite3D cursor enable bit is reversed */
237342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_DEVID);
23802fbfe26Smartin 	dac = DAC_READ(sc, FFB_DAC_VALUE);
23902fbfe26Smartin 	sc->sc_dacrev = (dac >> 28) & 0xf;
24042027578Sheas 
24102fbfe26Smartin 	if (sc->sc_type == FFB_AFB) {
24242027578Sheas 		sc->sc_dacrev = 10;
24302fbfe26Smartin 		sc->sc_needredraw = 0;
24402fbfe26Smartin 	} else {
24502fbfe26Smartin 		/* see what kind of DAC we have */
24602fbfe26Smartin 		int pnum = (dac & 0x0ffff000) >> 12;
24702fbfe26Smartin 		if (pnum == 0x236e) {
24802fbfe26Smartin 			sc->sc_needredraw = 0;
24902fbfe26Smartin 		} else {
25002fbfe26Smartin 			sc->sc_needredraw = 1;
25102fbfe26Smartin 		}
25202fbfe26Smartin 	}
25342027578Sheas 	printf(", model %s, dac %u\n", model, sc->sc_dacrev);
25402fbfe26Smartin 	if (sc->sc_needredraw)
25502fbfe26Smartin 		printf("%s: found old DAC, enabling redraw on unblank\n",
256817999e9Schristos 		    device_xname(sc->sc_dev));
25742027578Sheas 
258342707d5Sjdc 	/* Check if a console resolution "<device>:r<res>" is set. */
259342707d5Sjdc 	if (sc->sc_console) {
2606492be2aSjdc 		out_dev = prom_getpropstring(sc->sc_node, "output-device");
261342707d5Sjdc 		if (out_dev != NULL && strlen(out_dev) != 0 &&
262342707d5Sjdc 		    strstr(out_dev, ":r") != NULL)
263342707d5Sjdc 			try_edid = 0;
264342707d5Sjdc 		else
265342707d5Sjdc 			try_edid = 1;
266342707d5Sjdc 	} else
267342707d5Sjdc 		try_edid = 1;
268342707d5Sjdc 
269eb422932Smacallan #if FFB_DEBUG > 0
2703bdd0f03Smacallan 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
2713bdd0f03Smacallan 	printf("tgc: %08x\n", DAC_READ(sc, FFB_DAC_VALUE));
2723bdd0f03Smacallan 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_DAC_CTRL);
2733bdd0f03Smacallan 	printf("dcl: %08x\n", DAC_READ(sc, FFB_DAC_VALUE));
2743bdd0f03Smacallan #endif
275342707d5Sjdc 	ffb_attach_i2c(sc);
276342707d5Sjdc 
277342707d5Sjdc 	/* Need to set asynchronous blank during DDC write/read */
278342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_USR_CTRL);
279342707d5Sjdc 	dac = DAC_READ(sc, FFB_DAC_VALUE);
280342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_USR_CTRL);
281342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, dac | FFB_DAC_USR_CTRL_BLANK);
282342707d5Sjdc 
283342707d5Sjdc 	/* Some monitors don't respond first time */
284342707d5Sjdc 	i = 0;
285342707d5Sjdc 	while (sc->sc_edid_data[1] == 0 && i++ < 3)
286342707d5Sjdc 		ddc_read_edid(&sc->sc_i2c, sc->sc_edid_data, EDID_DATA_LEN);
287342707d5Sjdc 
288342707d5Sjdc 	/* Remove asynchronous blank */
289342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_USR_CTRL);
290342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, dac);
291342707d5Sjdc 
292342707d5Sjdc 	if (edid_parse(&sc->sc_edid_data[0], &sc->sc_edid_info) != -1) {
293342707d5Sjdc 		sort_modes(sc->sc_edid_info.edid_modes,
294342707d5Sjdc 		    &sc->sc_edid_info.edid_preferred_mode,
295342707d5Sjdc 		    sc->sc_edid_info.edid_nmodes);
296817999e9Schristos 		DPRINTF(("%s: EDID data:\n  ", device_xname(sc->sc_dev)));
297342707d5Sjdc 		for (i = 0; i < EDID_DATA_LEN; i++) {
298342707d5Sjdc 			if (i && !(i % 32))
299342707d5Sjdc 				DPRINTF(("\n "));
300342707d5Sjdc 			if (i && !(i % 4))
301342707d5Sjdc 				DPRINTF((" "));
302342707d5Sjdc 			DPRINTF(("%02x", sc->sc_edid_data[i]));
303342707d5Sjdc 		}
304342707d5Sjdc 		DPRINTF(("\n"));
305342707d5Sjdc 		if (ffb_debug)
306342707d5Sjdc 			edid_print(&sc->sc_edid_info);
307342707d5Sjdc 
30846b33b4bSmartin 		data = prop_data_create_copy(sc->sc_edid_data, EDID_DATA_LEN);
309d436f96fSjdc 		prop_dictionary_set(device_properties(self), "EDID", data);
310d436f96fSjdc 		prop_object_release(data);
311d436f96fSjdc 
312342707d5Sjdc 		if (try_edid)
313342707d5Sjdc 			for (i = 0; i < sc->sc_edid_info.edid_nmodes; i++) {
314342707d5Sjdc 				if (ffb_set_vmode(sc,
315342707d5Sjdc 			    	    &(sc->sc_edid_info.edid_modes[i]), btype,
316342707d5Sjdc 				    &(sc->sc_width), &(sc->sc_height)))
317342707d5Sjdc 					break;
318342707d5Sjdc 			}
319342707d5Sjdc 	} else {
320817999e9Schristos 		DPRINTF(("%s: No EDID data.\n", device_xname(sc->sc_dev)));
321342707d5Sjdc 	}
322342707d5Sjdc 
323342707d5Sjdc 	ffb_ras_init(sc);
324342707d5Sjdc 
325a60b59ccSheas 	ffb_blank(sc, WSDISPLAYIO_SVIDEO, &blank);
326a60b59ccSheas 
327817999e9Schristos 	sc->sc_accel = ((device_cfdata(sc->sc_dev)->cf_flags &
3282be6494fSthorpej 	    FFB_CFFLAG_NOACCEL) == 0);
329d2c9943bSmacallan 
330d2c9943bSmacallan 	wsfont_init();
331d2c9943bSmacallan 
33202fbfe26Smartin 	vcons_init(&sc->vd, sc, &ffb_stdscreen, &ffb_accessops);
33302fbfe26Smartin 	sc->vd.init_screen = ffb_init_screen;
3344453737fSmacallan 	ri = &ffb_console_screen.scr_ri;
3351a8d6cd5Smartin 
33602fbfe26Smartin 	/* we mess with ffb_console_screen only once */
3371a8d6cd5Smartin 	if (sc->sc_console) {
338733da76fSmacallan 		ffb_console_screen.scr_flags = VCONS_SCREEN_IS_STATIC;
33902fbfe26Smartin 		vcons_init_screen(&sc->vd, &ffb_console_screen, 1, &defattr);
34002fbfe26Smartin 		SCREEN_VISIBLE((&ffb_console_screen));
34102fbfe26Smartin 		/*
34202fbfe26Smartin 		 * XXX we shouldn't use a global variable for the console
34302fbfe26Smartin 		 * screen
34402fbfe26Smartin 		 */
34502fbfe26Smartin 		sc->vd.active = &ffb_console_screen;
34602fbfe26Smartin 	} else {
34702fbfe26Smartin 		if (ffb_console_screen.scr_ri.ri_rows == 0) {
34802fbfe26Smartin 			/* do some minimal setup to avoid weirdnesses later */
34902fbfe26Smartin 			vcons_init_screen(&sc->vd, &ffb_console_screen, 1, &defattr);
3504453737fSmacallan 		} else
3514453737fSmacallan 			(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
35293bdac45Sjdc 	}
353d2c9943bSmacallan 
354d2c9943bSmacallan 	ffb_stdscreen.nrows = ri->ri_rows;
355d2c9943bSmacallan 	ffb_stdscreen.ncols = ri->ri_cols;
356d2c9943bSmacallan 	ffb_stdscreen.textops = &ri->ri_ops;
357d2c9943bSmacallan 
35821d2a323Smartin 	sc->sc_fb.fb_driver = &ffb_fbdriver;
35921d2a323Smartin 	sc->sc_fb.fb_type.fb_cmsize = 0;
36021d2a323Smartin 	sc->sc_fb.fb_type.fb_size = maxrow * sc->sc_linebytes;
36121d2a323Smartin 	sc->sc_fb.fb_type.fb_type = FBTYPE_CREATOR;
3629277bf83Smhitch 	sc->sc_fb.fb_type.fb_width = sc->sc_width;
3639277bf83Smhitch 	sc->sc_fb.fb_type.fb_depth = sc->sc_depth;
3649277bf83Smhitch 	sc->sc_fb.fb_type.fb_height = sc->sc_height;
365817999e9Schristos 	sc->sc_fb.fb_device = sc->sc_dev;
36621d2a323Smartin 	fb_attach(&sc->sc_fb, sc->sc_console);
36721d2a323Smartin 
36845727432Smacallan 	ffb_clearscreen(sc);
36945727432Smacallan 
37049f3f8ccSpetrov 	if (sc->sc_console) {
371d2c9943bSmacallan 		wsdisplay_cnattach(&ffb_stdscreen, ri, 0, 0, defattr);
37245727432Smacallan 		vcons_replay_msgbuf(&ffb_console_screen);
37349f3f8ccSpetrov 	}
37449f3f8ccSpetrov 
37549f3f8ccSpetrov 	waa.console = sc->sc_console;
37649f3f8ccSpetrov 	waa.scrdata = &ffb_screenlist;
37749f3f8ccSpetrov 	waa.accessops = &ffb_accessops;
37802fbfe26Smartin 	waa.accesscookie = &sc->vd;
379c7fb772bSthorpej 	config_found(sc->sc_dev, &waa, wsemuldisplaydevprint, CFARGS_NONE);
38049f3f8ccSpetrov }
38149f3f8ccSpetrov 
382342707d5Sjdc void
ffb_attach_i2c(struct ffb_softc * sc)383342707d5Sjdc ffb_attach_i2c(struct ffb_softc *sc)
384342707d5Sjdc {
385342707d5Sjdc 
386342707d5Sjdc 	/* Fill in the i2c tag */
387601e1783Sthorpej 	iic_tag_init(&sc->sc_i2c);
388342707d5Sjdc 	sc->sc_i2c.ic_cookie = sc;
389342707d5Sjdc 	sc->sc_i2c.ic_send_start = ffb_i2c_send_start;
390342707d5Sjdc 	sc->sc_i2c.ic_send_stop = ffb_i2c_send_stop;
391342707d5Sjdc 	sc->sc_i2c.ic_initiate_xfer = ffb_i2c_initiate_xfer;
392342707d5Sjdc 	sc->sc_i2c.ic_read_byte = ffb_i2c_read_byte;
393342707d5Sjdc 	sc->sc_i2c.ic_write_byte = ffb_i2c_write_byte;
394342707d5Sjdc }
395342707d5Sjdc 
39649f3f8ccSpetrov int
ffb_ioctl(void * v,void * vs,u_long cmd,void * data,int flags,struct lwp * l)39753524e44Schristos ffb_ioctl(void *v, void *vs, u_long cmd, void *data, int flags, struct lwp *l)
39849f3f8ccSpetrov {
39902fbfe26Smartin 	struct vcons_data *vd = v;
40002fbfe26Smartin 	struct ffb_softc *sc = vd->cookie;
40149f3f8ccSpetrov 	struct wsdisplay_fbinfo *wdf;
40202fbfe26Smartin 	struct vcons_screen *ms = vd->active;
40349f3f8ccSpetrov 
404342707d5Sjdc 	DPRINTF(("ffb_ioctl: %s cmd _IO%s%s('%c', %lu)\n",
405817999e9Schristos 	       device_xname(sc->sc_dev),
40649f3f8ccSpetrov 	       (cmd & IOC_IN) ? "W" : "", (cmd & IOC_OUT) ? "R" : "",
407342707d5Sjdc 	       (char)IOCGROUP(cmd), cmd & 0xff));
40849f3f8ccSpetrov 
40949f3f8ccSpetrov 	switch (cmd) {
41021d2a323Smartin 	case FBIOGTYPE:
41121d2a323Smartin 		*(struct fbtype *)data = sc->sc_fb.fb_type;
41221d2a323Smartin 		break;
41321d2a323Smartin 	case FBIOGATTR:
41421d2a323Smartin #define fba ((struct fbgattr *)data)
41521d2a323Smartin 		fba->real_type = sc->sc_fb.fb_type.fb_type;
41621d2a323Smartin 		fba->owner = 0; 	/* XXX ??? */
41721d2a323Smartin 		fba->fbtype = sc->sc_fb.fb_type;
41821d2a323Smartin 		fba->sattr.flags = 0;
41921d2a323Smartin 		fba->sattr.emu_type = sc->sc_fb.fb_type.fb_type;
42021d2a323Smartin 		fba->sattr.dev_specific[0] = -1;
42121d2a323Smartin 		fba->emu_types[0] = sc->sc_fb.fb_type.fb_type;
42221d2a323Smartin 		fba->emu_types[1] = -1;
42321d2a323Smartin #undef fba
42421d2a323Smartin 		break;
42521d2a323Smartin 
42621d2a323Smartin 	case FBIOGETCMAP:
42721d2a323Smartin 	case FBIOPUTCMAP:
42821d2a323Smartin 		return EIO;
42921d2a323Smartin 
43021d2a323Smartin 	case FBIOGVIDEO:
43121d2a323Smartin 	case FBIOSVIDEO:
43221d2a323Smartin 		return ffb_blank(sc, cmd == FBIOGVIDEO?
43321d2a323Smartin 		    WSDISPLAYIO_GVIDEO : WSDISPLAYIO_SVIDEO,
43421d2a323Smartin 		    (u_int *)data);
43521d2a323Smartin 		break;
43621d2a323Smartin 	case FBIOGCURSOR:
43721d2a323Smartin 	case FBIOSCURSOR:
438de0502e5Smartin 		/* the console driver is not using the hardware cursor */
439de0502e5Smartin 		break;
44021d2a323Smartin 	case FBIOGCURPOS:
441817999e9Schristos 		printf("%s: FBIOGCURPOS not implemented\n",
442817999e9Schristos 		    device_xname(sc->sc_dev));
44321d2a323Smartin 		return EIO;
44421d2a323Smartin 	case FBIOSCURPOS:
445817999e9Schristos 		printf("%s: FBIOSCURPOS not implemented\n",
446817999e9Schristos 		    device_xname(sc->sc_dev));
44721d2a323Smartin 		return EIO;
44821d2a323Smartin 	case FBIOGCURMAX:
449817999e9Schristos 		printf("%s: FBIOGCURMAX not implemented\n",
450817999e9Schristos 		    device_xname(sc->sc_dev));
45121d2a323Smartin 		return EIO;
45221d2a323Smartin 
45349f3f8ccSpetrov 	case WSDISPLAYIO_GTYPE:
454de28b235Sheas 		*(u_int *)data = WSDISPLAY_TYPE_SUNFFB;
45549f3f8ccSpetrov 		break;
45649f3f8ccSpetrov 	case WSDISPLAYIO_SMODE:
457d2c9943bSmacallan 		{
458d2c9943bSmacallan 			if (sc->sc_mode != *(u_int *)data) {
45949f3f8ccSpetrov 				sc->sc_mode = *(u_int *)data;
46002fbfe26Smartin 				if ((sc->sc_mode == WSDISPLAYIO_MODE_EMUL) &&
46102fbfe26Smartin 				    (sc->sc_locked == 0)) {
462d2c9943bSmacallan 					ffb_ras_init(sc);
46302fbfe26Smartin 					vcons_redraw_screen(ms);
464877740feSmacallan 				} else {
465877740feSmacallan 					ffb_ras_wait(sc);
466d2c9943bSmacallan 				}
467d2c9943bSmacallan 			}
468d2c9943bSmacallan 		}
46949f3f8ccSpetrov 		break;
47049f3f8ccSpetrov 	case WSDISPLAYIO_GINFO:
47149f3f8ccSpetrov 		wdf = (void *)data;
47249f3f8ccSpetrov 		wdf->height = sc->sc_height;
47349f3f8ccSpetrov 		wdf->width  = sc->sc_width;
47449f3f8ccSpetrov 		wdf->depth  = 32;
47549f3f8ccSpetrov 		wdf->cmsize = 256; /* XXX */
47649f3f8ccSpetrov 		break;
47749f3f8ccSpetrov #ifdef WSDISPLAYIO_LINEBYTES
47849f3f8ccSpetrov 	case WSDISPLAYIO_LINEBYTES:
47949f3f8ccSpetrov 		*(u_int *)data = sc->sc_linebytes;
48049f3f8ccSpetrov 		break;
48149f3f8ccSpetrov #endif
48249f3f8ccSpetrov 	case WSDISPLAYIO_GETCMAP:
48349f3f8ccSpetrov 		break;/* XXX */
48449f3f8ccSpetrov 
48549f3f8ccSpetrov 	case WSDISPLAYIO_PUTCMAP:
48649f3f8ccSpetrov 		break;/* XXX */
48749f3f8ccSpetrov 
48849f3f8ccSpetrov 	case WSDISPLAYIO_SVIDEO:
48949f3f8ccSpetrov 	case WSDISPLAYIO_GVIDEO:
490a60b59ccSheas 		return(ffb_blank(sc, cmd, (u_int *)data));
491a60b59ccSheas 		break;
492da28d6ecSmacallan 
49349f3f8ccSpetrov 	case WSDISPLAYIO_GCURPOS:
49449f3f8ccSpetrov 	case WSDISPLAYIO_SCURPOS:
49549f3f8ccSpetrov 	case WSDISPLAYIO_GCURMAX:
49649f3f8ccSpetrov 	case WSDISPLAYIO_GCURSOR:
49749f3f8ccSpetrov 	case WSDISPLAYIO_SCURSOR:
49882a05c7fSmartin 		return EIO; /* not supported yet */
499d436f96fSjdc 		break;
500da28d6ecSmacallan 
501d436f96fSjdc 	case WSDISPLAYIO_GET_EDID: {
502d436f96fSjdc 		struct wsdisplayio_edid_info *d = data;
503d436f96fSjdc 		return wsdisplayio_get_edid(sc->sc_dev, d);
504d436f96fSjdc 	}
505da28d6ecSmacallan 
506da28d6ecSmacallan 	case WSDISPLAYIO_GET_FBINFO: {
507da28d6ecSmacallan 		struct wsdisplayio_fbinfo *fbi = data;
508da28d6ecSmacallan 		return wsdisplayio_get_fbinfo(&ms->scr_ri, fbi);
509da28d6ecSmacallan 	}
510da28d6ecSmacallan 
51149f3f8ccSpetrov 	default:
51282a05c7fSmartin 		return EPASSTHROUGH;
51349f3f8ccSpetrov 	}
51449f3f8ccSpetrov 
51549f3f8ccSpetrov 	return (0);
51649f3f8ccSpetrov }
51749f3f8ccSpetrov 
518a60b59ccSheas /* blank/unblank the screen */
519a60b59ccSheas static int
ffb_blank(struct ffb_softc * sc,u_long cmd,u_int * data)520a60b59ccSheas ffb_blank(struct ffb_softc *sc, u_long cmd, u_int *data)
521a60b59ccSheas {
52202fbfe26Smartin 	struct vcons_screen *ms = sc->vd.active;
523a60b59ccSheas 	u_int val;
524a60b59ccSheas 
525342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
526a60b59ccSheas 	val = DAC_READ(sc, FFB_DAC_VALUE);
527a60b59ccSheas 
528a60b59ccSheas 	switch (cmd) {
529a60b59ccSheas 	case WSDISPLAYIO_GVIDEO:
530a60b59ccSheas 		*data = val & 1;
531a60b59ccSheas 		return(0);
532a60b59ccSheas 		break;
533a60b59ccSheas 	case WSDISPLAYIO_SVIDEO:
534a60b59ccSheas 		if (*data == WSDISPLAYIO_VIDEO_OFF)
535a60b59ccSheas 			val &= ~1;
536a60b59ccSheas 		else if (*data == WSDISPLAYIO_VIDEO_ON)
537a60b59ccSheas 			val |= 1;
538a60b59ccSheas 		else
539a60b59ccSheas 			return(EINVAL);
540a60b59ccSheas 		break;
541a60b59ccSheas 	default:
542a60b59ccSheas 		return(EINVAL);
543a60b59ccSheas 	}
544a60b59ccSheas 
545342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
546a60b59ccSheas 	DAC_WRITE(sc, FFB_DAC_VALUE, val);
547a60b59ccSheas 
54802fbfe26Smartin 	if ((val & 1) && sc->sc_needredraw) {
54902fbfe26Smartin 		if (ms != NULL) {
55002fbfe26Smartin 			if ((sc->sc_mode == WSDISPLAYIO_MODE_EMUL) &&
55102fbfe26Smartin 			    (sc->sc_locked == 0)) {
55202fbfe26Smartin 				ffb_ras_init(sc);
55302fbfe26Smartin 				vcons_redraw_screen(ms);
55402fbfe26Smartin 			}
55502fbfe26Smartin 		}
55602fbfe26Smartin 	}
55702fbfe26Smartin 
558a60b59ccSheas 	return(0);
559a60b59ccSheas }
560a60b59ccSheas 
56149f3f8ccSpetrov paddr_t
ffb_mmap(void * vsc,void * vs,off_t off,int prot)5627a51d4ddSjmmv ffb_mmap(void *vsc, void *vs, off_t off, int prot)
56349f3f8ccSpetrov {
56402fbfe26Smartin 	struct vcons_data *vd = vsc;
56502fbfe26Smartin 	struct ffb_softc *sc = vd->cookie;
56649f3f8ccSpetrov 	int i;
56749f3f8ccSpetrov 
56849f3f8ccSpetrov 	switch (sc->sc_mode) {
56949f3f8ccSpetrov 	case WSDISPLAYIO_MODE_MAPPED:
57049f3f8ccSpetrov 		for (i = 0; i < sc->sc_nreg; i++) {
57149f3f8ccSpetrov 			/* Before this set? */
57249f3f8ccSpetrov 			if (off < sc->sc_addrs[i])
57349f3f8ccSpetrov 				continue;
57449f3f8ccSpetrov 			/* After this set? */
57549f3f8ccSpetrov 			if (off >= (sc->sc_addrs[i] + sc->sc_sizes[i]))
57649f3f8ccSpetrov 				continue;
57749f3f8ccSpetrov 
57849f3f8ccSpetrov 			return (bus_space_mmap(sc->sc_bt, sc->sc_addrs[i],
57949f3f8ccSpetrov 			    off - sc->sc_addrs[i], prot, BUS_SPACE_MAP_LINEAR));
58049f3f8ccSpetrov 		}
58149f3f8ccSpetrov 		break;
58249f3f8ccSpetrov #ifdef WSDISPLAYIO_MODE_DUMBFB
58349f3f8ccSpetrov 	case WSDISPLAYIO_MODE_DUMBFB:
58449f3f8ccSpetrov 		if (sc->sc_nreg < FFB_REG_DFB24)
58549f3f8ccSpetrov 			break;
58649f3f8ccSpetrov 		if (off >= 0 && off < sc->sc_sizes[FFB_REG_DFB24])
58749f3f8ccSpetrov 			return (bus_space_mmap(sc->sc_bt,
58849f3f8ccSpetrov 			    sc->sc_addrs[FFB_REG_DFB24], off, prot,
589877740feSmacallan 			    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE));
59049f3f8ccSpetrov 		break;
59149f3f8ccSpetrov #endif
59249f3f8ccSpetrov 	}
59349f3f8ccSpetrov 	return (-1);
59449f3f8ccSpetrov }
59549f3f8ccSpetrov 
59649f3f8ccSpetrov void
ffb_ras_fifo_wait(struct ffb_softc * sc,int n)597b49b5731Smartin ffb_ras_fifo_wait(struct ffb_softc *sc, int n)
59849f3f8ccSpetrov {
59949f3f8ccSpetrov 	int32_t cache = sc->sc_fifo_cache;
60049f3f8ccSpetrov 
60149f3f8ccSpetrov 	if (cache < n) {
60249f3f8ccSpetrov 		do {
60349f3f8ccSpetrov 			cache = FBC_READ(sc, FFB_FBC_UCSR);
60449f3f8ccSpetrov 			cache = (cache & FBC_UCSR_FIFO_MASK) - 8;
60549f3f8ccSpetrov 		} while (cache < n);
60649f3f8ccSpetrov 	}
60749f3f8ccSpetrov 	sc->sc_fifo_cache = cache - n;
60849f3f8ccSpetrov }
60949f3f8ccSpetrov 
61049f3f8ccSpetrov void
ffb_ras_wait(struct ffb_softc * sc)611b49b5731Smartin ffb_ras_wait(struct ffb_softc *sc)
61249f3f8ccSpetrov {
6131a509d61Scdi 	uint32_t ucsr, r;
61449f3f8ccSpetrov 
61549f3f8ccSpetrov 	while (1) {
61649f3f8ccSpetrov 		ucsr = FBC_READ(sc, FFB_FBC_UCSR);
61749f3f8ccSpetrov 		if ((ucsr & (FBC_UCSR_FB_BUSY|FBC_UCSR_RP_BUSY)) == 0)
61849f3f8ccSpetrov 			break;
61949f3f8ccSpetrov 		r = ucsr & (FBC_UCSR_READ_ERR | FBC_UCSR_FIFO_OVFL);
62049f3f8ccSpetrov 		if (r != 0)
62149f3f8ccSpetrov 			FBC_WRITE(sc, FFB_FBC_UCSR, r);
62249f3f8ccSpetrov 	}
62349f3f8ccSpetrov }
62449f3f8ccSpetrov 
62549f3f8ccSpetrov void
ffb_ras_init(struct ffb_softc * sc)626b49b5731Smartin ffb_ras_init(struct ffb_softc *sc)
62749f3f8ccSpetrov {
628342707d5Sjdc 	uint32_t fbc;
629342707d5Sjdc 
630342707d5Sjdc 	if (sc->sc_width > 1280) {
631342707d5Sjdc 	DPRINTF(("ffb_ras_init: high resolution.\n"));
6323bdd0f03Smacallan 		fbc = FFB_FBC_WM_COMBINED | FFB_FBC_WE_FORCEON |
633342707d5Sjdc 		    FFB_FBC_ZE_OFF | FFB_FBC_YE_OFF | FFB_FBC_XE_ON;
634342707d5Sjdc 	} else {
635342707d5Sjdc 	DPRINTF(("ffb_ras_init: standard resolution.\n"));
636342707d5Sjdc 		fbc = FFB_FBC_XE_OFF;
637342707d5Sjdc 	}
6387b2ed462Smacallan 	ffb_ras_fifo_wait(sc, 7);
6393bdd0f03Smacallan 	DPRINTF(("WID: %08x\n", FBC_READ(sc, FFB_FBC_WID)));
6403bdd0f03Smacallan 	FBC_WRITE(sc, FFB_FBC_WID, 0x0);
64149f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_PPC,
6429912ffbbSmacallan 	    FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE | FBC_PPC_ACE_DIS |
6433bdd0f03Smacallan 	    FBC_PPC_APE_DIS | FBC_PPC_DCE_DIS | FBC_PPC_CS_CONST |
644fa70cee0Smacallan 	    FBC_PPC_ABE_DIS | FBC_PPC_XS_WID);
6453bdd0f03Smacallan 
6463bdd0f03Smacallan 	fbc |= FFB_FBC_WB_A | FFB_FBC_RB_A | FFB_FBC_SB_BOTH |
6473bdd0f03Smacallan 	       FFB_FBC_RGBE_MASK;
6483bdd0f03Smacallan         DPRINTF(("%s: fbc is %08x\n", __func__, fbc));
6493bdd0f03Smacallan         FBC_WRITE(sc, FFB_FBC_FBC, fbc);
65049f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_NEW);
65149f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_RECTANGLE);
65249f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_PMASK, 0xffffffff);
65349f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_FONTINC, 0x10000);
6547b2ed462Smacallan 	ffb_ras_fifo_wait(sc, 5);
65549f3f8ccSpetrov 	sc->sc_fg_cache = 0;
65649f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_FG, sc->sc_fg_cache);
6577b2ed462Smacallan 	sc->sc_bg_cache = 0;
6587b2ed462Smacallan 	FBC_WRITE(sc, FFB_FBC_BG, sc->sc_bg_cache);
659fa70cee0Smacallan 	FBC_WRITE(sc, FFB_FBC_BLENDC, FFB_BLENDC_FORCE_ONE |
660fa70cee0Smacallan 				      FFB_BLENDC_DF_ONE_M_A |
661fa70cee0Smacallan 				      FFB_BLENDC_SF_A);
662fa70cee0Smacallan 	FBC_WRITE(sc, FFB_FBC_BLENDC1, 0);
663fa70cee0Smacallan 	FBC_WRITE(sc, FFB_FBC_BLENDC2, 0);
66449f3f8ccSpetrov 	ffb_ras_wait(sc);
66549f3f8ccSpetrov }
66649f3f8ccSpetrov 
66749f3f8ccSpetrov void
ffb_ras_eraserows(void * cookie,int row,int n,long attr)668b49b5731Smartin ffb_ras_eraserows(void *cookie, int row, int n, long attr)
66949f3f8ccSpetrov {
67049f3f8ccSpetrov 	struct rasops_info *ri = cookie;
67102fbfe26Smartin 	struct vcons_screen *scr = ri->ri_hw;
67202fbfe26Smartin 	struct ffb_softc *sc = scr->scr_cookie;
67349f3f8ccSpetrov 
67449f3f8ccSpetrov 	if (row < 0) {
67549f3f8ccSpetrov 		n += row;
67649f3f8ccSpetrov 		row = 0;
67749f3f8ccSpetrov 	}
67849f3f8ccSpetrov 	if (row + n > ri->ri_rows)
67949f3f8ccSpetrov 		n = ri->ri_rows - row;
68049f3f8ccSpetrov 	if (n <= 0)
68149f3f8ccSpetrov 		return;
68249f3f8ccSpetrov 
68349f3f8ccSpetrov 	ffb_ras_fill(sc);
68449f3f8ccSpetrov 	ffb_ras_setfg(sc, ri->ri_devcmap[(attr >> 16) & 0xf]);
68549f3f8ccSpetrov 	ffb_ras_fifo_wait(sc, 4);
68649f3f8ccSpetrov 	if ((n == ri->ri_rows) && (ri->ri_flg & RI_FULLCLEAR)) {
68749f3f8ccSpetrov 		FBC_WRITE(sc, FFB_FBC_BY, 0);
68849f3f8ccSpetrov 		FBC_WRITE(sc, FFB_FBC_BX, 0);
68949f3f8ccSpetrov 		FBC_WRITE(sc, FFB_FBC_BH, ri->ri_height);
69049f3f8ccSpetrov 		FBC_WRITE(sc, FFB_FBC_BW, ri->ri_width);
691c385f585Smacallan 		ri->ri_flg &= ~RI_CURSOR;
69249f3f8ccSpetrov 	} else {
69349f3f8ccSpetrov 		row *= ri->ri_font->fontheight;
69449f3f8ccSpetrov 		FBC_WRITE(sc, FFB_FBC_BY, ri->ri_yorigin + row);
69549f3f8ccSpetrov 		FBC_WRITE(sc, FFB_FBC_BX, ri->ri_xorigin);
69649f3f8ccSpetrov 		FBC_WRITE(sc, FFB_FBC_BH, n * ri->ri_font->fontheight);
69749f3f8ccSpetrov 		FBC_WRITE(sc, FFB_FBC_BW, ri->ri_emuwidth);
69849f3f8ccSpetrov 	}
6999912ffbbSmacallan 	SYNC;
70049f3f8ccSpetrov }
70149f3f8ccSpetrov 
70249f3f8ccSpetrov void
ffb_ras_erasecols(void * cookie,int row,int col,int n,long attr)703b49b5731Smartin ffb_ras_erasecols(void *cookie, int row, int col, int n, long attr)
70449f3f8ccSpetrov {
70549f3f8ccSpetrov 	struct rasops_info *ri = cookie;
70602fbfe26Smartin 	struct vcons_screen *scr = ri->ri_hw;
70702fbfe26Smartin 	struct ffb_softc *sc = scr->scr_cookie;
70849f3f8ccSpetrov 
70949f3f8ccSpetrov 	if ((row < 0) || (row >= ri->ri_rows))
71049f3f8ccSpetrov 		return;
71149f3f8ccSpetrov 	if (col < 0) {
71249f3f8ccSpetrov 		n += col;
71349f3f8ccSpetrov 		col = 0;
71449f3f8ccSpetrov 	}
71549f3f8ccSpetrov 	if (col + n > ri->ri_cols)
71649f3f8ccSpetrov 		n = ri->ri_cols - col;
71749f3f8ccSpetrov 	if (n <= 0)
71849f3f8ccSpetrov 		return;
719c385f585Smacallan 
72049f3f8ccSpetrov 	n *= ri->ri_font->fontwidth;
72149f3f8ccSpetrov 	col *= ri->ri_font->fontwidth;
72249f3f8ccSpetrov 	row *= ri->ri_font->fontheight;
72349f3f8ccSpetrov 
72449f3f8ccSpetrov 	ffb_ras_fill(sc);
72549f3f8ccSpetrov 	ffb_ras_setfg(sc, ri->ri_devcmap[(attr >> 16) & 0xf]);
72649f3f8ccSpetrov 	ffb_ras_fifo_wait(sc, 4);
72749f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_BY, ri->ri_yorigin + row);
72849f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_BX, ri->ri_xorigin + col);
72949f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_BH, ri->ri_font->fontheight);
7307b2ed462Smacallan 	FBC_WRITE(sc, FFB_FBC_BW, n);
7319912ffbbSmacallan 	SYNC;
73249f3f8ccSpetrov }
73349f3f8ccSpetrov 
73449f3f8ccSpetrov void
ffb_ras_fill(struct ffb_softc * sc)735b49b5731Smartin ffb_ras_fill(struct ffb_softc *sc)
73649f3f8ccSpetrov {
737fa70cee0Smacallan 	ffb_ras_fifo_wait(sc, 3);
738fa70cee0Smacallan 	FBC_WRITE(sc, FFB_FBC_PPC,
739fa70cee0Smacallan 	    FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE | FBC_PPC_ACE_DIS |
740fa70cee0Smacallan 	    FBC_PPC_APE_DIS | FBC_PPC_DCE_DIS | FBC_PPC_CS_CONST |
741fa70cee0Smacallan 	    FBC_PPC_ABE_DIS | FBC_PPC_XS_WID);
74249f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_NEW);
74349f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_RECTANGLE);
7449912ffbbSmacallan 	SYNC;
74549f3f8ccSpetrov }
74649f3f8ccSpetrov 
74749f3f8ccSpetrov void
ffb_ras_invert(struct ffb_softc * sc)748b1dfce18Smacallan ffb_ras_invert(struct ffb_softc *sc)
749b1dfce18Smacallan {
750b1dfce18Smacallan 	ffb_ras_fifo_wait(sc, 3);
751b1dfce18Smacallan 	FBC_WRITE(sc, FFB_FBC_PPC,
752b1dfce18Smacallan 	    FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE | FBC_PPC_ACE_DIS |
753b1dfce18Smacallan 	    FBC_PPC_APE_DIS | FBC_PPC_DCE_DIS | FBC_PPC_CS_CONST |
754b1dfce18Smacallan 	    FBC_PPC_ABE_DIS | FBC_PPC_XS_WID);
755b1dfce18Smacallan 	FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_INVERT);
756b1dfce18Smacallan 	FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_RECTANGLE);
757b1dfce18Smacallan 	SYNC;
758b1dfce18Smacallan }
759b1dfce18Smacallan 
760b1dfce18Smacallan void
ffb_ras_copyrows(void * cookie,int src,int dst,int n)761b49b5731Smartin ffb_ras_copyrows(void *cookie, int src, int dst, int n)
76249f3f8ccSpetrov {
76349f3f8ccSpetrov 	struct rasops_info *ri = cookie;
76402fbfe26Smartin 	struct vcons_screen *scr = ri->ri_hw;
76502fbfe26Smartin 	struct ffb_softc *sc = scr->scr_cookie;
76649f3f8ccSpetrov 
76749f3f8ccSpetrov 	if (dst == src)
76849f3f8ccSpetrov 		return;
76949f3f8ccSpetrov 	if (src < 0) {
77049f3f8ccSpetrov 		n += src;
77149f3f8ccSpetrov 		src = 0;
77249f3f8ccSpetrov 	}
77349f3f8ccSpetrov 	if ((src + n) > ri->ri_rows)
77449f3f8ccSpetrov 		n = ri->ri_rows - src;
77549f3f8ccSpetrov 	if (dst < 0) {
77649f3f8ccSpetrov 		n += dst;
77749f3f8ccSpetrov 		dst = 0;
77849f3f8ccSpetrov 	}
77949f3f8ccSpetrov 	if ((dst + n) > ri->ri_rows)
78049f3f8ccSpetrov 		n = ri->ri_rows - dst;
78149f3f8ccSpetrov 	if (n <= 0)
78249f3f8ccSpetrov 		return;
78349f3f8ccSpetrov 	n *= ri->ri_font->fontheight;
78449f3f8ccSpetrov 	src *= ri->ri_font->fontheight;
78549f3f8ccSpetrov 	dst *= ri->ri_font->fontheight;
78649f3f8ccSpetrov 
787fa70cee0Smacallan 	ffb_ras_fifo_wait(sc, 9);
788fa70cee0Smacallan 	FBC_WRITE(sc, FFB_FBC_PPC,
789fa70cee0Smacallan 	    FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE | FBC_PPC_ACE_DIS |
790fa70cee0Smacallan 	    FBC_PPC_APE_DIS | FBC_PPC_DCE_DIS | FBC_PPC_CS_CONST |
791fa70cee0Smacallan 	    FBC_PPC_ABE_DIS | FBC_PPC_XS_WID);
79249f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_OLD | (FBC_ROP_OLD << 8));
79349f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_VSCROLL);
79449f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_BY, ri->ri_yorigin + src);
79549f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_BX, ri->ri_xorigin);
79649f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_DY, ri->ri_yorigin + dst);
79749f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_DX, ri->ri_xorigin);
79849f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_BH, n);
79949f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_BW, ri->ri_emuwidth);
8009912ffbbSmacallan 	SYNC;
80149f3f8ccSpetrov }
80249f3f8ccSpetrov 
8039912ffbbSmacallan static void
ffb_ras_setfg(struct ffb_softc * sc,int32_t fg)804b49b5731Smartin ffb_ras_setfg(struct ffb_softc *sc, int32_t fg)
80549f3f8ccSpetrov {
80649f3f8ccSpetrov 	ffb_ras_fifo_wait(sc, 1);
80749f3f8ccSpetrov 	if (fg == sc->sc_fg_cache)
80849f3f8ccSpetrov 		return;
80949f3f8ccSpetrov 	sc->sc_fg_cache = fg;
81049f3f8ccSpetrov 	FBC_WRITE(sc, FFB_FBC_FG, fg);
8119912ffbbSmacallan 	SYNC;
8129912ffbbSmacallan }
8139912ffbbSmacallan 
8149912ffbbSmacallan static void
ffb_ras_setbg(struct ffb_softc * sc,int32_t bg)8159912ffbbSmacallan ffb_ras_setbg(struct ffb_softc *sc, int32_t bg)
8169912ffbbSmacallan {
8179912ffbbSmacallan 	ffb_ras_fifo_wait(sc, 1);
8189912ffbbSmacallan 	if (bg == sc->sc_bg_cache)
8199912ffbbSmacallan 		return;
8209912ffbbSmacallan 	sc->sc_bg_cache = bg;
8219912ffbbSmacallan 	FBC_WRITE(sc, FFB_FBC_BG, bg);
8229912ffbbSmacallan 	SYNC;
82349f3f8ccSpetrov }
82421d2a323Smartin 
82521d2a323Smartin /* frame buffer generic driver support functions */
82621d2a323Smartin static void
ffbfb_unblank(device_t dev)827817999e9Schristos ffbfb_unblank(device_t dev)
82821d2a323Smartin {
829d38a1476Smartin 	struct ffb_softc *sc = device_private(dev);
83002fbfe26Smartin 	struct vcons_screen *ms = sc->vd.active;
831df850dc7Smartin 	u_int on = 1;
83202fbfe26Smartin 	int redraw = 0;
83302fbfe26Smartin 
83402fbfe26Smartin 	ffb_ras_init(sc);
83502fbfe26Smartin 	if (sc->sc_locked) {
83602fbfe26Smartin 		sc->sc_locked = 0;
83702fbfe26Smartin 		redraw = 1;
83802fbfe26Smartin 	}
83921d2a323Smartin 
840cfce4b55Smacallan 	ffb_blank(sc, WSDISPLAYIO_SVIDEO, &on);
841cfce4b55Smacallan #if 0
84202fbfe26Smartin 	if ((sc->vd.active != &ffb_console_screen) &&
84302fbfe26Smartin 	    (ffb_console_screen.scr_flags & VCONS_SCREEN_IS_STATIC)) {
84402fbfe26Smartin 		/*
84502fbfe26Smartin 		 * force-switch to the console screen.
84602fbfe26Smartin 		 * Caveat: the higher layer will think we're still on the
84702fbfe26Smartin 		 * other screen
84802fbfe26Smartin 		 */
84902fbfe26Smartin 
85002fbfe26Smartin 		SCREEN_INVISIBLE(sc->vd.active);
85102fbfe26Smartin 		sc->vd.active = &ffb_console_screen;
85202fbfe26Smartin 		SCREEN_VISIBLE(sc->vd.active);
85302fbfe26Smartin 		ms = sc->vd.active;
85402fbfe26Smartin 		redraw = 1;
85502fbfe26Smartin 	}
856cfce4b55Smacallan #endif
85702fbfe26Smartin 	if (redraw) {
85802fbfe26Smartin 		vcons_redraw_screen(ms);
85902fbfe26Smartin 	}
86021d2a323Smartin }
86121d2a323Smartin 
86221d2a323Smartin int
ffbfb_open(dev_t dev,int flags,int mode,struct lwp * l)863948731b9Schristos ffbfb_open(dev_t dev, int flags, int mode, struct lwp *l)
86421d2a323Smartin {
86502fbfe26Smartin 	struct ffb_softc *sc;
86621d2a323Smartin 
8672dea63feScegger 	sc = device_lookup_private(&ffb_cd, minor(dev));
8682dea63feScegger 	if (sc == NULL)
86921d2a323Smartin 		return ENXIO;
87021d2a323Smartin 
87102fbfe26Smartin 	sc->sc_locked = 1;
87221d2a323Smartin 	return 0;
87321d2a323Smartin }
87421d2a323Smartin 
87521d2a323Smartin int
ffbfb_close(dev_t dev,int flags,int mode,struct lwp * l)876948731b9Schristos ffbfb_close(dev_t dev, int flags, int mode, struct lwp *l)
87721d2a323Smartin {
8782dea63feScegger 	struct ffb_softc *sc = device_lookup_private(&ffb_cd, minor(dev));
87902fbfe26Smartin 	struct vcons_screen *ms = sc->vd.active;
88002fbfe26Smartin 
88102fbfe26Smartin 	sc->sc_locked = 0;
88202fbfe26Smartin 	if (ms != NULL) {
88302fbfe26Smartin 		if ((sc->sc_mode == WSDISPLAYIO_MODE_EMUL) &&
88402fbfe26Smartin 		    (sc->sc_locked == 0)) {
88502fbfe26Smartin 			ffb_ras_init(sc);
88602fbfe26Smartin 			vcons_redraw_screen(ms);
88702fbfe26Smartin 		}
88802fbfe26Smartin 	}
88921d2a323Smartin 	return 0;
89021d2a323Smartin }
89121d2a323Smartin 
89221d2a323Smartin int
ffbfb_ioctl(dev_t dev,u_long cmd,void * data,int flags,struct lwp * l)89353524e44Schristos ffbfb_ioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
89421d2a323Smartin {
8952dea63feScegger 	struct ffb_softc *sc = device_lookup_private(&ffb_cd, minor(dev));
89621d2a323Smartin 
89702fbfe26Smartin 	return ffb_ioctl(&sc->vd, NULL, cmd, data, flags, l);
89821d2a323Smartin }
89921d2a323Smartin 
90021d2a323Smartin paddr_t
ffbfb_mmap(dev_t dev,off_t off,int prot)90121d2a323Smartin ffbfb_mmap(dev_t dev, off_t off, int prot)
90221d2a323Smartin {
9032dea63feScegger 	struct ffb_softc *sc = device_lookup_private(&ffb_cd, minor(dev));
904588c5238Smacallan 	uint64_t size;
90521d2a323Smartin 	int i, reg;
90621d2a323Smartin 	off_t o;
90721d2a323Smartin 
90821d2a323Smartin 	/*
90921d2a323Smartin 	 * off is a magic cookie (see xfree86/drivers/sunffb/ffb.h),
91021d2a323Smartin 	 * which we map to an index into the "reg" property, and use
91121d2a323Smartin 	 * our copy of the firmware data as arguments for the real
91221d2a323Smartin 	 * mapping.
91321d2a323Smartin 	 */
914877740feSmacallan 	static struct { unsigned long voff; int reg; long flags; } map[] = {
915877740feSmacallan 		{ 0x00000000, FFB_REG_SFB8R, BUS_SPACE_MAP_PREFETCHABLE },
916877740feSmacallan 		{ 0x00400000, FFB_REG_SFB8G, BUS_SPACE_MAP_PREFETCHABLE },
917877740feSmacallan 		{ 0x00800000, FFB_REG_SFB8B, BUS_SPACE_MAP_PREFETCHABLE },
918877740feSmacallan 		{ 0x00c00000, FFB_REG_SFB8X, BUS_SPACE_MAP_PREFETCHABLE },
919877740feSmacallan 		{ 0x01000000, FFB_REG_SFB32, BUS_SPACE_MAP_PREFETCHABLE },
920877740feSmacallan 		{ 0x02000000, FFB_REG_SFB64, BUS_SPACE_MAP_PREFETCHABLE  },
921877740feSmacallan 		{ 0x04000000, FFB_REG_FBC, 0 },
922877740feSmacallan 		{ 0x04004000, FFB_REG_DFB8R, BUS_SPACE_MAP_PREFETCHABLE },
923877740feSmacallan 		{ 0x04404000, FFB_REG_DFB8G, BUS_SPACE_MAP_PREFETCHABLE },
924877740feSmacallan 		{ 0x04804000, FFB_REG_DFB8B, BUS_SPACE_MAP_PREFETCHABLE },
925877740feSmacallan 		{ 0x04c04000, FFB_REG_DFB8X, BUS_SPACE_MAP_PREFETCHABLE },
926877740feSmacallan 		{ 0x05004000, FFB_REG_DFB24, BUS_SPACE_MAP_PREFETCHABLE },
927877740feSmacallan 		{ 0x06004000, FFB_REG_DFB32, BUS_SPACE_MAP_PREFETCHABLE },
928877740feSmacallan 		{ 0x07004000, FFB_REG_DFB422A, BUS_SPACE_MAP_PREFETCHABLE },
929877740feSmacallan 		{ 0x0bc06000, FFB_REG_DAC, 0 },
930877740feSmacallan 		{ 0x0bc08000, FFB_REG_PROM, 0 },
931877740feSmacallan 		{ 0x0bc18000, 0, 0 }
93221d2a323Smartin 	};
93321d2a323Smartin 
93421d2a323Smartin 	/* special value "FFB_EXP_VOFF" - not backed by any "reg" entry */
93521d2a323Smartin 	if (off == 0x0bc18000)
93621d2a323Smartin 		return bus_space_mmap(sc->sc_bt, sc->sc_addrs[FFB_REG_PROM],
93721d2a323Smartin 		    0x00200000, prot, BUS_SPACE_MAP_LINEAR);
93821d2a323Smartin 
939588c5238Smacallan 	/*
940588c5238Smacallan 	 * FFB_VOFF_FBC_KREGS - used by afbinit to upload firmware. We should
941588c5238Smacallan 	 * probably mmap them only on afb boards
942588c5238Smacallan 	 */
943588c5238Smacallan 	if ((off >= 0x0bc04000) && (off < 0x0bc06000))
944588c5238Smacallan 		return bus_space_mmap(sc->sc_bt, sc->sc_addrs[FFB_REG_PROM],
945588c5238Smacallan 		    0x00610000 + (off - 0x0bc04000), prot,
946588c5238Smacallan 		    BUS_SPACE_MAP_LINEAR);
947588c5238Smacallan 
94821d2a323Smartin #define NELEMS(arr) (sizeof(arr)/sizeof((arr)[0]))
94921d2a323Smartin 
95021d2a323Smartin 	/* the map is ordered by voff */
951588c5238Smacallan 	for (i = 0; i < NELEMS(map)-1; i++) {
95221d2a323Smartin 		reg = map[i].reg;
953d2c9943bSmacallan 		/* the number of entries in reg seems to vary */
954588c5238Smacallan 		if (reg < sc->sc_nreg) {
955d1579b2dSriastradh 			size = uimin((map[i + 1].voff - map[i].voff),
956588c5238Smacallan 			    sc->sc_sizes[reg]);
957588c5238Smacallan 			if ((off >= map[i].voff) &&
958588c5238Smacallan 			    (off < (map[i].voff + size))) {
95921d2a323Smartin 				o = off - map[i].voff;
960588c5238Smacallan 				return bus_space_mmap(sc->sc_bt,
961588c5238Smacallan 				    sc->sc_addrs[reg], o, prot,
962877740feSmacallan 				    BUS_SPACE_MAP_LINEAR | map[i].flags);
96321d2a323Smartin 			}
964588c5238Smacallan 		}
965588c5238Smacallan 	}
96621d2a323Smartin 
96721d2a323Smartin 	return -1;
96821d2a323Smartin }
969d2c9943bSmacallan 
970d2c9943bSmacallan void
ffb_clearscreen(struct ffb_softc * sc)971d2c9943bSmacallan ffb_clearscreen(struct ffb_softc *sc)
972d2c9943bSmacallan {
97302fbfe26Smartin 	struct rasops_info *ri = &ffb_console_screen.scr_ri;
974d2c9943bSmacallan 	ffb_ras_fill(sc);
975d2c9943bSmacallan 	ffb_ras_setfg(sc, ri->ri_devcmap[WS_DEFAULT_BG]);
976d2c9943bSmacallan 	ffb_ras_fifo_wait(sc, 4);
977d2c9943bSmacallan 	FBC_WRITE(sc, FFB_FBC_BY, 0);
978d2c9943bSmacallan 	FBC_WRITE(sc, FFB_FBC_BX, 0);
97902fbfe26Smartin 	FBC_WRITE(sc, FFB_FBC_BH, sc->sc_height);
98002fbfe26Smartin 	FBC_WRITE(sc, FFB_FBC_BW, sc->sc_width);
981d2c9943bSmacallan }
982d2c9943bSmacallan 
983d2c9943bSmacallan void
ffb_cursor(void * cookie,int on,int row,int col)984d2c9943bSmacallan ffb_cursor(void *cookie, int on, int row, int col)
985d2c9943bSmacallan {
986d2c9943bSmacallan 	struct rasops_info *ri = cookie;
98702fbfe26Smartin 	struct vcons_screen *scr;
98802fbfe26Smartin 	struct ffb_softc *sc;
989b1dfce18Smacallan 	int x, y, wi, he;
990d2c9943bSmacallan 
99102fbfe26Smartin 	if (cookie != NULL) {
99202fbfe26Smartin 		scr = ri->ri_hw;
99302fbfe26Smartin 		sc = scr->scr_cookie;
99402fbfe26Smartin 
995d2c9943bSmacallan 		wi = ri->ri_font->fontwidth;
996d2c9943bSmacallan 		he = ri->ri_font->fontheight;
997d2c9943bSmacallan 
99802fbfe26Smartin 		if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
999b1dfce18Smacallan 
1000b1dfce18Smacallan 			if (ri->ri_flg & RI_CURSOR) {
1001b1dfce18Smacallan 
1002b1dfce18Smacallan 				/* remove cursor */
100302fbfe26Smartin 				x = ri->ri_ccol * wi + ri->ri_xorigin;
100402fbfe26Smartin 				y = ri->ri_crow * he + ri->ri_yorigin;
1005d2c9943bSmacallan 
1006b1dfce18Smacallan 				ffb_ras_invert(sc);
1007b1dfce18Smacallan 				ffb_ras_fifo_wait(sc, 4);
1008b1dfce18Smacallan 				FBC_WRITE(sc, FFB_FBC_BY, y);
1009b1dfce18Smacallan 				FBC_WRITE(sc, FFB_FBC_BX, x);
1010b1dfce18Smacallan 				FBC_WRITE(sc, FFB_FBC_BH, he);
1011b1dfce18Smacallan 				FBC_WRITE(sc, FFB_FBC_BW, wi);
1012b1dfce18Smacallan 
101302fbfe26Smartin 				ri->ri_flg &= ~RI_CURSOR;
1014d2c9943bSmacallan 			}
101502fbfe26Smartin 			ri->ri_crow = row;
101602fbfe26Smartin 			ri->ri_ccol = col;
101702fbfe26Smartin 			if (on)
1018d2c9943bSmacallan 			{
101902fbfe26Smartin 				x = ri->ri_ccol * wi + ri->ri_xorigin;
102002fbfe26Smartin 				y = ri->ri_crow * he + ri->ri_yorigin;
1021b1dfce18Smacallan 
1022b1dfce18Smacallan 				ffb_ras_invert(sc);
1023b1dfce18Smacallan 				ffb_ras_fifo_wait(sc, 4);
1024b1dfce18Smacallan 				FBC_WRITE(sc, FFB_FBC_BY, y);
1025b1dfce18Smacallan 				FBC_WRITE(sc, FFB_FBC_BX, x);
1026b1dfce18Smacallan 				FBC_WRITE(sc, FFB_FBC_BH, he);
1027b1dfce18Smacallan 				FBC_WRITE(sc, FFB_FBC_BW, wi);
1028b1dfce18Smacallan 
102902fbfe26Smartin 				ri->ri_flg |= RI_CURSOR;
1030d2c9943bSmacallan 			}
1031d2c9943bSmacallan 		} else {
103202fbfe26Smartin 			ri->ri_crow = row;
103302fbfe26Smartin 			ri->ri_ccol = col;
103402fbfe26Smartin 			ri->ri_flg &= ~RI_CURSOR;
103502fbfe26Smartin 		}
1036d2c9943bSmacallan 	}
1037d2c9943bSmacallan }
1038d2c9943bSmacallan 
1039b1dfce18Smacallan /* mono bitmap font */
1040d2c9943bSmacallan void
ffb_putchar_mono(void * cookie,int row,int col,u_int c,long attr)1041b1dfce18Smacallan ffb_putchar_mono(void *cookie, int row, int col, u_int c, long attr)
1042d2c9943bSmacallan {
1043d2c9943bSmacallan 	struct rasops_info *ri = cookie;
104402fbfe26Smartin 	struct vcons_screen *scr = ri->ri_hw;
10459912ffbbSmacallan 	struct wsdisplay_font *font = PICK_FONT(ri, c);
104602fbfe26Smartin 	struct ffb_softc *sc = scr->scr_cookie;
10479912ffbbSmacallan 	void *data;
10489912ffbbSmacallan 	uint32_t fg, bg;
1049b1dfce18Smacallan 	int i;
10509912ffbbSmacallan 	int x, y, wi, he;
10519912ffbbSmacallan 
1052fa70cee0Smacallan 	if (sc->sc_mode != WSDISPLAYIO_MODE_EMUL)
1053fa70cee0Smacallan 		return;
1054fa70cee0Smacallan 
10559912ffbbSmacallan 	wi = font->fontwidth;
10569912ffbbSmacallan 	he = font->fontheight;
10579912ffbbSmacallan 
10589912ffbbSmacallan 	if (!CHAR_IN_FONT(c, font))
10599912ffbbSmacallan 		return;
1060fa70cee0Smacallan 
10619912ffbbSmacallan 	bg = ri->ri_devcmap[(attr >> 16) & 0xf];
10629912ffbbSmacallan 	fg = ri->ri_devcmap[(attr >> 24) & 0xf];
10639912ffbbSmacallan 	x = ri->ri_xorigin + col * wi;
10649912ffbbSmacallan 	y = ri->ri_yorigin + row * he;
10659912ffbbSmacallan 
1066b1dfce18Smacallan 	data = WSFONT_GLYPH(c, font);
10679912ffbbSmacallan 
10689912ffbbSmacallan 	ffb_ras_setbg(sc, bg);
10699912ffbbSmacallan 	ffb_ras_setfg(sc, fg);
1070fa70cee0Smacallan 	ffb_ras_fifo_wait(sc, 4);
10719912ffbbSmacallan 	FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_NEW);
10729912ffbbSmacallan 	FBC_WRITE(sc, FFB_FBC_FONTXY, (y << 16) | x);
10739912ffbbSmacallan 	FBC_WRITE(sc, FFB_FBC_FONTW, wi);
1074fa70cee0Smacallan 	FBC_WRITE(sc, FFB_FBC_PPC,
1075fa70cee0Smacallan 	    FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE | FBC_PPC_ACE_DIS |
1076fa70cee0Smacallan 	    FBC_PPC_APE_DIS | FBC_PPC_DCE_DIS | FBC_PPC_CS_CONST |
1077fa70cee0Smacallan 	    FBC_PPC_ABE_DIS | FBC_PPC_XS_WID);
10789912ffbbSmacallan 
1079fa70cee0Smacallan 	switch (font->stride) {
10809912ffbbSmacallan 		case 1: {
10819912ffbbSmacallan 			uint8_t *data8 = data;
10829912ffbbSmacallan 			uint32_t reg;
108305c8e03aSmacallan 			if (attr & WSATTR_UNDERLINE) {
108405c8e03aSmacallan 				for (i = 0; i < he - 2; i++) {
108505c8e03aSmacallan 					reg = *data8;
108605c8e03aSmacallan 					FBC_WRITE(sc, FFB_FBC_FONT, reg << 24);
108705c8e03aSmacallan 					data8++;
108805c8e03aSmacallan 				}
108905c8e03aSmacallan 				FBC_WRITE(sc, FFB_FBC_FONT, 0xff000000);
109005c8e03aSmacallan 				data8++;
109105c8e03aSmacallan 				reg = *data8;
109205c8e03aSmacallan 				FBC_WRITE(sc, FFB_FBC_FONT, reg << 24);
109305c8e03aSmacallan 			} else {
10949912ffbbSmacallan 				for (i = 0; i < he; i++) {
10959912ffbbSmacallan 					reg = *data8;
10969912ffbbSmacallan 					FBC_WRITE(sc, FFB_FBC_FONT, reg << 24);
10979912ffbbSmacallan 					data8++;
10989912ffbbSmacallan 				}
109905c8e03aSmacallan 			}
11009912ffbbSmacallan 			break;
11019912ffbbSmacallan 		}
11029912ffbbSmacallan 		case 2: {
11039912ffbbSmacallan 			uint16_t *data16 = data;
11049912ffbbSmacallan 			uint32_t reg;
110505c8e03aSmacallan 			if (attr & WSATTR_UNDERLINE) {
110605c8e03aSmacallan 				for (i = 0; i < he - 2; i++) {
110705c8e03aSmacallan 					reg = *data16;
110805c8e03aSmacallan 					FBC_WRITE(sc, FFB_FBC_FONT, reg << 16);
110905c8e03aSmacallan 					data16++;
111005c8e03aSmacallan 				}
111105c8e03aSmacallan 				FBC_WRITE(sc, FFB_FBC_FONT, 0xffff0000);
111205c8e03aSmacallan 				data16++;
111305c8e03aSmacallan 				reg = *data16;
111405c8e03aSmacallan 				FBC_WRITE(sc, FFB_FBC_FONT, reg << 16);
111505c8e03aSmacallan 			} else {
11169912ffbbSmacallan 				for (i = 0; i < he; i++) {
11179912ffbbSmacallan 					reg = *data16;
11189912ffbbSmacallan 					FBC_WRITE(sc, FFB_FBC_FONT, reg << 16);
11199912ffbbSmacallan 					data16++;
11209912ffbbSmacallan 				}
112105c8e03aSmacallan 			}
11229912ffbbSmacallan 			break;
11239912ffbbSmacallan 		}
11249912ffbbSmacallan 	}
1125b1dfce18Smacallan }
1126b1dfce18Smacallan 
1127fa70cee0Smacallan /* alpha font */
1128b1dfce18Smacallan void
ffb_putchar_aa(void * cookie,int row,int col,u_int c,long attr)1129b1dfce18Smacallan ffb_putchar_aa(void *cookie, int row, int col, u_int c, long attr)
1130b1dfce18Smacallan {
1131b1dfce18Smacallan 	struct rasops_info *ri = cookie;
1132b1dfce18Smacallan 	struct vcons_screen *scr = ri->ri_hw;
1133b1dfce18Smacallan 	struct wsdisplay_font *font = PICK_FONT(ri, c);
1134b1dfce18Smacallan 	struct ffb_softc *sc = scr->scr_cookie;
1135fa70cee0Smacallan 	volatile uint32_t *dest, *ddest;
1136b1dfce18Smacallan 	uint8_t *data8;
1137b1dfce18Smacallan 	uint32_t fg, bg;
1138b1dfce18Smacallan 	int i;
1139b1dfce18Smacallan 	int x, y, wi, he;
1140fa70cee0Smacallan 	uint32_t alpha = 0x80;
1141fa70cee0Smacallan 	int j;
1142fa70cee0Smacallan 
1143b1dfce18Smacallan 	if (sc->sc_mode != WSDISPLAYIO_MODE_EMUL)
1144b1dfce18Smacallan 		return;
1145b1dfce18Smacallan 
1146b1dfce18Smacallan 	wi = font->fontwidth;
1147b1dfce18Smacallan 	he = font->fontheight;
1148b1dfce18Smacallan 
1149b1dfce18Smacallan 	if (!CHAR_IN_FONT(c, font))
1150b1dfce18Smacallan 		return;
1151b1dfce18Smacallan 
1152b1dfce18Smacallan 	bg = ri->ri_devcmap[(attr >> 16) & 0xf];
1153b1dfce18Smacallan 	fg = ri->ri_devcmap[(attr >> 24) & 0xf];
1154b1dfce18Smacallan 	x = ri->ri_xorigin + col * wi;
1155b1dfce18Smacallan 	y = ri->ri_yorigin + row * he;
1156b1dfce18Smacallan 
1157b1dfce18Smacallan 	data8 = WSFONT_GLYPH(c, font);
1158b1dfce18Smacallan 
1159fa70cee0Smacallan 	/* first we erase the background */
1160fa70cee0Smacallan 	ffb_ras_fill(sc);
1161fa70cee0Smacallan 	ffb_ras_setfg(sc, bg);
1162fa70cee0Smacallan 	ffb_ras_fifo_wait(sc, 4);
1163fa70cee0Smacallan 	FBC_WRITE(sc, FFB_FBC_BY, y);
1164fa70cee0Smacallan 	FBC_WRITE(sc, FFB_FBC_BX, x);
1165fa70cee0Smacallan 	FBC_WRITE(sc, FFB_FBC_BH, he);
1166fa70cee0Smacallan 	FBC_WRITE(sc, FFB_FBC_BW, wi);
1167fa70cee0Smacallan 
1168fa70cee0Smacallan 	/* if we draw a space we're done */
116905c8e03aSmacallan 	if (c == ' ') goto out;
1170fa70cee0Smacallan 
1171fa70cee0Smacallan 	/* now enable alpha blending */
1172fa70cee0Smacallan 	ffb_ras_setfg(sc, fg);
1173fa70cee0Smacallan 	ffb_ras_fifo_wait(sc, 2);
1174fa70cee0Smacallan 	FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_NEW);
1175fa70cee0Smacallan 
1176fa70cee0Smacallan 	FBC_WRITE(sc, FFB_FBC_PPC,
1177fa70cee0Smacallan 	    FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE | FBC_PPC_ACE_DIS |
1178fa70cee0Smacallan 	    FBC_PPC_APE_DIS | FBC_PPC_DCE_DIS | FBC_PPC_CS_CONST |
1179fa70cee0Smacallan 	    FBC_PPC_ABE_ENA | FBC_PPC_XS_VAR);
1180fa70cee0Smacallan 	/*
1181b1dfce18Smacallan 	 * we have to wait for both the rectangle drawing op above and the
1182b1dfce18Smacallan 	 * FFB_FBC_PPC write to finish before mucking around in the SFB aperture
1183fa70cee0Smacallan 	 */
1184fa70cee0Smacallan 	ffb_ras_wait(sc);
1185fa70cee0Smacallan 
1186fa70cee0Smacallan 	/* ... and draw the character */
1187b1dfce18Smacallan 	dest = sc->sc_sfb32 + (y << 11) + x;
1188fa70cee0Smacallan 	for (i = 0; i < he; i++) {
1189fa70cee0Smacallan 		ddest = dest;
1190fa70cee0Smacallan 		for (j = 0; j < wi; j++) {
1191fa70cee0Smacallan 			alpha = *data8;
1192fa70cee0Smacallan 			/*
1193b1dfce18Smacallan 			 * We set the colour source to constant above so we only
1194b1dfce18Smacallan 			 * have to write the alpha channel here and the colour
1195b1dfce18Smacallan 			 * comes from the FG register. It would be nice if we
1196b1dfce18Smacallan 			 * could just use the SFB8X aperture and memcpy() the
1197b1dfce18Smacallan 			 * alpha map line by line but for some strange reason
1198b1dfce18Smacallan 			 * that will take colour info from the framebuffer even
1199b1dfce18Smacallan 			 * if we set the FBC_PPC_CS_CONST bit above.
1200fa70cee0Smacallan 			 */
1201fa70cee0Smacallan 			*ddest = alpha << 24;
1202fa70cee0Smacallan 			data8++;
1203fa70cee0Smacallan 			ddest++;
1204fa70cee0Smacallan 		}
1205fa70cee0Smacallan 		dest += 2048;
1206fa70cee0Smacallan 	}
120705c8e03aSmacallan out:
120805c8e03aSmacallan 	/* check if we need to draw an underline */
120905c8e03aSmacallan 	if (attr & WSATTR_UNDERLINE) {
121005c8e03aSmacallan 		dest =  sc->sc_sfb32 + ((y + he - 2) << 11) + x;
121105c8e03aSmacallan 		for (i = 0; i < wi; i++) {
121205c8e03aSmacallan 			*dest = 0xa0000000;
121305c8e03aSmacallan 			dest++;
121405c8e03aSmacallan 		}
121505c8e03aSmacallan 	}
1216d2c9943bSmacallan }
1217d2c9943bSmacallan 
1218d2c9943bSmacallan int
ffb_allocattr(void * cookie,int fg,int bg,int flags,long * attrp)1219d2c9943bSmacallan ffb_allocattr(void *cookie, int fg, int bg, int flags, long *attrp)
1220d2c9943bSmacallan {
1221d2c9943bSmacallan 	if ((fg == 0) && (bg == 0))
1222d2c9943bSmacallan 	{
1223d2c9943bSmacallan 		fg = WS_DEFAULT_FG;
1224d2c9943bSmacallan 		bg = WS_DEFAULT_BG;
1225d2c9943bSmacallan 	}
1226d2c9943bSmacallan 	if (flags & WSATTR_REVERSE) {
122705c8e03aSmacallan 		*attrp = (bg & 0xff) << 24 | (fg & 0xff) << 16;
1228d2c9943bSmacallan 	} else
122905c8e03aSmacallan 		*attrp = (fg & 0xff) << 24 | (bg & 0xff) << 16;
123005c8e03aSmacallan 	if (flags & WSATTR_UNDERLINE)
123105c8e03aSmacallan 		*attrp |= WSATTR_UNDERLINE;
1232d2c9943bSmacallan 	return 0;
1233d2c9943bSmacallan }
1234d2c9943bSmacallan 
1235d2c9943bSmacallan void
ffb_init_screen(void * cookie,struct vcons_screen * scr,int existing,long * defattr)123602fbfe26Smartin ffb_init_screen(void *cookie, struct vcons_screen *scr,
1237d2c9943bSmacallan     int existing, long *defattr)
1238d2c9943bSmacallan {
123902fbfe26Smartin 	struct ffb_softc *sc = cookie;
124002fbfe26Smartin 	struct rasops_info *ri = &scr->scr_ri;
1241d2c9943bSmacallan 
1242d2c9943bSmacallan 	ri->ri_depth = 32;
1243d2c9943bSmacallan 	ri->ri_width = sc->sc_width;
1244d2c9943bSmacallan 	ri->ri_height = sc->sc_height;
1245d2c9943bSmacallan 	ri->ri_stride = sc->sc_linebytes;
124605c8e03aSmacallan 	ri->ri_flg = RI_CENTER | RI_ENABLE_ALPHA | RI_PREFER_ALPHA;
1247d2c9943bSmacallan 
12489912ffbbSmacallan 	/*
12499912ffbbSmacallan 	 * we can't accelerate copycols() so instead of falling back to
12509912ffbbSmacallan 	 * software use vcons' putchar() based implementation
12519912ffbbSmacallan 	 */
1252733da76fSmacallan 	scr->scr_flags |= VCONS_NO_COPYCOLS | VCONS_LOADFONT;
1253733da76fSmacallan 
12543bdd0f03Smacallan #ifdef VCONS_DRAW_INTR
12553bdd0f03Smacallan         scr->scr_flags |= VCONS_DONT_READ;
12563bdd0f03Smacallan #endif
1257342707d5Sjdc 	DPRINTF(("ffb_init_screen: addr: %08lx\n",(ulong)ri->ri_bits));
1258342707d5Sjdc 
1259c6ba58b3Smacallan 	/* explicitly request BGR in case the default changes */
1260c6ba58b3Smacallan 	ri->ri_rnum = 8;
1261c6ba58b3Smacallan 	ri->ri_gnum = 8;
1262c6ba58b3Smacallan 	ri->ri_bnum = 8;
1263c6ba58b3Smacallan 	ri->ri_rpos = 0;
1264c6ba58b3Smacallan 	ri->ri_gpos = 8;
1265c6ba58b3Smacallan 	ri->ri_bpos = 16;
1266c6ba58b3Smacallan 
12671fa79ac8Smacallan 	rasops_init(ri, 0, 0);
1268d2c9943bSmacallan 	rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight,
1269d2c9943bSmacallan 		    sc->sc_width / ri->ri_font->fontwidth);
1270733da76fSmacallan 	ri->ri_caps = WSSCREEN_WSCOLORS | WSSCREEN_UNDERLINE |
1271733da76fSmacallan 	              WSSCREEN_REVERSE | WSSCREEN_RESIZE;
1272d2c9943bSmacallan 
1273d2c9943bSmacallan 	/* enable acceleration */
1274d2c9943bSmacallan 	ri->ri_ops.copyrows = ffb_ras_copyrows;
1275d2c9943bSmacallan 	ri->ri_ops.eraserows = ffb_ras_eraserows;
1276d2c9943bSmacallan 	ri->ri_ops.erasecols = ffb_ras_erasecols;
1277d2c9943bSmacallan 	ri->ri_ops.cursor = ffb_cursor;
1278d2c9943bSmacallan 	ri->ri_ops.allocattr = ffb_allocattr;
1279b1dfce18Smacallan 	if (FONT_IS_ALPHA(ri->ri_font)) {
1280b1dfce18Smacallan 		ri->ri_ops.putchar = ffb_putchar_aa;
1281b1dfce18Smacallan 	} else
1282b1dfce18Smacallan 		ri->ri_ops.putchar = ffb_putchar_mono;
1283d2c9943bSmacallan }
1284342707d5Sjdc 
1285342707d5Sjdc /* I2C bitbanging */
ffb_i2cbb_set_bits(void * cookie,uint32_t bits)1286342707d5Sjdc static void ffb_i2cbb_set_bits(void *cookie, uint32_t bits)
1287342707d5Sjdc {
1288342707d5Sjdc 	struct ffb_softc *sc = cookie;
1289342707d5Sjdc 
1290342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_CFG_MPDATA);
1291342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, bits);
1292342707d5Sjdc }
1293342707d5Sjdc 
ffb_i2cbb_set_dir(void * cookie,uint32_t dir)1294342707d5Sjdc static void ffb_i2cbb_set_dir(void *cookie, uint32_t dir)
1295342707d5Sjdc {
1296342707d5Sjdc 	/* Nothing to do */
1297342707d5Sjdc }
1298342707d5Sjdc 
ffb_i2cbb_read(void * cookie)1299342707d5Sjdc static uint32_t ffb_i2cbb_read(void *cookie)
1300342707d5Sjdc {
1301342707d5Sjdc 	struct ffb_softc *sc = cookie;
1302342707d5Sjdc 	uint32_t bits;
1303342707d5Sjdc 
1304342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_CFG_MPSENSE);
1305342707d5Sjdc 	bits = DAC_READ(sc, FFB_DAC_VALUE);
1306342707d5Sjdc 
1307342707d5Sjdc 	return bits;
1308342707d5Sjdc }
1309342707d5Sjdc 
1310342707d5Sjdc /* higher level I2C stuff */
1311342707d5Sjdc static int
ffb_i2c_send_start(void * cookie,int flags)1312342707d5Sjdc ffb_i2c_send_start(void *cookie, int flags)
1313342707d5Sjdc {
1314342707d5Sjdc 	return (i2c_bitbang_send_start(cookie, flags, &ffb_i2cbb_ops));
1315342707d5Sjdc }
1316342707d5Sjdc 
1317342707d5Sjdc static int
ffb_i2c_send_stop(void * cookie,int flags)1318342707d5Sjdc ffb_i2c_send_stop(void *cookie, int flags)
1319342707d5Sjdc {
1320342707d5Sjdc 
1321342707d5Sjdc 	return (i2c_bitbang_send_stop(cookie, flags, &ffb_i2cbb_ops));
1322342707d5Sjdc }
1323342707d5Sjdc 
1324342707d5Sjdc static int
ffb_i2c_initiate_xfer(void * cookie,i2c_addr_t addr,int flags)1325342707d5Sjdc ffb_i2c_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
1326342707d5Sjdc {
1327342707d5Sjdc 	/*
1328342707d5Sjdc 	 * for some reason i2c_bitbang_initiate_xfer left-shifts
1329342707d5Sjdc 	 * the I2C-address and then sets the direction bit
1330342707d5Sjdc 	 */
1331342707d5Sjdc 	return (i2c_bitbang_initiate_xfer(cookie, addr, flags,
1332342707d5Sjdc 	    &ffb_i2cbb_ops));
1333342707d5Sjdc }
1334342707d5Sjdc 
1335342707d5Sjdc static int
ffb_i2c_read_byte(void * cookie,uint8_t * valp,int flags)1336342707d5Sjdc ffb_i2c_read_byte(void *cookie, uint8_t *valp, int flags)
1337342707d5Sjdc {
1338342707d5Sjdc 	return (i2c_bitbang_read_byte(cookie, valp, flags, &ffb_i2cbb_ops));
1339342707d5Sjdc }
1340342707d5Sjdc 
1341342707d5Sjdc static int
ffb_i2c_write_byte(void * cookie,uint8_t val,int flags)1342342707d5Sjdc ffb_i2c_write_byte(void *cookie, uint8_t val, int flags)
1343342707d5Sjdc {
1344342707d5Sjdc 	return (i2c_bitbang_write_byte(cookie, val, flags, &ffb_i2cbb_ops));
1345342707d5Sjdc }
1346342707d5Sjdc 
1347342707d5Sjdc 
1348342707d5Sjdc #define TVC_READ_LIMIT	100000
1349342707d5Sjdc int
ffb_tgc_disable(struct ffb_softc * sc)1350342707d5Sjdc ffb_tgc_disable(struct ffb_softc *sc)
1351342707d5Sjdc {
1352342707d5Sjdc 	int i;
1353342707d5Sjdc 
1354342707d5Sjdc 	/* Is the timing generator disabled? */
1355342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
1356342707d5Sjdc 	if (!(DAC_READ(sc, FFB_DAC_VALUE) & FFB_DAC_TGC_TIMING_ENABLE))
1357342707d5Sjdc 		return 1;
1358342707d5Sjdc 
1359342707d5Sjdc 	/* If not, disable it when the vertical counter reaches 0 */
1360342707d5Sjdc 	for (i = 0; i < TVC_READ_LIMIT; i++) {
1361342707d5Sjdc 		DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TVC);
1362342707d5Sjdc 		if (!DAC_READ(sc, FFB_DAC_VALUE)) {
1363342707d5Sjdc 			DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
1364342707d5Sjdc 			DAC_WRITE(sc, FFB_DAC_VALUE, 0);
1365342707d5Sjdc 			return 1;
1366342707d5Sjdc 		}
1367342707d5Sjdc 	}
1368342707d5Sjdc 	return 0;
1369342707d5Sjdc }
1370342707d5Sjdc 
1371342707d5Sjdc /*
1372342707d5Sjdc  * PLL Control Register values:
1373342707d5Sjdc  *	M)ultiplier = bits 0:6 + 1
1374342707d5Sjdc  *	D)ivisor = bits 7:10 + 1
1375342707d5Sjdc  *	P)ost divisor = bits 11:13 (000 = 1, 001 = 2, 010 = 4, 011 = 8)
1376342707d5Sjdc  *	Frequency = 13.5 * M / D / P
1377342707d5Sjdc  */
1378342707d5Sjdc #define FFB_PLL_FREQ	13500000
1379342707d5Sjdc void
ffb_get_pclk(int request,uint32_t * pll,int * diff)1380342707d5Sjdc ffb_get_pclk(int request, uint32_t *pll, int *diff)
1381342707d5Sjdc {
1382342707d5Sjdc 	int m, d, p, f, hex = 0, curdiff;
1383342707d5Sjdc 
1384342707d5Sjdc 	*diff = 100000000;
1385342707d5Sjdc 
1386342707d5Sjdc 	for (m = 32; m <= 80; m++) {
1387342707d5Sjdc 		for (d = 4; d <= 11; d++) {
1388342707d5Sjdc 			for (p = 1; p <= 8; p = p << 1) {
1389342707d5Sjdc 				switch (p) {
1390342707d5Sjdc 				case 1:
1391342707d5Sjdc 					hex = 0x4000 + (d << 7) + m;
1392342707d5Sjdc 					break;
1393342707d5Sjdc 				case 2:
1394342707d5Sjdc 					hex = 0x4800 + (d << 7) + m;
1395342707d5Sjdc 					break;
1396342707d5Sjdc 				case 4:
1397342707d5Sjdc 					hex = 0x5000 + (d << 7) + m;
1398342707d5Sjdc 					break;
1399342707d5Sjdc 				case 8:
1400342707d5Sjdc 					hex = 0x6000 + (d << 7) + m;
1401342707d5Sjdc 					break;
1402342707d5Sjdc 				}
1403342707d5Sjdc 				f = 13500000 * m / d / p;
1404342707d5Sjdc 				if (f == request) {
1405342707d5Sjdc 					*diff = 0;
1406342707d5Sjdc 					*pll = hex;
1407342707d5Sjdc 					return;
1408342707d5Sjdc 				} else {
1409342707d5Sjdc 					curdiff = abs(request - f);
1410342707d5Sjdc 					if (curdiff < *diff) {
1411342707d5Sjdc 						*diff = curdiff;
1412342707d5Sjdc 						*pll = hex;
1413342707d5Sjdc 					}
1414342707d5Sjdc 				}
1415342707d5Sjdc 			}
1416342707d5Sjdc 		}
1417342707d5Sjdc 	}
1418342707d5Sjdc }
1419342707d5Sjdc 
1420342707d5Sjdc /*
1421342707d5Sjdc  * Details of the FFB RAMDAC are contained in the Brooktree BT497/498
1422342707d5Sjdc  * and in the Connexant BT497A/498A documentation.
1423342707d5Sjdc  *
1424342707d5Sjdc  * VESA timings to FFB register conversion:
1425342707d5Sjdc  *	If interleave = 4/2:1 then x = 2, if interleave = 8/2:1 then x = 4
1426342707d5Sjdc  *	VBE = VBS - vres = (sync pulse - 1) + back porch
1427342707d5Sjdc  *	VBS = VSS - front porch = (sync pulse - 1) + back porch + vres
1428342707d5Sjdc  *	VSE = sync pulse - 1
1429342707d5Sjdc  *	VSS = (sync pulse - 1) + back porch + vres + front porch
1430342707d5Sjdc  *	HRE = HSS - HSE - 1
1431342707d5Sjdc  *	HBE = (sync pulse + back porch) / x - 1
1432342707d5Sjdc  *	HBS = (sync pulse + back porch + hres) / x - 1
1433342707d5Sjdc  *	HSE = sync pulse / x - 1
1434342707d5Sjdc  *	HSS = (sync pulse + back porch + hres + front porch) / x - 1
1435342707d5Sjdc  *	HCE = HBS - 4
1436342707d5Sjdc  *	HCS = HBE - 4
1437342707d5Sjdc  *	EPE = EIE = EIS = 0 (for all non-interlaced modes)
1438342707d5Sjdc  *
1439342707d5Sjdc  * Note, that 8/2:1 Single Buffered Interleaving is only supported by the
1440342707d5Sjdc  * double-buffered FFB (Creator3D), and at higher resolutions than 1280x1024
1441342707d5Sjdc  *
1442342707d5Sjdc  * Note, that the timing generator should be disabled and re-enabled when the
1443342707d5Sjdc  * the timing parameter registers are being programmed.  Stopping the timing
1444342707d5Sjdc  * generator should only be done when the vertical counter is zero.
1445342707d5Sjdc  */
1446342707d5Sjdc #define DIVIDE(x,y)	(((x) + ((y) / 2)) / (y))
1447342707d5Sjdc int
ffb_set_vmode(struct ffb_softc * sc,struct videomode * mode,int btype,int * hres,int * vres)1448342707d5Sjdc ffb_set_vmode(struct ffb_softc *sc, struct videomode *mode, int btype,
1449342707d5Sjdc     int *hres, int *vres)
1450342707d5Sjdc {
1451342707d5Sjdc 	int diff;
1452342707d5Sjdc 	uint32_t fp, sp, bp, x;
1453342707d5Sjdc 	uint32_t pll, pfc, ucl, dcl, tgc;
1454342707d5Sjdc 	uint32_t vbe, vbs, vse, vss, hre, hbe, hbs, hse, hss, hce, hcs;
1455342707d5Sjdc 	uint32_t epe, eie, eis;
1456342707d5Sjdc 	uint32_t fbcfg0;
1457342707d5Sjdc 
1458342707d5Sjdc 	DPRINTF(("ffb_set_vmode: %dx%d@%d", mode->hdisplay, mode->vdisplay,
1459342707d5Sjdc 	    DIVIDE(DIVIDE(mode->dot_clock * 1000,
1460342707d5Sjdc 	    mode->htotal), mode->vtotal)));
1461342707d5Sjdc 	DPRINTF((" (%d %d %d %d %d %d %d",
1462342707d5Sjdc 	    mode->dot_clock, mode->hsync_start, mode->hsync_end, mode->htotal,
1463342707d5Sjdc 	    mode->vsync_start, mode->vsync_end, mode->vtotal));
1464342707d5Sjdc 	DPRINTF((" %s%sH %s%sV)\n",
1465342707d5Sjdc 	    mode->flags & VID_PHSYNC ? "+" : "",
1466342707d5Sjdc 	    mode->flags & VID_NHSYNC ? "-" : "",
1467342707d5Sjdc 	    mode->flags & VID_PVSYNC ? "+" : "",
1468342707d5Sjdc 	    mode->flags & VID_NVSYNC ? "-" : ""));
1469342707d5Sjdc 
1470342707d5Sjdc 	/* We don't handle interlaced or doublescan (yet) */
1471342707d5Sjdc 	if ((mode->flags & VID_INTERLACE) || (mode->flags & VID_DBLSCAN))
1472342707d5Sjdc 		return 0;
1473342707d5Sjdc 
1474342707d5Sjdc 	/* Only Creator3D can be set to > 1280x1024 */
1475342707d5Sjdc 	if(((sc->sc_type == FFB_CREATOR && !((btype & 7) == 3)) ||
1476342707d5Sjdc 	    sc->sc_type == FFB_AFB)
1477342707d5Sjdc 	    && (mode->hdisplay > 1280 || mode->vdisplay > 1024))
1478342707d5Sjdc 		return 0;
1479342707d5Sjdc 	/* Creator3D can be set to <= 1920x1360 */
1480342707d5Sjdc 	if (mode->hdisplay > 1920 || mode->vdisplay > 1360)
1481342707d5Sjdc 		return 0;
1482342707d5Sjdc 
1483342707d5Sjdc 	/*
1484342707d5Sjdc 	 * Look for a matching pixel clock and set PLL Control.
1485342707d5Sjdc 	 * XXX: 640x480@60 is 25175000 in modelines but 25125000 in the
1486342707d5Sjdc 	 * FFB PROM, and the closest match to 25175000 (0x4da9/25159090)
1487342707d5Sjdc 	 * does not work.  So, use the PROM value instead.
1488342707d5Sjdc 	 */
1489342707d5Sjdc 	if (mode->hdisplay == 640 && mode->vdisplay == 480 &&
1490342707d5Sjdc 	    mode->dot_clock == 25175) {
1491342707d5Sjdc 		DPRINTF(("ffb_set_vmode: 640x480@60: adjusted dot clock\n"));
1492342707d5Sjdc 		mode->dot_clock = 25125;
1493342707d5Sjdc 	}
1494342707d5Sjdc 	ffb_get_pclk(mode->dot_clock * 1000, &pll, &diff);
1495342707d5Sjdc 	if (diff > 250000)
1496342707d5Sjdc 		return 0;
1497342707d5Sjdc 
1498342707d5Sjdc 	/* Pixel Format Control, User Control and FBC Configuration. */
1499342707d5Sjdc 	if (mode->hdisplay > 1280) {
1500342707d5Sjdc 		pfc = FFB_DAC_PIX_FMT_821;
1501342707d5Sjdc 		ucl = FFB_DAC_USR_CTRL_OVERLAY | FFB_DAC_USR_CTRL_WMODE_C;
1502342707d5Sjdc 		x = 4;
1503342707d5Sjdc 		fbcfg0 = FBC_READ(sc, FFB_FBC_FBCFG0) | FBC_CFG0_DOUBLE_BUF;
1504342707d5Sjdc 	} else {
1505342707d5Sjdc 		pfc = FFB_DAC_PIX_FMT_421;
1506342707d5Sjdc 		/* Only Creator3D and Elite3D can have double-buffer */
1507342707d5Sjdc 		if ((sc->sc_type == FFB_CREATOR && !((btype & 7) == 3)))
1508342707d5Sjdc 			ucl = 0;
1509342707d5Sjdc 		else
1510342707d5Sjdc 			ucl = FFB_DAC_USR_CTRL_DOUBLE;
1511342707d5Sjdc 		ucl |= (FFB_DAC_USR_CTRL_OVERLAY | FFB_DAC_USR_CTRL_WMODE_S8);
1512342707d5Sjdc 		x = 2;
1513342707d5Sjdc 		fbcfg0 = FBC_READ(sc, FFB_FBC_FBCFG0) | FBC_CFG0_SINGLE_BUF;
1514342707d5Sjdc 	}
1515342707d5Sjdc 
1516342707d5Sjdc 	/* DAC Control and Timing Generator Control */
1517d5f801e0Sjdc 	if (mode->flags & VID_PVSYNC)
1518d5f801e0Sjdc 		dcl = FFB_DAC_DAC_CTRL_POS_VSYNC;
1519d5f801e0Sjdc 	else
1520342707d5Sjdc 		dcl = 0;
1521342707d5Sjdc 	tgc = 0;
1522342707d5Sjdc #define EDID_VID_INP	sc->sc_edid_info.edid_video_input
1523d5f801e0Sjdc 	if ((EDID_VID_INP & EDID_VIDEO_INPUT_COMPOSITE_SYNC)) {
1524d5f801e0Sjdc 		dcl |= FFB_DAC_DAC_CTRL_VSYNC_DIS;
1525d5f801e0Sjdc 		tgc = FFB_DAC_TGC_EQUAL_DISABLE;
1526d5f801e0Sjdc 	} else {
1527342707d5Sjdc 		dcl |= FFB_DAC_DAC_CTRL_SYNC_G;
15288902e570Sjdc 		if (EDID_VID_INP & EDID_VIDEO_INPUT_SEPARATE_SYNCS)
1529342707d5Sjdc 			tgc |= FFB_DAC_TGC_VSYNC_DISABLE;
1530d5f801e0Sjdc 		else
1531d5f801e0Sjdc 			tgc = FFB_DAC_TGC_EQUAL_DISABLE;
1532342707d5Sjdc 	}
1533342707d5Sjdc 	if (EDID_VID_INP & EDID_VIDEO_INPUT_BLANK_TO_BLACK)
1534342707d5Sjdc 		dcl |= FFB_DAC_DAC_CTRL_PED_ENABLE;
1535342707d5Sjdc 	tgc |= (FFB_DAC_TGC_VIDEO_ENABLE | FFB_DAC_TGC_TIMING_ENABLE |
1536342707d5Sjdc 	    FFB_DAC_TGC_MASTER_ENABLE);
1537342707d5Sjdc 
1538342707d5Sjdc 	/* Vertical timing */
1539342707d5Sjdc 	fp = mode->vsync_start - mode->vdisplay;
1540342707d5Sjdc 	sp = mode->vsync_end - mode->vsync_start;
1541342707d5Sjdc 	bp = mode->vtotal - mode->vsync_end;
1542342707d5Sjdc 
1543342707d5Sjdc 	vbe = sp - 1 + bp;
1544342707d5Sjdc 	vbs = sp - 1 + bp + mode->vdisplay;
1545342707d5Sjdc 	vse = sp - 1;
1546342707d5Sjdc 	vss = sp  - 1 + bp + mode->vdisplay + fp;
1547342707d5Sjdc 
1548342707d5Sjdc 	/* Horizontal timing */
1549342707d5Sjdc 	fp = mode->hsync_start - mode->hdisplay;
1550342707d5Sjdc 	sp = mode->hsync_end - mode->hsync_start;
1551342707d5Sjdc 	bp = mode->htotal - mode->hsync_end;
1552342707d5Sjdc 
1553342707d5Sjdc 	hbe = (sp + bp) / x - 1;
1554342707d5Sjdc 	hbs = (sp + bp + mode->hdisplay) / x - 1;
1555342707d5Sjdc 	hse = sp / x - 1;
1556342707d5Sjdc 	hss = (sp + bp + mode->hdisplay + fp) / x -1;
1557342707d5Sjdc 	hre = hss - hse - 1;
1558342707d5Sjdc 	hce = hbs - 4;
1559342707d5Sjdc 	hcs = hbe - 4;
1560342707d5Sjdc 
1561342707d5Sjdc 	/* Equalisation (interlaced modes) */
1562342707d5Sjdc 	epe = 0;
1563342707d5Sjdc 	eie = 0;
1564342707d5Sjdc 	eis = 0;
1565342707d5Sjdc 
1566342707d5Sjdc 	DPRINTF(("ffb_set_vmode: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n",
1567342707d5Sjdc 	    pll, pfc, ucl, dcl, tgc));
1568342707d5Sjdc 	DPRINTF(("\t0x%04x 0x%04x 0x%04x 0x%04x\n", vbe, vbs, vse, vss));
1569342707d5Sjdc 	DPRINTF(("\t0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n",
1570342707d5Sjdc 	    hre, hbe, hbs, hse, hss, hce, hcs));
1571342707d5Sjdc 	DPRINTF(("\t0x%04x 0x%04x 0x%04x\n", epe, eie, eis));
1572342707d5Sjdc 
1573342707d5Sjdc 	if (!ffb_tgc_disable(sc)) {
1574342707d5Sjdc 		DPRINTF(("ffb_set_vmode: failed to disable TGC register\n"));
1575342707d5Sjdc 		return 0;
1576342707d5Sjdc 	}
1577342707d5Sjdc 
1578342707d5Sjdc 	/*
1579342707d5Sjdc 	 * Program the mode registers.
1580342707d5Sjdc 	 * Program the timing generator last, as that re-enables output.
1581342707d5Sjdc 	 * Note, that a read to/write from a register increments the
1582342707d5Sjdc 	 * register address to the next register automatically.
1583342707d5Sjdc 	 */
1584342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_PLL_CTRL);
1585342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, pll);
1586342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_PIX_FMT);
1587342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, pfc);
1588342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, ucl);
1589342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_DAC_CTRL);
1590342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, dcl);
1591342707d5Sjdc 
1592342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_VBE);
1593342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, vbe);
1594342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, vbs);
1595342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, vse);
1596342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, vss);
1597342707d5Sjdc 
1598342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, hre);
1599342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, hbe);
1600342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, hbs);
1601342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, hse);
1602342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, hss);
1603342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, hce);
1604342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, hcs);
1605342707d5Sjdc 
1606342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, epe);
1607342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, eie);
1608342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, eis);
1609342707d5Sjdc 
1610342707d5Sjdc 	FBC_WRITE(sc, FFB_FBC_FBCFG0, fbcfg0);
1611342707d5Sjdc 
1612342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
1613342707d5Sjdc 	DAC_WRITE(sc, FFB_DAC_VALUE, tgc);
16143bdd0f03Smacallan 	DPRINTF(("new tgc: %08x\n", tgc));
1615342707d5Sjdc 
1616342707d5Sjdc 	*hres = mode->hdisplay;
1617342707d5Sjdc 	*vres = mode->vdisplay;
1618342707d5Sjdc 
1619342707d5Sjdc 	printf("%s: video mode set to %d x %d @ %dHz\n",
1620817999e9Schristos 	    device_xname(sc->sc_dev),
1621342707d5Sjdc 	    mode->hdisplay, mode->vdisplay,
1622342707d5Sjdc 	    DIVIDE(DIVIDE(mode->dot_clock * 1000,
1623342707d5Sjdc 	    mode->htotal), mode->vtotal));
1624342707d5Sjdc 
1625342707d5Sjdc 	return 1;
1626342707d5Sjdc }
1627