xref: /netbsd-src/sys/arch/sgimips/dev/crmfb.c (revision aad9773e38ed2370a628a6416e098f9008fc10a7)
1 /* $NetBSD: crmfb.c,v 1.38 2014/09/02 15:44:44 macallan Exp $ */
2 
3 /*-
4  * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
5  *               2008 Michael Lorenz <macallan@netbsd.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /*
31  * SGI-CRM (O2) Framebuffer driver
32  */
33 
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: crmfb.c,v 1.38 2014/09/02 15:44:44 macallan Exp $");
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/device.h>
40 #include <sys/malloc.h>
41 
42 #define _SGIMIPS_BUS_DMA_PRIVATE
43 #include <machine/autoconf.h>
44 #include <sys/bus.h>
45 #include <machine/machtype.h>
46 #include <machine/vmparam.h>
47 
48 #include <dev/arcbios/arcbios.h>
49 #include <dev/arcbios/arcbiosvar.h>
50 
51 #include <dev/wscons/wsdisplayvar.h>
52 #include <dev/wscons/wsconsio.h>
53 #include <dev/wsfont/wsfont.h>
54 #include <dev/rasops/rasops.h>
55 #include <dev/wscons/wsdisplay_vconsvar.h>
56 
57 #include <dev/i2c/i2cvar.h>
58 #include <dev/i2c/i2c_bitbang.h>
59 #include <dev/i2c/ddcvar.h>
60 #include <dev/videomode/videomode.h>
61 #include <dev/videomode/edidvar.h>
62 
63 #include <arch/sgimips/dev/crmfbreg.h>
64 
65 #include "opt_crmfb.h"
66 
67 #ifdef CRMFB_DEBUG
68 #define DPRINTF printf
69 #else
70 #define DPRINTF while (0) printf
71 #endif
72 
73 struct wsscreen_descr crmfb_defaultscreen = {
74 	"default",
75 	0, 0,
76 	NULL,
77 	8, 16,
78 	WSSCREEN_WSCOLORS,
79 	NULL,
80 };
81 
82 const struct wsscreen_descr *_crmfb_scrlist[] = {
83 	&crmfb_defaultscreen,
84 };
85 
86 struct wsscreen_list crmfb_screenlist = {
87 	sizeof(_crmfb_scrlist) / sizeof(struct wsscreen_descr *),
88 	_crmfb_scrlist
89 };
90 
91 static struct vcons_screen crmfb_console_screen;
92 
93 static int	crmfb_ioctl(void *, void *, u_long, void *, int, struct lwp *);
94 static paddr_t	crmfb_mmap(void *, void *, off_t, int);
95 static void	crmfb_init_screen(void *, struct vcons_screen *, int, long *);
96 
97 struct wsdisplay_accessops crmfb_accessops = {
98 	crmfb_ioctl,
99 	crmfb_mmap,
100 	NULL,	/* alloc_screen */
101 	NULL,	/* free_screen */
102 	NULL,	/* show_screen */
103 	NULL,	/* load_font */
104 	NULL,	/* pollc */
105 	NULL,	/* scroll */
106 };
107 
108 /* Memory to allocate to SGI-CRM -- remember, this is stolen from
109  * host memory!
110  */
111 #define CRMFB_TILESIZE	(512*128)
112 
113 static int	crmfb_match(device_t, struct cfdata *, void *);
114 static void	crmfb_attach(device_t, device_t, void *);
115 int		crmfb_probe(void);
116 
117 #define KERNADDR(p)	((void *)((p).addr))
118 #define DMAADDR(p)	((p).map->dm_segs[0].ds_addr)
119 
120 #define CRMFB_REG_MASK(msb, lsb) \
121 	( (((uint32_t) 1 << ((msb)-(lsb)+1)) - 1) << (lsb) )
122 
123 
124 struct crmfb_dma {
125 	bus_dmamap_t		map;
126 	void			*addr;
127 	bus_dma_segment_t	segs[1];
128 	int			nsegs;
129 	size_t			size;
130 };
131 
132 struct crmfb_softc {
133 	device_t		sc_dev;
134 	struct vcons_data	sc_vd;
135 	struct i2c_controller	sc_i2c;
136 	int sc_dir;
137 
138 	bus_space_tag_t		sc_iot;
139 	bus_space_handle_t	sc_ioh;
140 	bus_space_handle_t	sc_reh;
141 
142 	bus_dma_tag_t		sc_dmat;
143 
144 	struct crmfb_dma	sc_dma;
145 	struct crmfb_dma	sc_dmai;
146 
147 	int			sc_width;
148 	int			sc_height;
149 	int			sc_depth;
150 	int			sc_console_depth;
151 	int			sc_tiles_x, sc_tiles_y;
152 	uint32_t		sc_fbsize;
153 	int			sc_mte_direction;
154 	int			sc_mte_x_shift;
155 	uint32_t		sc_mte_mode;
156 	uint32_t		sc_de_mode;
157 	uint8_t			*sc_scratch;
158 	paddr_t			sc_linear;
159 	uint32_t		sc_vtflags;
160 	int			sc_wsmode, sc_video_on;
161 	uint8_t			sc_edid_data[128];
162 	struct edid_info 	sc_edid_info;
163 
164 	/* cursor stuff */
165 	int			sc_cur_x;
166 	int			sc_cur_y;
167 	int			sc_hot_x;
168 	int			sc_hot_y;
169 
170 	u_char			sc_cmap_red[256];
171 	u_char			sc_cmap_green[256];
172 	u_char			sc_cmap_blue[256];
173 };
174 
175 static int	crmfb_putcmap(struct crmfb_softc *, struct wsdisplay_cmap *);
176 static int	crmfb_getcmap(struct crmfb_softc *, struct wsdisplay_cmap *);
177 static void	crmfb_set_palette(struct crmfb_softc *,
178 				  int, uint8_t, uint8_t, uint8_t);
179 static int	crmfb_set_curpos(struct crmfb_softc *, int, int);
180 static int	crmfb_gcursor(struct crmfb_softc *, struct wsdisplay_cursor *);
181 static int	crmfb_scursor(struct crmfb_softc *, struct wsdisplay_cursor *);
182 static inline void	crmfb_write_reg(struct crmfb_softc *, int, uint32_t);
183 static inline uint32_t	crmfb_read_reg(struct crmfb_softc *, int);
184 static int	crmfb_wait_dma_idle(struct crmfb_softc *);
185 
186 /* setup video hw in given colour depth */
187 static int	crmfb_setup_video(struct crmfb_softc *, int);
188 static void	crmfb_setup_palette(struct crmfb_softc *);
189 
190 static void crmfb_fill_rect(struct crmfb_softc *, int, int, int, int, uint32_t);
191 static void crmfb_bitblt(struct crmfb_softc *, int, int, int, int, int, int,
192 			 uint32_t);
193 static void crmfb_scroll(struct crmfb_softc *, int, int, int, int, int, int);
194 
195 static void	crmfb_copycols(void *, int, int, int, int);
196 static void	crmfb_erasecols(void *, int, int, int, long);
197 static void	crmfb_copyrows(void *, int, int, int);
198 static void	crmfb_eraserows(void *, int, int, long);
199 static void	crmfb_cursor(void *, int, int, int);
200 static void	crmfb_putchar(void *, int, int, u_int, long);
201 
202 /* I2C glue */
203 static int crmfb_i2c_acquire_bus(void *, int);
204 static void crmfb_i2c_release_bus(void *, int);
205 static int crmfb_i2c_send_start(void *, int);
206 static int crmfb_i2c_send_stop(void *, int);
207 static int crmfb_i2c_initiate_xfer(void *, i2c_addr_t, int);
208 static int crmfb_i2c_read_byte(void *, uint8_t *, int);
209 static int crmfb_i2c_write_byte(void *, uint8_t, int);
210 
211 /* I2C bitbang glue */
212 static void crmfb_i2cbb_set_bits(void *, uint32_t);
213 static void crmfb_i2cbb_set_dir(void *, uint32_t);
214 static uint32_t crmfb_i2cbb_read(void *);
215 
216 static const struct i2c_bitbang_ops crmfb_i2cbb_ops = {
217 	crmfb_i2cbb_set_bits,
218 	crmfb_i2cbb_set_dir,
219 	crmfb_i2cbb_read,
220 	{
221 		CRMFB_I2C_SDA,
222 		CRMFB_I2C_SCL,
223 		0,
224 		1
225 	}
226 };
227 static void crmfb_setup_ddc(struct crmfb_softc *);
228 
229 /* mode setting stuff */
230 static uint32_t calc_pll(int);	/* frequency in kHz */
231 static int crmfb_set_mode(struct crmfb_softc *, const struct videomode *);
232 
233 CFATTACH_DECL_NEW(crmfb, sizeof(struct crmfb_softc),
234     crmfb_match, crmfb_attach, NULL, NULL);
235 
236 static int
237 crmfb_match(device_t parent, struct cfdata *cf, void *opaque)
238 {
239 	return crmfb_probe();
240 }
241 
242 static void
243 crmfb_attach(device_t parent, device_t self, void *opaque)
244 {
245 	struct mainbus_attach_args *ma;
246 	struct crmfb_softc *sc;
247 	struct rasops_info *ri;
248 	struct wsemuldisplaydev_attach_args aa;
249 	uint32_t d, h;
250 	uint16_t *p;
251 	unsigned long v;
252 	long defattr;
253 	const char *consdev;
254 	int rv, i;
255 
256 	sc = device_private(self);
257 	sc->sc_dev = self;
258 
259 	ma = (struct mainbus_attach_args *)opaque;
260 
261 	sc->sc_iot = SGIMIPS_BUS_SPACE_CRIME;
262 	sc->sc_dmat = &sgimips_default_bus_dma_tag;
263 	sc->sc_wsmode = WSDISPLAYIO_MODE_EMUL;
264 
265 	aprint_normal(": SGI CRIME Graphics Display Engine\n");
266 	rv = bus_space_map(sc->sc_iot, ma->ma_addr, 0 /* XXX */,
267 	    BUS_SPACE_MAP_LINEAR, &sc->sc_ioh);
268 	if (rv)
269 		panic("crmfb_attach: can't map I/O space");
270 	rv = bus_space_map(sc->sc_iot, 0x15000000, 0x6000, 0, &sc->sc_reh);
271 	if (rv)
272 		panic("crmfb_attach: can't map rendering engine");
273 
274 	/* determine mode configured by firmware */
275 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_VT_HCMAP);
276 	sc->sc_width = (d >> CRMFB_VT_HCMAP_ON_SHIFT) & 0xfff;
277 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_VT_VCMAP);
278 	sc->sc_height = (d >> CRMFB_VT_VCMAP_ON_SHIFT) & 0xfff;
279 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_TILESIZE);
280 	h = (d >> CRMFB_FRM_TILESIZE_DEPTH_SHIFT) & 0x3;
281 	if (h == 0)
282 		sc->sc_depth = 8;
283 	else if (h == 1)
284 		sc->sc_depth = 16;
285 	else
286 		sc->sc_depth = 32;
287 
288 	if (sc->sc_width == 0 || sc->sc_height == 0) {
289 		/*
290 		 * XXX
291 		 * actually, these days we probably could
292 		 */
293 		aprint_error_dev(sc->sc_dev,
294 		    "device unusable if not setup by firmware\n");
295 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, 0 /* XXX */);
296 		return;
297 	}
298 
299 	aprint_normal_dev(sc->sc_dev, "initial resolution %dx%d\n",
300 	    sc->sc_width, sc->sc_height);
301 
302 	sc->sc_console_depth = 8;
303 
304 	crmfb_setup_ddc(sc);
305 	if ((sc->sc_edid_info.edid_preferred_mode != NULL)) {
306 		if (crmfb_set_mode(sc, sc->sc_edid_info.edid_preferred_mode))
307 			aprint_normal_dev(sc->sc_dev, "using %dx%d\n",
308 			    sc->sc_width, sc->sc_height);
309 	}
310 	/*
311 	 * first determine how many tiles we need
312 	 * in 32bit each tile is 128x128 pixels
313 	 */
314 	sc->sc_tiles_x = (sc->sc_width + 127) >> 7;
315 	sc->sc_tiles_y = (sc->sc_height + 127) >> 7;
316 	sc->sc_fbsize = 0x10000 * sc->sc_tiles_x * sc->sc_tiles_y;
317 
318 	sc->sc_dmai.size = 256 * sizeof(uint16_t);
319 	rv = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmai.size, 65536, 0,
320 	    sc->sc_dmai.segs,
321 	    sizeof(sc->sc_dmai.segs) / sizeof(sc->sc_dmai.segs[0]),
322 	    &sc->sc_dmai.nsegs, BUS_DMA_NOWAIT);
323 	if (rv)
324 		panic("crmfb_attach: can't allocate DMA memory");
325 	rv = bus_dmamem_map(sc->sc_dmat, sc->sc_dmai.segs, sc->sc_dmai.nsegs,
326 	    sc->sc_dmai.size, &sc->sc_dmai.addr,
327 	    BUS_DMA_NOWAIT);
328 	if (rv)
329 		panic("crmfb_attach: can't map DMA memory");
330 	rv = bus_dmamap_create(sc->sc_dmat, sc->sc_dmai.size, 1,
331 	    sc->sc_dmai.size, 0, BUS_DMA_NOWAIT, &sc->sc_dmai.map);
332 	if (rv)
333 		panic("crmfb_attach: can't create DMA map");
334 	rv = bus_dmamap_load(sc->sc_dmat, sc->sc_dmai.map, sc->sc_dmai.addr,
335 	    sc->sc_dmai.size, NULL, BUS_DMA_NOWAIT);
336 	if (rv)
337 		panic("crmfb_attach: can't load DMA map");
338 
339 	/* allocate an extra 64Kb for a linear buffer */
340 	sc->sc_dma.size = 0x10000 * (16 * sc->sc_tiles_x + 1);
341 	rv = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dma.size, 65536, 0,
342 	    sc->sc_dma.segs,
343 	    sizeof(sc->sc_dma.segs) / sizeof(sc->sc_dma.segs[0]),
344 	    &sc->sc_dma.nsegs, BUS_DMA_NOWAIT);
345 	if (rv)
346 		panic("crmfb_attach: can't allocate DMA memory");
347 	rv = bus_dmamem_map(sc->sc_dmat, sc->sc_dma.segs, sc->sc_dma.nsegs,
348 	    sc->sc_dma.size, &sc->sc_dma.addr,
349 	    BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
350 	if (rv)
351 		panic("crmfb_attach: can't map DMA memory");
352 	rv = bus_dmamap_create(sc->sc_dmat, sc->sc_dma.size, 1,
353 	    sc->sc_dma.size, 0, BUS_DMA_NOWAIT, &sc->sc_dma.map);
354 	if (rv)
355 		panic("crmfb_attach: can't create DMA map");
356 	rv = bus_dmamap_load(sc->sc_dmat, sc->sc_dma.map, sc->sc_dma.addr,
357 	    sc->sc_dma.size, NULL, BUS_DMA_NOWAIT);
358 	if (rv)
359 		panic("crmfb_attach: can't load DMA map");
360 
361 	p = KERNADDR(sc->sc_dmai);
362 	v = (unsigned long)DMAADDR(sc->sc_dma);
363 	for (i = 0; i < (sc->sc_tiles_x * sc->sc_tiles_y); i++) {
364 		p[i] = ((uint32_t)v >> 16) + i;
365 	}
366 	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmai.map, 0, sc->sc_dmai.size,
367 	    BUS_DMASYNC_PREWRITE);
368 	sc->sc_scratch = (char *)KERNADDR(sc->sc_dma) + (0xf0000 * sc->sc_tiles_x);
369 	sc->sc_linear = (paddr_t)DMAADDR(sc->sc_dma) + 0x100000 * sc->sc_tiles_x;
370 
371 	aprint_normal_dev(sc->sc_dev, "allocated %d byte fb @ %p (%p)\n",
372 	    sc->sc_fbsize, KERNADDR(sc->sc_dmai), KERNADDR(sc->sc_dma));
373 
374 	crmfb_setup_video(sc, sc->sc_console_depth);
375 	ri = &crmfb_console_screen.scr_ri;
376 	memset(ri, 0, sizeof(struct rasops_info));
377 	sc->sc_video_on = 1;
378 
379 	vcons_init(&sc->sc_vd, sc, &crmfb_defaultscreen, &crmfb_accessops);
380 	sc->sc_vd.init_screen = crmfb_init_screen;
381 	crmfb_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
382 	vcons_init_screen(&sc->sc_vd, &crmfb_console_screen, 1, &defattr);
383 
384 	crmfb_defaultscreen.ncols = ri->ri_cols;
385 	crmfb_defaultscreen.nrows = ri->ri_rows;
386 	crmfb_defaultscreen.textops = &ri->ri_ops;
387 	crmfb_defaultscreen.capabilities = ri->ri_caps;
388 	crmfb_defaultscreen.modecookie = NULL;
389 
390 	crmfb_setup_palette(sc);
391 	crmfb_fill_rect(sc, 0, 0, sc->sc_width, sc->sc_height,
392 	    ri->ri_devcmap[(defattr >> 16) & 0xff]);
393 
394 	consdev = arcbios_GetEnvironmentVariable("ConsoleOut");
395 	if (consdev != NULL && strcmp(consdev, "video()") == 0) {
396 		wsdisplay_cnattach(&crmfb_defaultscreen, ri, 0, 0, defattr);
397 		vcons_replay_msgbuf(&crmfb_console_screen);
398 		aa.console = 1;
399 	} else
400 		aa.console = 0;
401 	aa.scrdata = &crmfb_screenlist;
402 	aa.accessops = &crmfb_accessops;
403 	aa.accesscookie = &sc->sc_vd;
404 
405 	config_found(self, &aa, wsemuldisplaydevprint);
406 
407 	sc->sc_cur_x = 0;
408 	sc->sc_cur_y = 0;
409 	sc->sc_hot_x = 0;
410 	sc->sc_hot_y = 0;
411 
412 	return;
413 }
414 
415 int
416 crmfb_probe(void)
417 {
418 
419         if (mach_type != MACH_SGI_IP32)
420                 return 0;
421 
422 	return 1;
423 }
424 
425 static int
426 crmfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
427 {
428 	struct vcons_data *vd;
429 	struct crmfb_softc *sc;
430 	struct wsdisplay_fbinfo *wdf;
431 	int nmode;
432 
433 	vd = (struct vcons_data *)v;
434 	sc = (struct crmfb_softc *)vd->cookie;
435 
436 	switch (cmd) {
437 	case WSDISPLAYIO_GTYPE:
438 		/* not really, but who cares? */
439 		/* xf86-video-crime does */
440 		*(u_int *)data = WSDISPLAY_TYPE_CRIME;
441 		return 0;
442 	case WSDISPLAYIO_GINFO:
443 		if (vd->active != NULL) {
444 			wdf = (void *)data;
445 			wdf->height = sc->sc_height;
446 			wdf->width = sc->sc_width;
447 			wdf->depth = 32;
448 			wdf->cmsize = 256;
449 			return 0;
450 		} else
451 			return ENODEV;
452 	case WSDISPLAYIO_GETCMAP:
453 		if (sc->sc_depth == 8)
454 			return crmfb_getcmap(sc, (struct wsdisplay_cmap *)data);
455 		else
456 			return EINVAL;
457 	case WSDISPLAYIO_PUTCMAP:
458 		if (sc->sc_depth == 8)
459 			return crmfb_putcmap(sc, (struct wsdisplay_cmap *)data);
460 		else
461 			return EINVAL;
462 	case WSDISPLAYIO_LINEBYTES:
463 		*(u_int *)data = sc->sc_width * sc->sc_depth / 8;
464 		return 0;
465 	case WSDISPLAYIO_SMODE:
466 		nmode = *(int *)data;
467 		if (nmode != sc->sc_wsmode) {
468 			sc->sc_wsmode = nmode;
469 			if (nmode == WSDISPLAYIO_MODE_EMUL) {
470 				crmfb_setup_video(sc, sc->sc_console_depth);
471 				crmfb_setup_palette(sc);
472 				vcons_redraw_screen(vd->active);
473 			} else {
474 				crmfb_setup_video(sc, 32);
475 			}
476 		}
477 		return 0;
478 	case WSDISPLAYIO_SVIDEO:
479 		{
480 			int d = *(int *)data;
481 			if (d == sc->sc_video_on)
482 				return 0;
483 			sc->sc_video_on = d;
484 			if (d == WSDISPLAYIO_VIDEO_ON) {
485 				crmfb_write_reg(sc,
486 				    CRMFB_VT_FLAGS, sc->sc_vtflags);
487 			} else {
488 				/* turn all SYNCs off */
489 				crmfb_write_reg(sc, CRMFB_VT_FLAGS,
490 				    sc->sc_vtflags | CRMFB_VT_FLAGS_VDRV_LOW |
491 				     CRMFB_VT_FLAGS_HDRV_LOW |
492 				     CRMFB_VT_FLAGS_SYNC_LOW);
493 			}
494 		}
495 		return 0;
496 
497 	case WSDISPLAYIO_GVIDEO:
498 		*(int *)data = sc->sc_video_on;
499 		return 0;
500 
501 	case WSDISPLAYIO_GCURPOS:
502 		{
503 			struct wsdisplay_curpos *pos;
504 
505 			pos = (struct wsdisplay_curpos *)data;
506 			pos->x = sc->sc_cur_x;
507 			pos->y = sc->sc_cur_y;
508 		}
509 		return 0;
510 	case WSDISPLAYIO_SCURPOS:
511 		{
512 			struct wsdisplay_curpos *pos;
513 
514 			pos = (struct wsdisplay_curpos *)data;
515 			crmfb_set_curpos(sc, pos->x, pos->y);
516 		}
517 		return 0;
518 	case WSDISPLAYIO_GCURMAX:
519 		{
520 			struct wsdisplay_curpos *pos;
521 
522 			pos = (struct wsdisplay_curpos *)data;
523 			pos->x = 32;
524 			pos->y = 32;
525 		}
526 		return 0;
527 	case WSDISPLAYIO_GCURSOR:
528 		{
529 			struct wsdisplay_cursor *cu;
530 
531 			cu = (struct wsdisplay_cursor *)data;
532 			return crmfb_gcursor(sc, cu);
533 		}
534 	case WSDISPLAYIO_SCURSOR:
535 		{
536 			struct wsdisplay_cursor *cu;
537 
538 			cu = (struct wsdisplay_cursor *)data;
539 			return crmfb_scursor(sc, cu);
540 		}
541 	case WSDISPLAYIO_GET_EDID: {
542 		struct wsdisplayio_edid_info *d = data;
543 
544 		d->data_size = 128;
545 		if (d->buffer_size < 128)
546 			return EAGAIN;
547 		if (sc->sc_edid_data[1] == 0)
548 			return ENODATA;
549 		return copyout(sc->sc_edid_data, d->edid_data, 128);
550 	}
551 	}
552 	return EPASSTHROUGH;
553 }
554 
555 static paddr_t
556 crmfb_mmap(void *v, void *vs, off_t offset, int prot)
557 {
558 	struct vcons_data *vd;
559 	struct crmfb_softc *sc;
560 	paddr_t pa;
561 
562 	vd = (struct vcons_data *)v;
563 	sc = (struct crmfb_softc *)vd->cookie;
564 
565 	/* we probably shouldn't let anyone mmap the framebuffer */
566 #if 1
567 	if (offset >= 0 && offset < (0x100000 * sc->sc_tiles_x)) {
568 		pa = bus_dmamem_mmap(sc->sc_dmat, sc->sc_dma.segs,
569 		    sc->sc_dma.nsegs, offset, prot,
570 		    BUS_DMA_WAITOK | BUS_DMA_COHERENT);
571 		return pa;
572 	}
573 #endif
574 	/*
575 	 * here would the TLBs be but we don't want to show them to userland
576 	 * so we return the page containing the status register
577 	 */
578 	if ((offset >= 0x15000000) && (offset < 0x15002000))
579 		return bus_space_mmap(sc->sc_iot, 0x15004000, 0, prot, 0);
580 	/* now the actual engine registers */
581 	if ((offset >= 0x15002000) && (offset < 0x15005000))
582 		return bus_space_mmap(sc->sc_iot, offset, 0, prot, 0);
583 	/* and now the scratch area */
584 	if ((offset >= 0x15010000) && (offset < 0x15020000))
585 		return bus_dmamem_mmap(sc->sc_dmat, sc->sc_dma.segs,
586 		     sc->sc_dma.nsegs,
587 		     offset + (0x100000 * sc->sc_tiles_x) - 0x15010000,
588 		     prot, BUS_DMA_WAITOK | BUS_DMA_COHERENT);
589 	return -1;
590 }
591 
592 static void
593 crmfb_init_screen(void *c, struct vcons_screen *scr, int existing,
594     long *defattr)
595 {
596 	struct crmfb_softc *sc;
597 	struct rasops_info *ri;
598 
599 	sc = (struct crmfb_softc *)c;
600 	ri = &scr->scr_ri;
601 
602 	ri->ri_flg = RI_CENTER | RI_FULLCLEAR;
603 	ri->ri_depth = sc->sc_console_depth;
604 	ri->ri_width = sc->sc_width;
605 	ri->ri_height = sc->sc_height;
606 	ri->ri_stride = ri->ri_width * (ri->ri_depth / 8);
607 
608 	switch (ri->ri_depth) {
609 	case 8:
610 		ri->ri_flg |= RI_8BIT_IS_RGB;
611 		break;
612 	case 16:
613 		ri->ri_rnum = ri->ri_gnum = ri->ri_bnum = 5;
614 		ri->ri_rpos = 11;
615 		ri->ri_gpos = 6;
616 		ri->ri_bpos = 1;
617 		break;
618 	case 32:
619 		ri->ri_rnum = ri->ri_gnum = ri->ri_bnum = 8;
620 		ri->ri_rpos = 8;
621 		ri->ri_gpos = 16;
622 		ri->ri_bpos = 24;
623 		break;
624 	}
625 
626 	ri->ri_bits = KERNADDR(sc->sc_dma);
627 
628 	if (existing)
629 		ri->ri_flg |= RI_CLEAR;
630 
631 	rasops_init(ri, 0, 0);
632 	ri->ri_caps = WSSCREEN_WSCOLORS;
633 	rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight,
634 	    ri->ri_width / ri->ri_font->fontwidth);
635 	ri->ri_hw = scr;
636 
637 	ri->ri_ops.cursor    = crmfb_cursor;
638 	ri->ri_ops.copyrows  = crmfb_copyrows;
639 	ri->ri_ops.eraserows = crmfb_eraserows;
640 	ri->ri_ops.copycols  = crmfb_copycols;
641 	ri->ri_ops.erasecols = crmfb_erasecols;
642 	ri->ri_ops.putchar   = crmfb_putchar;
643 
644 	return;
645 }
646 
647 static int
648 crmfb_putcmap(struct crmfb_softc *sc, struct wsdisplay_cmap *cm)
649 {
650 	u_int idx, cnt;
651 	u_char r[256], g[256], b[256];
652 	u_char *rp, *gp, *bp;
653 	int rv, i;
654 
655 	idx = cm->index;
656 	cnt = cm->count;
657 
658 	if (idx >= 255 || cnt > 256 || idx + cnt > 256)
659 		return EINVAL;
660 
661 	rv = copyin(cm->red, &r[idx], cnt);
662 	if (rv)
663 		return rv;
664 	rv = copyin(cm->green, &g[idx], cnt);
665 	if (rv)
666 		return rv;
667 	rv = copyin(cm->blue, &b[idx], cnt);
668 	if (rv)
669 		return rv;
670 
671 	memcpy(&sc->sc_cmap_red[idx], &r[idx], cnt);
672 	memcpy(&sc->sc_cmap_green[idx], &g[idx], cnt);
673 	memcpy(&sc->sc_cmap_blue[idx], &b[idx], cnt);
674 
675 	rp = &sc->sc_cmap_red[idx];
676 	gp = &sc->sc_cmap_green[idx];
677 	bp = &sc->sc_cmap_blue[idx];
678 
679 	for (i = 0; i < cnt; i++) {
680 		crmfb_set_palette(sc, idx, *rp, *gp, *bp);
681 		idx++;
682 		rp++, gp++, bp++;
683 	}
684 
685 	return 0;
686 }
687 
688 static int
689 crmfb_getcmap(struct crmfb_softc *sc, struct wsdisplay_cmap *cm)
690 {
691 	u_int idx, cnt;
692 	int rv;
693 
694 	idx = cm->index;
695 	cnt = cm->count;
696 
697 	if (idx >= 255 || cnt > 256 || idx + cnt > 256)
698 		return EINVAL;
699 
700 	rv = copyout(&sc->sc_cmap_red[idx], cm->red, cnt);
701 	if (rv)
702 		return rv;
703 	rv = copyout(&sc->sc_cmap_green[idx], cm->green, cnt);
704 	if (rv)
705 		return rv;
706 	rv = copyout(&sc->sc_cmap_blue[idx], cm->blue, cnt);
707 	if (rv)
708 		return rv;
709 
710 	return 0;
711 }
712 
713 static void
714 crmfb_set_palette(struct crmfb_softc *sc, int reg, uint8_t r, uint8_t g,
715     uint8_t b)
716 {
717 	uint32_t val;
718 
719 	if (reg > 255 || sc->sc_depth != 8)
720 		return;
721 
722 	while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_CMAP_FIFO) >= 63)
723 		DELAY(10);
724 
725 	val = (r << 8) | (g << 16) | (b << 24);
726 	crmfb_write_reg(sc, CRMFB_CMAP + (reg * 4), val);
727 
728 	return;
729 }
730 
731 static int
732 crmfb_set_curpos(struct crmfb_softc *sc, int x, int y)
733 {
734 	uint32_t val;
735 
736 	sc->sc_cur_x = x;
737 	sc->sc_cur_y = y;
738 
739 	val = ((x - sc->sc_hot_x) & 0xffff) | ((y - sc->sc_hot_y) << 16);
740 	crmfb_write_reg(sc, CRMFB_CURSOR_POS, val);
741 
742 	return 0;
743 }
744 
745 static int
746 crmfb_gcursor(struct crmfb_softc *sc, struct wsdisplay_cursor *cur)
747 {
748 	/* do nothing for now */
749 	return 0;
750 }
751 
752 static int
753 crmfb_scursor(struct crmfb_softc *sc, struct wsdisplay_cursor *cur)
754 {
755 	if (cur->which & WSDISPLAY_CURSOR_DOCUR) {
756 
757 		crmfb_write_reg(sc, CRMFB_CURSOR_CONTROL, cur->enable ? 1 : 0);
758 	}
759 	if (cur->which & WSDISPLAY_CURSOR_DOHOT) {
760 
761 		sc->sc_hot_x = cur->hot.x;
762 		sc->sc_hot_y = cur->hot.y;
763 	}
764 	if (cur->which & WSDISPLAY_CURSOR_DOPOS) {
765 
766 		crmfb_set_curpos(sc, cur->pos.x, cur->pos.y);
767 	}
768 	if (cur->which & WSDISPLAY_CURSOR_DOCMAP) {
769 		int i;
770 		uint32_t val;
771 
772 		for (i = 0; i < cur->cmap.count; i++) {
773 			val = (cur->cmap.red[i] << 24) |
774 			      (cur->cmap.green[i] << 16) |
775 			      (cur->cmap.blue[i] << 8);
776 			crmfb_write_reg(sc, CRMFB_CURSOR_CMAP0 +
777 			    ((i + cur->cmap.index) << 2), val);
778 		}
779 	}
780 	if (cur->which & WSDISPLAY_CURSOR_DOSHAPE) {
781 
782 		int i, j, cnt = 0;
783 		uint32_t latch = 0, omask;
784 		uint8_t imask;
785 		for (i = 0; i < 64; i++) {
786 			omask = 0x80000000;
787 			imask = 0x01;
788 			cur->image[cnt] &= cur->mask[cnt];
789 			for (j = 0; j < 8; j++) {
790 				if (cur->image[cnt] & imask)
791 					latch |= omask;
792 				omask >>= 1;
793 				if (cur->mask[cnt] & imask)
794 					latch |= omask;
795 				omask >>= 1;
796 				imask <<= 1;
797 			}
798 			cnt++;
799 			imask = 0x01;
800 			cur->image[cnt] &= cur->mask[cnt];
801 			for (j = 0; j < 8; j++) {
802 				if (cur->image[cnt] & imask)
803 					latch |= omask;
804 				omask >>= 1;
805 				if (cur->mask[cnt] & imask)
806 					latch |= omask;
807 				omask >>= 1;
808 				imask <<= 1;
809 			}
810 			cnt++;
811 			crmfb_write_reg(sc, CRMFB_CURSOR_BITMAP + (i << 2),
812 			    latch);
813 			latch = 0;
814 		}
815 	}
816 	return 0;
817 }
818 
819 static inline void
820 crmfb_write_reg(struct crmfb_softc *sc, int offset, uint32_t val)
821 {
822 
823 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, val);
824 	wbflush();
825 }
826 
827 static inline uint32_t
828 crmfb_read_reg(struct crmfb_softc *sc, int offset)
829 {
830 
831 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset);
832 }
833 
834 static int
835 crmfb_wait_dma_idle(struct crmfb_softc *sc)
836 {
837 	int bail = 100000, idle;
838 
839 	do {
840 		idle = ((bus_space_read_4(sc->sc_iot, sc->sc_ioh,
841 		         CRMFB_OVR_CONTROL) & 1) == 0) &&
842 		       ((bus_space_read_4(sc->sc_iot, sc->sc_ioh,
843 		         CRMFB_FRM_CONTROL) & 1) == 0) &&
844 		       ((bus_space_read_4(sc->sc_iot, sc->sc_ioh,
845 		         CRMFB_DID_CONTROL) & 1) == 0);
846 		if (!idle)
847 			delay(10);
848 		bail--;
849 	} while ((!idle) && (bail > 0));
850 	return idle;
851 }
852 
853 static int
854 crmfb_setup_video(struct crmfb_softc *sc, int depth)
855 {
856 	uint64_t reg;
857 	uint32_t d, h, page;
858 	int i, bail, tile_width, tlbptr, lptr, j, tx, shift, overhang;
859 	const char *wantsync;
860 	uint16_t v;
861 
862 	/* disable DMA */
863 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_OVR_CONTROL);
864 	d &= ~(1 << CRMFB_OVR_CONTROL_DMAEN_SHIFT);
865 	crmfb_write_reg(sc, CRMFB_OVR_CONTROL, d);
866 	DELAY(50000);
867 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_CONTROL);
868 	d &= ~(1 << CRMFB_FRM_CONTROL_DMAEN_SHIFT);
869 	crmfb_write_reg(sc, CRMFB_FRM_CONTROL, d);
870 	DELAY(50000);
871 	crmfb_write_reg(sc, CRMFB_DID_CONTROL, d);
872 	DELAY(50000);
873 
874 	if (!crmfb_wait_dma_idle(sc))
875 		aprint_error("crmfb: crmfb_wait_dma_idle timed out\n");
876 
877 	/* ensure that CRM starts drawing at the top left of the screen
878 	 * when we re-enable DMA later
879 	 */
880 	d = (1 << CRMFB_VT_XY_FREEZE_SHIFT);
881 	crmfb_write_reg(sc, CRMFB_VT_XY, d);
882 	delay(1000);
883 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_DOTCLOCK);
884 	d &= ~(1 << CRMFB_DOTCLOCK_CLKRUN_SHIFT);
885 	crmfb_write_reg(sc, CRMFB_DOTCLOCK, d);
886 
887 	/* wait for dotclock to turn off */
888 	bail = 10000;
889 	while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_DOTCLOCK) &
890 	    (1 << CRMFB_DOTCLOCK_CLKRUN_SHIFT)) && (bail > 0)) {
891 		delay(10);
892 		bail--;
893 	}
894 
895 	/* reset FIFO */
896 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_TILESIZE);
897 	d |= (1 << CRMFB_FRM_TILESIZE_FIFOR_SHIFT);
898 	crmfb_write_reg(sc, CRMFB_FRM_TILESIZE, d);
899 	d &= ~(1 << CRMFB_FRM_TILESIZE_FIFOR_SHIFT);
900 	crmfb_write_reg(sc, CRMFB_FRM_TILESIZE, d);
901 
902 	/* setup colour mode */
903 	switch (depth) {
904 	case 8:
905 		h = CRMFB_MODE_TYP_RG3B2;
906 		tile_width = 512;
907 		break;
908 	case 16:
909 		h = CRMFB_MODE_TYP_ARGB5;
910 		tile_width = 256;
911 		break;
912 	case 32:
913 		h = CRMFB_MODE_TYP_RGB8;
914 		tile_width = 128;
915 		break;
916 	default:
917 		panic("Unsupported depth");
918 	}
919 	d = h << CRMFB_MODE_TYP_SHIFT;
920 	d |= CRMFB_MODE_BUF_BOTH << CRMFB_MODE_BUF_SHIFT;
921 	for (i = 0; i < (32 * 4); i += 4)
922 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, CRMFB_MODE + i, d);
923 	wbflush();
924 
925 	/* setup tile pointer, but don't turn on DMA yet! */
926 	h = DMAADDR(sc->sc_dmai);
927 	d = (h >> 9) << CRMFB_FRM_CONTROL_TILEPTR_SHIFT;
928 	crmfb_write_reg(sc, CRMFB_FRM_CONTROL, d);
929 
930 	/* init framebuffer width and pixel size */
931 	/*d = (1 << CRMFB_FRM_TILESIZE_WIDTH_SHIFT);*/
932 
933 	d = ((int)(sc->sc_width / tile_width)) <<
934 	    CRMFB_FRM_TILESIZE_WIDTH_SHIFT;
935 	overhang = sc->sc_width % tile_width;
936 	if (overhang != 0) {
937 		uint32_t val;
938 		DPRINTF("tile width: %d\n", tile_width);
939 		DPRINTF("overhang: %d\n", overhang);
940 		val = (overhang * (depth >> 3)) >> 5;
941 		DPRINTF("reg: %08x\n", val);
942 		d |= (val & 0x1f);
943 		DPRINTF("d: %08x\n", d);
944 	}
945 
946 	switch (depth) {
947 	case 8:
948 		h = CRMFB_FRM_TILESIZE_DEPTH_8;
949 		break;
950 	case 16:
951 		h = CRMFB_FRM_TILESIZE_DEPTH_16;
952 		break;
953 	case 32:
954 		h = CRMFB_FRM_TILESIZE_DEPTH_32;
955 		break;
956 	default:
957 		panic("Unsupported depth");
958 	}
959 	d |= (h << CRMFB_FRM_TILESIZE_DEPTH_SHIFT);
960 	crmfb_write_reg(sc, CRMFB_FRM_TILESIZE, d);
961 
962 	/*h = sc->sc_width * sc->sc_height / (512 / (depth >> 3));*/
963 	h = sc->sc_height;
964 	d = h << CRMFB_FRM_PIXSIZE_HEIGHT_SHIFT;
965 	crmfb_write_reg(sc, CRMFB_FRM_PIXSIZE, d);
966 
967 	/* turn off firmware overlay and hardware cursor */
968 	crmfb_write_reg(sc, CRMFB_OVR_WIDTH_TILE, 0);
969 	crmfb_write_reg(sc, CRMFB_CURSOR_CONTROL, 0);
970 
971 	/* turn on DMA for the framebuffer */
972 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_CONTROL);
973 	d |= (1 << CRMFB_FRM_CONTROL_DMAEN_SHIFT);
974 	crmfb_write_reg(sc, CRMFB_FRM_CONTROL, d);
975 
976 	/* enable drawing again */
977 	crmfb_write_reg(sc, CRMFB_VT_XY, 0);
978 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_DOTCLOCK);
979 	d |= (1 << CRMFB_DOTCLOCK_CLKRUN_SHIFT);
980 	crmfb_write_reg(sc, CRMFB_DOTCLOCK, d);
981 
982 	/* turn off sync-on-green */
983 
984 	wantsync = arcbios_GetEnvironmentVariable("SyncOnGreen");
985 	if ( (wantsync != NULL) && (wantsync[0] == 'n') ) {
986 		sc->sc_vtflags |= CRMFB_VT_FLAGS_SYNC_LOW;
987 		crmfb_write_reg(sc, CRMFB_VT_FLAGS, d);
988 	}
989 
990 	sc->sc_depth = depth;
991 
992 	/* finally set up the drawing engine's TLB A */
993 	v = (DMAADDR(sc->sc_dma) >> 16) & 0xffff;
994 	tlbptr = 0;
995 	tx = ((sc->sc_width + (tile_width - 1)) & ~(tile_width - 1)) /
996 	    tile_width;
997 
998 	DPRINTF("tx: %d\n", tx);
999 
1000 	for (i = 0; i < 16; i++) {
1001 		reg = 0;
1002 		shift = 64;
1003 		lptr = 0;
1004 		for (j = 0; j < tx; j++) {
1005 			shift -= 16;
1006 			reg |= (((uint64_t)(v | 0x8000)) << shift);
1007 			if (shift == 0) {
1008 				shift = 64;
1009 				bus_space_write_8(sc->sc_iot, sc->sc_reh,
1010 				    CRIME_RE_TLB_A + tlbptr + lptr,
1011 				    reg);
1012 				DPRINTF("%04x: %016"PRIx64"\n", tlbptr + lptr, reg);
1013 				reg = 0;
1014 				lptr += 8;
1015 			}
1016 			v++;
1017 		}
1018 		if (shift != 64) {
1019 			bus_space_write_8(sc->sc_iot, sc->sc_reh,
1020 			    CRIME_RE_TLB_A + tlbptr + lptr, reg);
1021 			DPRINTF("%04x: %016"PRIx64"\n", tlbptr + lptr, reg);
1022 		}
1023 		tlbptr += 32;
1024 	}
1025 	sc->sc_scratch = (char *)KERNADDR(sc->sc_dma) + (0xf0000 * tx);
1026 
1027 	/* now put the last 64kB into the 1st linear TLB */
1028 	page = (sc->sc_linear >> 12) | 0x80000000;
1029 	tlbptr = 0;
1030 	for (i = 0; i < 8; i++) {
1031 		reg = ((uint64_t)page << 32) | (page + 1);
1032 		bus_space_write_8(sc->sc_iot, sc->sc_reh,
1033 		    CRIME_RE_LINEAR_A + tlbptr, reg);
1034 		page += 2;
1035 		tlbptr += 8;
1036 	}
1037 	wbflush();
1038 
1039 	/* do some very basic engine setup */
1040 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_CLIPMODE, 0);
1041 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_WINOFFSET_SRC, 0);
1042 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_WINOFFSET_DST, 0);
1043 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_PLANEMASK,
1044 	    0xffffffff);
1045 
1046 	bus_space_write_8(sc->sc_iot, sc->sc_reh, 0x20, 0);
1047 	bus_space_write_8(sc->sc_iot, sc->sc_reh, 0x28, 0);
1048 	bus_space_write_8(sc->sc_iot, sc->sc_reh, 0x30, 0);
1049 	bus_space_write_8(sc->sc_iot, sc->sc_reh, 0x38, 0);
1050 	bus_space_write_8(sc->sc_iot, sc->sc_reh, 0x40, 0);
1051 
1052 	switch (depth) {
1053 		case 8:
1054 			sc->sc_de_mode = DE_MODE_TLB_A | DE_MODE_BUFDEPTH_8 |
1055 			    DE_MODE_TYPE_CI | DE_MODE_PIXDEPTH_8;
1056 			sc->sc_mte_mode = MTE_MODE_DST_ECC |
1057 			    (MTE_TLB_A << MTE_DST_TLB_SHIFT) |
1058 			    (MTE_TLB_A << MTE_SRC_TLB_SHIFT) |
1059 			    (MTE_DEPTH_8 << MTE_DEPTH_SHIFT);
1060 			sc->sc_mte_x_shift = 0;
1061 			break;
1062 		case 16:
1063 			sc->sc_de_mode = DE_MODE_TLB_A | DE_MODE_BUFDEPTH_16 |
1064 			    DE_MODE_TYPE_RGBA | DE_MODE_PIXDEPTH_16;
1065 			sc->sc_mte_mode = MTE_MODE_DST_ECC |
1066 			    (MTE_TLB_A << MTE_DST_TLB_SHIFT) |
1067 			    (MTE_TLB_A << MTE_SRC_TLB_SHIFT) |
1068 			    (MTE_DEPTH_16 << MTE_DEPTH_SHIFT);
1069 			sc->sc_mte_x_shift = 1;
1070 			break;
1071 		case 32:
1072 			sc->sc_de_mode = DE_MODE_TLB_A | DE_MODE_BUFDEPTH_32 |
1073 			    DE_MODE_TYPE_RGBA | DE_MODE_PIXDEPTH_32;
1074 			break;
1075 			sc->sc_mte_mode = MTE_MODE_DST_ECC |
1076 			    (MTE_TLB_A << MTE_DST_TLB_SHIFT) |
1077 			    (MTE_TLB_A << MTE_SRC_TLB_SHIFT) |
1078 			    (MTE_DEPTH_32 << MTE_DEPTH_SHIFT);
1079 			sc->sc_mte_x_shift = 2;
1080 		default:
1081 			panic("%s: unsuported colour depth %d\n", __func__,
1082 			    depth);
1083 	}
1084 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_MODE_DST,
1085 	    sc->sc_de_mode);
1086 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_MODE_SRC,
1087 	    sc->sc_de_mode);
1088 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_XFER_STEP_X, 1);
1089 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_XFER_STEP_Y, 1);
1090 
1091 	/* initialize memory transfer engine */
1092 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_MODE,
1093 	    sc->sc_mte_mode | MTE_MODE_COPY);
1094 	sc->sc_mte_direction = 1;
1095 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_DST_Y_STEP, 1);
1096 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_SRC_Y_STEP, 1);
1097 
1098 	return 0;
1099 }
1100 
1101 static void
1102 crmfb_set_mte_direction(struct crmfb_softc *sc, int dir)
1103 {
1104 	if (dir == sc->sc_mte_direction)
1105 		return;
1106 
1107 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_DST_Y_STEP, dir);
1108 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_SRC_Y_STEP, dir);
1109 	sc->sc_mte_direction = dir;
1110 }
1111 
1112 static void
1113 crmfb_setup_palette(struct crmfb_softc *sc)
1114 {
1115 	int i;
1116 
1117 	for (i = 0; i < 256; i++) {
1118 		crmfb_set_palette(sc, i, rasops_cmap[(i * 3) + 2],
1119 		    rasops_cmap[(i * 3) + 1], rasops_cmap[(i * 3) + 0]);
1120 		sc->sc_cmap_red[i] = rasops_cmap[(i * 3) + 2];
1121 		sc->sc_cmap_green[i] = rasops_cmap[(i * 3) + 1];
1122 		sc->sc_cmap_blue[i] = rasops_cmap[(i * 3) + 0];
1123 	}
1124 }
1125 
1126 static inline void
1127 crmfb_wait_idle(struct crmfb_softc *sc)
1128 {
1129 	int i = 0;
1130 
1131 	do {
1132 		i++;
1133 	} while (((bus_space_read_4(sc->sc_iot, sc->sc_reh, CRIME_DE_STATUS) &
1134 		   CRIME_DE_IDLE) == 0) && (i < 100000000));
1135 	if (i >= 100000000)
1136 		aprint_error("crmfb_wait_idle() timed out\n");
1137 }
1138 
1139 static void
1140 crmfb_fill_rect(struct crmfb_softc *sc, int x, int y, int width, int height,
1141     uint32_t colour)
1142 {
1143 	int rxa, rxe;
1144 
1145 	rxa = x << sc->sc_mte_x_shift;
1146 	rxe = ((x + width) << sc->sc_mte_x_shift) - 1;
1147 	crmfb_wait_idle(sc);
1148 	crmfb_set_mte_direction(sc, 1);
1149 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_MODE,
1150 	    sc->sc_mte_mode | 0);
1151 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_BG, colour);
1152 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_DST0,
1153 	    (rxa << 16) | (y & 0xffff));
1154 	bus_space_write_4(sc->sc_iot, sc->sc_reh,
1155 	    CRIME_MTE_DST1 | CRIME_DE_START,
1156 	    (rxe << 16) | ((y + height - 1) & 0xffff));
1157 }
1158 
1159 static void
1160 crmfb_bitblt(struct crmfb_softc *sc, int xs, int ys, int xd, int yd,
1161     int wi, int he, uint32_t rop)
1162 {
1163 	uint32_t prim = DE_PRIM_RECTANGLE;
1164 	int rxa, rya, rxe, rye, rxs, rys;
1165 	crmfb_wait_idle(sc);
1166 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_MODE_SRC,
1167 	    sc->sc_de_mode);
1168 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_DRAWMODE,
1169 	    DE_DRAWMODE_PLANEMASK | DE_DRAWMODE_BYTEMASK | DE_DRAWMODE_ROP |
1170 	    DE_DRAWMODE_XFER_EN);
1171 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_ROP, rop);
1172 	if (xs < xd) {
1173 		prim |= DE_PRIM_RL;
1174 		rxe = xd;
1175 		rxa = xd + wi - 1;
1176 		rxs = xs + wi - 1;
1177 	} else {
1178 		prim |= DE_PRIM_LR;
1179 		rxe = xd + wi - 1;
1180 		rxa = xd;
1181 		rxs = xs;
1182 	}
1183 	if (ys < yd) {
1184 		prim |= DE_PRIM_BT;
1185 		rye = yd;
1186 		rya = yd + he - 1;
1187 		rys = ys + he - 1;
1188 	} else {
1189 		prim |= DE_PRIM_TB;
1190 		rye = yd + he - 1;
1191 		rya = yd;
1192 		rys = ys;
1193 	}
1194 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_PRIMITIVE, prim);
1195 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_XFER_ADDR_SRC,
1196 	    (rxs << 16) | (rys & 0xffff));
1197 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_X_VERTEX_0,
1198 	    (rxa << 16) | (rya & 0xffff));
1199 	bus_space_write_4(sc->sc_iot, sc->sc_reh,
1200 	    CRIME_DE_X_VERTEX_1 | CRIME_DE_START,
1201 	    (rxe << 16) | (rye & 0xffff));
1202 }
1203 
1204 static void
1205 crmfb_scroll(struct crmfb_softc *sc, int xs, int ys, int xd, int yd,
1206     int wi, int he)
1207 {
1208 	int rxa, rya, rxe, rye, rxd, ryd, rxde, ryde;
1209 
1210 	rxa = xs << sc->sc_mte_x_shift;
1211 	rxd = xd << sc->sc_mte_x_shift;
1212 	rxe = ((xs + wi) << sc->sc_mte_x_shift) - 1;
1213 	rxde = ((xd + wi) << sc->sc_mte_x_shift) - 1;
1214 
1215 	crmfb_wait_idle(sc);
1216 
1217 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_MODE,
1218 	    sc->sc_mte_mode | MTE_MODE_COPY);
1219 
1220 	if (ys < yd) {
1221 		/* bottom to top */
1222 		rye = ys;
1223 		rya = ys + he - 1;
1224 		ryd = yd + he - 1;
1225 		ryde = yd;
1226 		crmfb_set_mte_direction(sc, -1);
1227 	} else {
1228 		/* top to bottom */
1229 		rye = ys + he - 1;
1230 		rya = ys;
1231 		ryd = yd;
1232 		ryde = yd + he - 1;
1233 		crmfb_set_mte_direction(sc, 1);
1234 	}
1235 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_SRC0,
1236 	    (rxa << 16) | rya);
1237 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_SRC1,
1238 	    (rxe << 16) | rye);
1239 	bus_space_write_4(sc->sc_iot, sc->sc_reh,
1240 	    CRIME_MTE_DST0,
1241 	    (rxd << 16) | ryd);
1242 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_DST1 |
1243 	    CRIME_DE_START,
1244 	    (rxde << 16) | ryde);
1245 }
1246 
1247 static void
1248 crmfb_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
1249 {
1250 	struct rasops_info *ri = cookie;
1251 	struct vcons_screen *scr = ri->ri_hw;
1252 	int32_t xs, xd, y, width, height;
1253 
1254 	xs = ri->ri_xorigin + ri->ri_font->fontwidth * srccol;
1255 	xd = ri->ri_xorigin + ri->ri_font->fontwidth * dstcol;
1256 	y = ri->ri_yorigin + ri->ri_font->fontheight * row;
1257 	width = ri->ri_font->fontwidth * ncols;
1258 	height = ri->ri_font->fontheight;
1259 	crmfb_bitblt(scr->scr_cookie, xs, y, xd, y, width, height, 3);
1260 }
1261 
1262 static void
1263 crmfb_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
1264 {
1265 	struct rasops_info *ri = cookie;
1266 	struct vcons_screen *scr = ri->ri_hw;
1267 	int32_t x, y, width, height, bg;
1268 
1269 	x = ri->ri_xorigin + ri->ri_font->fontwidth * startcol;
1270 	y = ri->ri_yorigin + ri->ri_font->fontheight * row;
1271 	width = ri->ri_font->fontwidth * ncols;
1272 	height = ri->ri_font->fontheight;
1273 	bg = (uint32_t)ri->ri_devcmap[(fillattr >> 16) & 0xff];
1274 	crmfb_fill_rect(scr->scr_cookie, x, y, width, height, bg);
1275 }
1276 
1277 static void
1278 crmfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
1279 {
1280 	struct rasops_info *ri = cookie;
1281 	struct vcons_screen *scr = ri->ri_hw;
1282 	int32_t x, ys, yd, width, height;
1283 
1284 	x = ri->ri_xorigin;
1285 	ys = ri->ri_yorigin + ri->ri_font->fontheight * srcrow;
1286 	yd = ri->ri_yorigin + ri->ri_font->fontheight * dstrow;
1287 	width = ri->ri_emuwidth;
1288 	height = ri->ri_font->fontheight * nrows;
1289 
1290 	crmfb_scroll(scr->scr_cookie, x, ys, x, yd, width, height);
1291 }
1292 
1293 static void
1294 crmfb_eraserows(void *cookie, int row, int nrows, long fillattr)
1295 {
1296 	struct rasops_info *ri = cookie;
1297 	struct vcons_screen *scr = ri->ri_hw;
1298 	int32_t x, y, width, height, bg;
1299 
1300 	if ((row == 0) && (nrows == ri->ri_rows)) {
1301 		x = y = 0;
1302 		width = ri->ri_width;
1303 		height = ri->ri_height;
1304 	} else {
1305 		x = ri->ri_xorigin;
1306 		y = ri->ri_yorigin + ri->ri_font->fontheight * row;
1307 		width = ri->ri_emuwidth;
1308 		height = ri->ri_font->fontheight * nrows;
1309 	}
1310 	bg = (uint32_t)ri->ri_devcmap[(fillattr >> 16) & 0xff];
1311 	crmfb_fill_rect(scr->scr_cookie, x, y, width, height, bg);
1312 }
1313 
1314 static void
1315 crmfb_cursor(void *cookie, int on, int row, int col)
1316 {
1317 	struct rasops_info *ri = cookie;
1318 	struct vcons_screen *scr = ri->ri_hw;
1319 	struct crmfb_softc *sc = scr->scr_cookie;
1320 	int x, y, wi,he;
1321 
1322 	wi = ri->ri_font->fontwidth;
1323 	he = ri->ri_font->fontheight;
1324 
1325 	if (ri->ri_flg & RI_CURSOR) {
1326 		x = ri->ri_ccol * wi + ri->ri_xorigin;
1327 		y = ri->ri_crow * he + ri->ri_yorigin;
1328 		crmfb_bitblt(sc, x, y, x, y, wi, he, 12);
1329 		ri->ri_flg &= ~RI_CURSOR;
1330 	}
1331 
1332 	ri->ri_crow = row;
1333 	ri->ri_ccol = col;
1334 
1335 	if (on)
1336 	{
1337 		x = ri->ri_ccol * wi + ri->ri_xorigin;
1338 		y = ri->ri_crow * he + ri->ri_yorigin;
1339 		crmfb_bitblt(sc, x, y, x, y, wi, he, 12);
1340 		ri->ri_flg |= RI_CURSOR;
1341 	}
1342 }
1343 
1344 static void
1345 crmfb_putchar(void *cookie, int row, int col, u_int c, long attr)
1346 {
1347 	struct rasops_info *ri = cookie;
1348 	struct vcons_screen *scr = ri->ri_hw;
1349 	struct crmfb_softc *sc = scr->scr_cookie;
1350 	struct wsdisplay_font *font = PICK_FONT(ri, c);
1351 	uint32_t bg, fg;
1352 	int x, y, wi, he, i, uc;
1353 	uint8_t *fd8;
1354 	uint16_t *fd16;
1355 	void *fd;
1356 
1357 	wi = font->fontwidth;
1358 	he = font->fontheight;
1359 
1360 	x = ri->ri_xorigin + col * wi;
1361 	y = ri->ri_yorigin + row * he;
1362 
1363 	bg = ri->ri_devcmap[(attr >> 16) & 0xff];
1364 	fg = ri->ri_devcmap[(attr >> 24) & 0xff];
1365 	uc = c - font->firstchar;
1366 	fd = (uint8_t *)font->data + uc * ri->ri_fontscale;
1367 	if (c == 0x20) {
1368 		crmfb_fill_rect(sc, x, y, wi, he, bg);
1369 	} else {
1370 		crmfb_wait_idle(sc);
1371 		/* setup */
1372 		bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_DRAWMODE,
1373 		    DE_DRAWMODE_PLANEMASK | DE_DRAWMODE_BYTEMASK |
1374 		    DE_DRAWMODE_ROP |
1375 		    DE_DRAWMODE_OPAQUE_STIP | DE_DRAWMODE_POLY_STIP);
1376 		wbflush();
1377 		bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_ROP, 3);
1378 		bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_FG, fg);
1379 		bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_BG, bg);
1380 		bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_PRIMITIVE,
1381 		    DE_PRIM_RECTANGLE | DE_PRIM_LR | DE_PRIM_TB);
1382 		bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_STIPPLE_MODE,
1383 		    0x001f0000);
1384 		/* now let's feed the engine */
1385 		if (font->stride == 1) {
1386 			/* shovel in 8 bit quantities */
1387 			fd8 = fd;
1388 			for (i = 0; i < he; i++) {
1389 				/*
1390 				 * the pipeline should be long enough to
1391 				 * draw any character without having to wait
1392 				 */
1393 				bus_space_write_4(sc->sc_iot, sc->sc_reh,
1394 				    CRIME_DE_STIPPLE_PAT, *fd8 << 24);
1395 				bus_space_write_4(sc->sc_iot, sc->sc_reh,
1396 				    CRIME_DE_X_VERTEX_0, (x << 16) | y);
1397 				bus_space_write_4(sc->sc_iot, sc->sc_reh,
1398 				    CRIME_DE_X_VERTEX_1 | CRIME_DE_START,
1399 				    ((x + wi) << 16) | y);
1400 				y++;
1401 				fd8++;
1402 			}
1403 		} else if (font->stride == 2) {
1404 			/* shovel in 16 bit quantities */
1405 			fd16 = fd;
1406 			for (i = 0; i < he; i++) {
1407 				/*
1408 				 * the pipeline should be long enough to
1409 				 * draw any character without having to wait
1410 				 */
1411 				bus_space_write_4(sc->sc_iot, sc->sc_reh,
1412 				    CRIME_DE_STIPPLE_PAT, *fd16 << 16);
1413 				bus_space_write_4(sc->sc_iot, sc->sc_reh,
1414 				    CRIME_DE_X_VERTEX_0, (x << 16) | y);
1415 				bus_space_write_4(sc->sc_iot, sc->sc_reh,
1416 				    CRIME_DE_X_VERTEX_1 | CRIME_DE_START,
1417 				    ((x + wi) << 16) | y);
1418 				y++;
1419 				fd16++;
1420 			}
1421 		}
1422 	}
1423 }
1424 
1425 static void
1426 crmfb_setup_ddc(struct crmfb_softc *sc)
1427 {
1428 	int i;
1429 
1430 	memset(sc->sc_edid_data, 0, 128);
1431 	sc->sc_i2c.ic_cookie = sc;
1432 	sc->sc_i2c.ic_acquire_bus = crmfb_i2c_acquire_bus;
1433 	sc->sc_i2c.ic_release_bus = crmfb_i2c_release_bus;
1434 	sc->sc_i2c.ic_send_start = crmfb_i2c_send_start;
1435 	sc->sc_i2c.ic_send_stop = crmfb_i2c_send_stop;
1436 	sc->sc_i2c.ic_initiate_xfer = crmfb_i2c_initiate_xfer;
1437 	sc->sc_i2c.ic_read_byte = crmfb_i2c_read_byte;
1438 	sc->sc_i2c.ic_write_byte = crmfb_i2c_write_byte;
1439 	sc->sc_i2c.ic_exec = NULL;
1440 	i = 0;
1441 	while (sc->sc_edid_data[1] == 0 && i++ < 10)
1442 		ddc_read_edid(&sc->sc_i2c, sc->sc_edid_data, 128);
1443 	if (i > 1)
1444 		aprint_debug_dev(sc->sc_dev,
1445 		    "had to try %d times to get EDID data\n", i);
1446 	if (i < 11) {
1447 		edid_parse(sc->sc_edid_data, &sc->sc_edid_info);
1448 		edid_print(&sc->sc_edid_info);
1449 	}
1450 }
1451 
1452 /* I2C bitbanging */
1453 static void
1454 crmfb_i2cbb_set_bits(void *cookie, uint32_t bits)
1455 {
1456 	struct crmfb_softc *sc = cookie;
1457 
1458 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, CRMFB_I2C_VGA, bits ^ 3);
1459 }
1460 
1461 static void
1462 crmfb_i2cbb_set_dir(void *cookie, uint32_t dir)
1463 {
1464 
1465 	/* Nothing to do */
1466 }
1467 
1468 static uint32_t
1469 crmfb_i2cbb_read(void *cookie)
1470 {
1471 	struct crmfb_softc *sc = cookie;
1472 
1473 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_I2C_VGA) ^ 3;
1474 }
1475 
1476 /* higher level I2C stuff */
1477 static int
1478 crmfb_i2c_acquire_bus(void *cookie, int flags)
1479 {
1480 
1481 	/* private bus */
1482 	return 0;
1483 }
1484 
1485 static void
1486 crmfb_i2c_release_bus(void *cookie, int flags)
1487 {
1488 
1489 	/* private bus */
1490 }
1491 
1492 static int
1493 crmfb_i2c_send_start(void *cookie, int flags)
1494 {
1495 
1496 	return i2c_bitbang_send_start(cookie, flags, &crmfb_i2cbb_ops);
1497 }
1498 
1499 static int
1500 crmfb_i2c_send_stop(void *cookie, int flags)
1501 {
1502 
1503 	return i2c_bitbang_send_stop(cookie, flags, &crmfb_i2cbb_ops);
1504 }
1505 
1506 static int
1507 crmfb_i2c_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
1508 {
1509 
1510 	return i2c_bitbang_initiate_xfer(cookie, addr, flags,
1511 	    &crmfb_i2cbb_ops);
1512 }
1513 
1514 static int
1515 crmfb_i2c_read_byte(void *cookie, uint8_t *valp, int flags)
1516 {
1517 
1518 	return i2c_bitbang_read_byte(cookie, valp, flags, &crmfb_i2cbb_ops);
1519 }
1520 
1521 static int
1522 crmfb_i2c_write_byte(void *cookie, uint8_t val, int flags)
1523 {
1524 
1525 	return i2c_bitbang_write_byte(cookie, val, flags, &crmfb_i2cbb_ops);
1526 }
1527 
1528 /* mode setting stuff */
1529 static uint32_t
1530 calc_pll(int f_out)
1531 {
1532 	uint32_t ret;
1533 	int f_in = 20000;	/* 20MHz in */
1534 	int M, N, P;
1535 	int error, div, best = 9999999;
1536 	int ff1, ff2;
1537 	int MM = 0, NN = 0, PP = 0, ff = 0;
1538 
1539 	/* f_out = M * f_in / (N * (1 << P) */
1540 
1541 	for (P = 0; P < 4; P++) {
1542 		for (N = 64; N > 0; N--) {
1543 			div = N * (1 << P);
1544 			M = f_out * div / f_in;
1545 			if ((M < 257) && (M > 100)) {
1546 				ff1 = M * f_in / div;
1547 				ff2 = (M + 1) * f_in / div;
1548 				error = abs(ff1 - f_out);
1549 				if (error < best) {
1550 					MM = M;
1551 					NN = N;
1552 					PP = P;
1553 					ff = ff1;
1554 					best = error;
1555 				}
1556 				error = abs(ff2 - f_out);
1557 				if ((error < best) && ( M < 256)){
1558 					MM = M + 1;
1559 					NN = N;
1560 					PP = P;
1561 					ff = ff2;
1562 					best = error;
1563 				}
1564 			}
1565 		}
1566 	}
1567 	DPRINTF("%d: M %d N %d P %d -> %d\n", f_out, MM, NN, PP, ff);
1568 	/* now shove the parameters into the register's format */
1569 	ret = (MM - 1) | ((NN - 1) << 8) | (P << 14);
1570 	return ret;
1571 }
1572 
1573 static int
1574 crmfb_set_mode(struct crmfb_softc *sc, const struct videomode *mode)
1575 {
1576 	uint32_t d, dc;
1577 	int tmp, diff;
1578 
1579 	switch (mode->hdisplay % 32) {
1580 		case 0:
1581 			sc->sc_console_depth = 8;
1582 			break;
1583 		case 16:
1584 			sc->sc_console_depth = 16;
1585 			break;
1586 		case 8:
1587 		case 24:
1588 			sc->sc_console_depth = 32;
1589 			break;
1590 		default:
1591 			aprint_error_dev(sc->sc_dev,
1592 			    "hdisplay (%d) is not a multiple of 32\n",
1593 			    mode->hdisplay);
1594 			return FALSE;
1595 	}
1596 	if (mode->dot_clock > 150000) {
1597 		aprint_error_dev(sc->sc_dev,
1598 		    "requested dot clock is too high ( %d MHz )\n",
1599 		    mode->dot_clock / 1000);
1600 		return FALSE;
1601 	}
1602 
1603 	/* disable DMA */
1604 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_OVR_CONTROL);
1605 	d &= ~(1 << CRMFB_OVR_CONTROL_DMAEN_SHIFT);
1606 	crmfb_write_reg(sc, CRMFB_OVR_CONTROL, d);
1607 	DELAY(50000);
1608 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_CONTROL);
1609 	d &= ~(1 << CRMFB_FRM_CONTROL_DMAEN_SHIFT);
1610 	crmfb_write_reg(sc, CRMFB_FRM_CONTROL, d);
1611 	DELAY(50000);
1612 	crmfb_write_reg(sc, CRMFB_DID_CONTROL, d);
1613 	DELAY(50000);
1614 
1615 	if (!crmfb_wait_dma_idle(sc))
1616 		aprint_error("crmfb: crmfb_wait_dma_idle timed out\n");
1617 
1618 	/* ok, now we're good to go */
1619 	dc = calc_pll(mode->dot_clock);
1620 
1621 	crmfb_write_reg(sc, CRMFB_VT_XY, 1 << CRMFB_VT_XY_FREEZE_SHIFT);
1622 	delay(1000);
1623 
1624 	/* set the dot clock pll but don't start it yet */
1625 	crmfb_write_reg(sc, CRMFB_DOTCLOCK, dc);
1626 	delay(10000);
1627 
1628 	/* pixel counter */
1629 	d = mode->htotal | (mode->vtotal << 12);
1630 	crmfb_write_reg(sc, CRMFB_VT_XYMAX, d);
1631 
1632 	/* video timings */
1633 	d = mode->vsync_end | (mode->vsync_start << 12);
1634 	crmfb_write_reg(sc, CRMFB_VT_VSYNC, d);
1635 
1636 	d = mode->hsync_end | (mode->hsync_start << 12);
1637 	crmfb_write_reg(sc, CRMFB_VT_HSYNC, d);
1638 
1639 	d = mode->vtotal | (mode->vdisplay << 12);
1640 	crmfb_write_reg(sc, CRMFB_VT_VBLANK, d);
1641 
1642 	d = (mode->htotal - 5) | ((mode->hdisplay - 5) << 12);
1643 	crmfb_write_reg(sc, CRMFB_VT_HBLANK, d);
1644 
1645 	d = mode->vtotal | (mode->vdisplay << 12);
1646 	crmfb_write_reg(sc, CRMFB_VT_VCMAP, d);
1647 	d = mode->htotal | (mode->hdisplay << 12);
1648 	crmfb_write_reg(sc, CRMFB_VT_HCMAP, d);
1649 
1650 	d = 0;
1651 	if (mode->flags & VID_NHSYNC) d |= CRMFB_VT_FLAGS_HDRV_INVERT;
1652 	if (mode->flags & VID_NVSYNC) d |= CRMFB_VT_FLAGS_VDRV_INVERT;
1653 	crmfb_write_reg(sc, CRMFB_VT_FLAGS, d);
1654 	sc->sc_vtflags = d;
1655 
1656 	diff = -abs(mode->vtotal - mode->vdisplay - 1);
1657 	d = ((uint32_t)diff << 12) & 0x00fff000;
1658 	d |= (mode->htotal - 20);
1659 	crmfb_write_reg(sc, CRMFB_VT_DID_STARTXY, d);
1660 
1661 	d = ((uint32_t)(diff + 1) << 12) & 0x00fff000;
1662 	d |= (mode->htotal - 54);
1663 	crmfb_write_reg(sc, CRMFB_VT_CRS_STARTXY, d);
1664 
1665 	d = ((uint32_t)diff << 12) & 0x00fff000;
1666 	d |= (mode->htotal - 4);
1667 	crmfb_write_reg(sc, CRMFB_VT_VC_STARTXY, d);
1668 
1669 	tmp = mode->htotal - 19;
1670 	d = tmp << 12;
1671 	d |= ((tmp + mode->hdisplay - 2) % mode->htotal);
1672 	crmfb_write_reg(sc, CRMFB_VT_HPIX_EN, d);
1673 
1674 	d = mode->vdisplay | (mode->vtotal << 12);
1675 	crmfb_write_reg(sc, CRMFB_VT_VPIX_EN, d);
1676 
1677 	sc->sc_width = mode->hdisplay;
1678 	sc->sc_height = mode->vdisplay;
1679 
1680 	return TRUE;
1681 }
1682 
1683