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