xref: /netbsd-src/sys/arch/sgimips/dev/crmfb.c (revision e5fbc36ada28f9b9a5836ecffaf4a06aa1ebb687)
1*e5fbc36aSthorpej /* $NetBSD: crmfb.c,v 1.50 2023/12/20 15:29:07 thorpej Exp $ */
2d4c79dc9Sjmcneill 
3d4c79dc9Sjmcneill /*-
4d4c79dc9Sjmcneill  * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
5b86243deSmacallan  *               2008 Michael Lorenz <macallan@netbsd.org>
6d4c79dc9Sjmcneill  * All rights reserved.
7d4c79dc9Sjmcneill  *
8d4c79dc9Sjmcneill  * Redistribution and use in source and binary forms, with or without
9d4c79dc9Sjmcneill  * modification, are permitted provided that the following conditions
10d4c79dc9Sjmcneill  * are met:
11d4c79dc9Sjmcneill  * 1. Redistributions of source code must retain the above copyright
12d4c79dc9Sjmcneill  *    notice, this list of conditions and the following disclaimer.
13d4c79dc9Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
14d4c79dc9Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
15d4c79dc9Sjmcneill  *    documentation and/or other materials provided with the distribution.
16d4c79dc9Sjmcneill  *
17d4c79dc9Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18d4c79dc9Sjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19d4c79dc9Sjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20d4c79dc9Sjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21d4c79dc9Sjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22d4c79dc9Sjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23d4c79dc9Sjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24d4c79dc9Sjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25d4c79dc9Sjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26d4c79dc9Sjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27d4c79dc9Sjmcneill  * POSSIBILITY OF SUCH DAMAGE.
28d4c79dc9Sjmcneill  */
29d4c79dc9Sjmcneill 
30d4c79dc9Sjmcneill /*
31d4c79dc9Sjmcneill  * SGI-CRM (O2) Framebuffer driver
32d4c79dc9Sjmcneill  */
33d4c79dc9Sjmcneill 
34d4c79dc9Sjmcneill #include <sys/cdefs.h>
35*e5fbc36aSthorpej __KERNEL_RCSID(0, "$NetBSD: crmfb.c,v 1.50 2023/12/20 15:29:07 thorpej Exp $");
36d4c79dc9Sjmcneill 
37d4c79dc9Sjmcneill #include <sys/param.h>
38d4c79dc9Sjmcneill #include <sys/systm.h>
39d4c79dc9Sjmcneill #include <sys/device.h>
40d4c79dc9Sjmcneill 
41d4c79dc9Sjmcneill #include <machine/autoconf.h>
42cf10107dSdyoung #include <sys/bus.h>
43d4c79dc9Sjmcneill #include <machine/machtype.h>
44d4c79dc9Sjmcneill #include <machine/vmparam.h>
45d4c79dc9Sjmcneill 
467abea6b5Sjmcneill #include <dev/arcbios/arcbios.h>
477abea6b5Sjmcneill #include <dev/arcbios/arcbiosvar.h>
487abea6b5Sjmcneill 
49d4c79dc9Sjmcneill #include <dev/wscons/wsdisplayvar.h>
50d4c79dc9Sjmcneill #include <dev/wscons/wsconsio.h>
51d4c79dc9Sjmcneill #include <dev/wsfont/wsfont.h>
52d4c79dc9Sjmcneill #include <dev/rasops/rasops.h>
53d4c79dc9Sjmcneill #include <dev/wscons/wsdisplay_vconsvar.h>
54d4c79dc9Sjmcneill 
559b94525aSmacallan #include <dev/i2c/i2cvar.h>
569b94525aSmacallan #include <dev/i2c/i2c_bitbang.h>
579b94525aSmacallan #include <dev/i2c/ddcvar.h>
589b94525aSmacallan #include <dev/videomode/videomode.h>
5995923c6fSjmcneill #include <dev/videomode/vesagtf.h>
609b94525aSmacallan #include <dev/videomode/edidvar.h>
619b94525aSmacallan 
620904724eSmacallan #include <arch/sgimips/dev/crmfbreg.h>
630904724eSmacallan 
64713b73c8Smacallan #include "opt_crmfb.h"
65713b73c8Smacallan 
66713b73c8Smacallan #ifdef CRMFB_DEBUG
67713b73c8Smacallan #define DPRINTF printf
68713b73c8Smacallan #else
69713b73c8Smacallan #define DPRINTF while (0) printf
70713b73c8Smacallan #endif
71b7875ec4Sjmcneill 
72d4c79dc9Sjmcneill struct wsscreen_descr crmfb_defaultscreen = {
73d4c79dc9Sjmcneill 	"default",
74d4c79dc9Sjmcneill 	0, 0,
75d4c79dc9Sjmcneill 	NULL,
76d4c79dc9Sjmcneill 	8, 16,
77733da76fSmacallan 	WSSCREEN_WSCOLORS | WSSCREEN_RESIZE,
78d4c79dc9Sjmcneill 	NULL,
79d4c79dc9Sjmcneill };
80d4c79dc9Sjmcneill 
81d4c79dc9Sjmcneill const struct wsscreen_descr *_crmfb_scrlist[] = {
82d4c79dc9Sjmcneill 	&crmfb_defaultscreen,
83d4c79dc9Sjmcneill };
84d4c79dc9Sjmcneill 
85d4c79dc9Sjmcneill struct wsscreen_list crmfb_screenlist = {
86d4c79dc9Sjmcneill 	sizeof(_crmfb_scrlist) / sizeof(struct wsscreen_descr *),
87d4c79dc9Sjmcneill 	_crmfb_scrlist
88d4c79dc9Sjmcneill };
89d4c79dc9Sjmcneill 
90d4c79dc9Sjmcneill static struct vcons_screen crmfb_console_screen;
91d4c79dc9Sjmcneill 
92d4c79dc9Sjmcneill static int	crmfb_ioctl(void *, void *, u_long, void *, int, struct lwp *);
93d4c79dc9Sjmcneill static paddr_t	crmfb_mmap(void *, void *, off_t, int);
94d4c79dc9Sjmcneill static void	crmfb_init_screen(void *, struct vcons_screen *, int, long *);
95d4c79dc9Sjmcneill 
96d4c79dc9Sjmcneill struct wsdisplay_accessops crmfb_accessops = {
97d4c79dc9Sjmcneill 	crmfb_ioctl,
98d4c79dc9Sjmcneill 	crmfb_mmap,
99d4c79dc9Sjmcneill 	NULL,	/* alloc_screen */
100d4c79dc9Sjmcneill 	NULL,	/* free_screen */
101d4c79dc9Sjmcneill 	NULL,	/* show_screen */
102d4c79dc9Sjmcneill 	NULL,	/* load_font */
103d4c79dc9Sjmcneill 	NULL,	/* pollc */
104d4c79dc9Sjmcneill 	NULL,	/* scroll */
105d4c79dc9Sjmcneill };
106d4c79dc9Sjmcneill 
107d4c79dc9Sjmcneill /* Memory to allocate to SGI-CRM -- remember, this is stolen from
108d4c79dc9Sjmcneill  * host memory!
109d4c79dc9Sjmcneill  */
110d4c79dc9Sjmcneill #define CRMFB_TILESIZE	(512*128)
111d4c79dc9Sjmcneill 
112e313f620Smacallan static int	crmfb_match(device_t, struct cfdata *, void *);
113e313f620Smacallan static void	crmfb_attach(device_t, device_t, void *);
114d4c79dc9Sjmcneill int		crmfb_probe(void);
115d4c79dc9Sjmcneill 
116d4c79dc9Sjmcneill #define KERNADDR(p)	((void *)((p).addr))
117d4c79dc9Sjmcneill #define DMAADDR(p)	((p).map->dm_segs[0].ds_addr)
118d4c79dc9Sjmcneill 
119ab0a73e2Ssekiya #define CRMFB_REG_MASK(msb, lsb) \
120ab0a73e2Ssekiya 	( (((uint32_t) 1 << ((msb)-(lsb)+1)) - 1) << (lsb) )
121ab0a73e2Ssekiya 
122ab0a73e2Ssekiya 
123d4c79dc9Sjmcneill struct crmfb_dma {
124d4c79dc9Sjmcneill 	bus_dmamap_t		map;
125d4c79dc9Sjmcneill 	void			*addr;
126d4c79dc9Sjmcneill 	bus_dma_segment_t	segs[1];
127d4c79dc9Sjmcneill 	int			nsegs;
128d4c79dc9Sjmcneill 	size_t			size;
129d4c79dc9Sjmcneill };
130d4c79dc9Sjmcneill 
131d4c79dc9Sjmcneill struct crmfb_softc {
132e313f620Smacallan 	device_t		sc_dev;
133d4c79dc9Sjmcneill 	struct vcons_data	sc_vd;
1349b94525aSmacallan 	struct i2c_controller	sc_i2c;
1359b94525aSmacallan 	int sc_dir;
136d4c79dc9Sjmcneill 
137d4c79dc9Sjmcneill 	bus_space_tag_t		sc_iot;
138d4c79dc9Sjmcneill 	bus_space_handle_t	sc_ioh;
139c5904c10Smacallan 	bus_space_handle_t	sc_reh;
1400904724eSmacallan 
141d4c79dc9Sjmcneill 	bus_dma_tag_t		sc_dmat;
142d4c79dc9Sjmcneill 
143d4c79dc9Sjmcneill 	struct crmfb_dma	sc_dma;
144d4c79dc9Sjmcneill 	struct crmfb_dma	sc_dmai;
145d4c79dc9Sjmcneill 
146d4c79dc9Sjmcneill 	int			sc_width;
147d4c79dc9Sjmcneill 	int			sc_height;
148d4c79dc9Sjmcneill 	int			sc_depth;
149fa88ee42Smacallan 	int			sc_console_depth;
150c5904c10Smacallan 	int			sc_tiles_x, sc_tiles_y;
151d4c79dc9Sjmcneill 	uint32_t		sc_fbsize;
152dca5aa4eSmacallan 	int			sc_mte_direction;
153fa88ee42Smacallan 	int			sc_mte_x_shift;
154fa88ee42Smacallan 	uint32_t		sc_mte_mode;
15571305eb6Smacallan 	uint32_t		sc_de_mode;
156a2db4fbdSmacallan 	uint32_t		sc_src_mode;
157a2db4fbdSmacallan 	uint32_t		sc_dst_mode;
158a2db4fbdSmacallan 	int			sc_needs_sync;
159a2db4fbdSmacallan 	uint8_t			*sc_lptr;
1604750dcc2Smacallan 	paddr_t			sc_linear;
16171305eb6Smacallan 	uint32_t		sc_vtflags;
16271305eb6Smacallan 	int			sc_wsmode, sc_video_on;
16371305eb6Smacallan 	uint8_t			sc_edid_data[128];
164713b73c8Smacallan 	struct edid_info 	sc_edid_info;
165d2924302Smacallan 
166d2924302Smacallan 	/* cursor stuff */
167d2924302Smacallan 	int			sc_cur_x;
168d2924302Smacallan 	int			sc_cur_y;
169d2924302Smacallan 	int			sc_hot_x;
170d2924302Smacallan 	int			sc_hot_y;
171d2924302Smacallan 
172d4c79dc9Sjmcneill 	u_char			sc_cmap_red[256];
173d4c79dc9Sjmcneill 	u_char			sc_cmap_green[256];
174d4c79dc9Sjmcneill 	u_char			sc_cmap_blue[256];
175d4c79dc9Sjmcneill };
176d4c79dc9Sjmcneill 
177d4c79dc9Sjmcneill static int	crmfb_putcmap(struct crmfb_softc *, struct wsdisplay_cmap *);
178d4c79dc9Sjmcneill static int	crmfb_getcmap(struct crmfb_softc *, struct wsdisplay_cmap *);
179d4c79dc9Sjmcneill static void	crmfb_set_palette(struct crmfb_softc *,
180d4c79dc9Sjmcneill 				  int, uint8_t, uint8_t, uint8_t);
181d2924302Smacallan static int	crmfb_set_curpos(struct crmfb_softc *, int, int);
182d2924302Smacallan static int	crmfb_gcursor(struct crmfb_softc *, struct wsdisplay_cursor *);
183d2924302Smacallan static int	crmfb_scursor(struct crmfb_softc *, struct wsdisplay_cursor *);
1840904724eSmacallan static inline void	crmfb_write_reg(struct crmfb_softc *, int, uint32_t);
185c5904c10Smacallan static inline uint32_t	crmfb_read_reg(struct crmfb_softc *, int);
186c5904c10Smacallan static int	crmfb_wait_dma_idle(struct crmfb_softc *);
1870904724eSmacallan 
1880904724eSmacallan /* setup video hw in given colour depth */
1890904724eSmacallan static int	crmfb_setup_video(struct crmfb_softc *, int);
1900904724eSmacallan static void	crmfb_setup_palette(struct crmfb_softc *);
191d4c79dc9Sjmcneill 
192c5904c10Smacallan static void crmfb_fill_rect(struct crmfb_softc *, int, int, int, int, uint32_t);
193c5904c10Smacallan static void crmfb_bitblt(struct crmfb_softc *, int, int, int, int, int, int,
194c5904c10Smacallan 			 uint32_t);
195eceb35ecSmacallan static void crmfb_scroll(struct crmfb_softc *, int, int, int, int, int, int);
196c5904c10Smacallan 
197c5904c10Smacallan static void	crmfb_copycols(void *, int, int, int, int);
198c5904c10Smacallan static void	crmfb_erasecols(void *, int, int, int, long);
199c5904c10Smacallan static void	crmfb_copyrows(void *, int, int, int);
200c5904c10Smacallan static void	crmfb_eraserows(void *, int, int, long);
201c5904c10Smacallan static void	crmfb_cursor(void *, int, int, int);
202c5904c10Smacallan static void	crmfb_putchar(void *, int, int, u_int, long);
203a2db4fbdSmacallan static void	crmfb_putchar_aa(void *, int, int, u_int, long);
204c5904c10Smacallan 
2059b94525aSmacallan /* I2C glue */
2069b94525aSmacallan static int crmfb_i2c_send_start(void *, int);
2079b94525aSmacallan static int crmfb_i2c_send_stop(void *, int);
2089b94525aSmacallan static int crmfb_i2c_initiate_xfer(void *, i2c_addr_t, int);
2099b94525aSmacallan static int crmfb_i2c_read_byte(void *, uint8_t *, int);
2109b94525aSmacallan static int crmfb_i2c_write_byte(void *, uint8_t, int);
2119b94525aSmacallan 
2129b94525aSmacallan /* I2C bitbang glue */
2139b94525aSmacallan static void crmfb_i2cbb_set_bits(void *, uint32_t);
2149b94525aSmacallan static void crmfb_i2cbb_set_dir(void *, uint32_t);
2159b94525aSmacallan static uint32_t crmfb_i2cbb_read(void *);
2169b94525aSmacallan 
2179b94525aSmacallan static const struct i2c_bitbang_ops crmfb_i2cbb_ops = {
2189b94525aSmacallan 	crmfb_i2cbb_set_bits,
2199b94525aSmacallan 	crmfb_i2cbb_set_dir,
2209b94525aSmacallan 	crmfb_i2cbb_read,
2219b94525aSmacallan 	{
2229b94525aSmacallan 		CRMFB_I2C_SDA,
2239b94525aSmacallan 		CRMFB_I2C_SCL,
2249b94525aSmacallan 		0,
2259b94525aSmacallan 		1
2269b94525aSmacallan 	}
2279b94525aSmacallan };
2289b94525aSmacallan static void crmfb_setup_ddc(struct crmfb_softc *);
2299b94525aSmacallan 
230713b73c8Smacallan /* mode setting stuff */
231713b73c8Smacallan static uint32_t calc_pll(int);	/* frequency in kHz */
232713b73c8Smacallan static int crmfb_set_mode(struct crmfb_softc *, const struct videomode *);
23395923c6fSjmcneill static int crmfb_parse_mode(const char *, struct videomode *);
234713b73c8Smacallan 
235e313f620Smacallan CFATTACH_DECL_NEW(crmfb, sizeof(struct crmfb_softc),
236d4c79dc9Sjmcneill     crmfb_match, crmfb_attach, NULL, NULL);
237d4c79dc9Sjmcneill 
238d4c79dc9Sjmcneill static int
crmfb_match(device_t parent,struct cfdata * cf,void * opaque)239e313f620Smacallan crmfb_match(device_t parent, struct cfdata *cf, void *opaque)
240d4c79dc9Sjmcneill {
241d4c79dc9Sjmcneill 	return crmfb_probe();
242d4c79dc9Sjmcneill }
243d4c79dc9Sjmcneill 
244d4c79dc9Sjmcneill static void
crmfb_attach(device_t parent,device_t self,void * opaque)245e313f620Smacallan crmfb_attach(device_t parent, device_t self, void *opaque)
246d4c79dc9Sjmcneill {
247d4c79dc9Sjmcneill 	struct mainbus_attach_args *ma;
248d4c79dc9Sjmcneill 	struct crmfb_softc *sc;
249d4c79dc9Sjmcneill 	struct rasops_info *ri;
250d4c79dc9Sjmcneill 	struct wsemuldisplaydev_attach_args aa;
251d4c79dc9Sjmcneill 	uint32_t d, h;
252d4c79dc9Sjmcneill 	uint16_t *p;
253d4c79dc9Sjmcneill 	unsigned long v;
254d4c79dc9Sjmcneill 	long defattr;
2557abea6b5Sjmcneill 	const char *consdev;
25695923c6fSjmcneill 	const char *modestr;
25795923c6fSjmcneill 	struct videomode mode, *pmode;
258c5904c10Smacallan 	int rv, i;
259d4c79dc9Sjmcneill 
260e313f620Smacallan 	sc = device_private(self);
261e313f620Smacallan 	sc->sc_dev = self;
262e313f620Smacallan 
263d4c79dc9Sjmcneill 	ma = (struct mainbus_attach_args *)opaque;
264d4c79dc9Sjmcneill 
265eb488f67Smacallan 	sc->sc_iot = normal_memt;
266d4c79dc9Sjmcneill 	sc->sc_dmat = &sgimips_default_bus_dma_tag;
267d4c79dc9Sjmcneill 	sc->sc_wsmode = WSDISPLAYIO_MODE_EMUL;
268d4c79dc9Sjmcneill 
269d4c79dc9Sjmcneill 	aprint_normal(": SGI CRIME Graphics Display Engine\n");
270d4c79dc9Sjmcneill 	rv = bus_space_map(sc->sc_iot, ma->ma_addr, 0 /* XXX */,
271d4c79dc9Sjmcneill 	    BUS_SPACE_MAP_LINEAR, &sc->sc_ioh);
272d4c79dc9Sjmcneill 	if (rv)
273d4c79dc9Sjmcneill 		panic("crmfb_attach: can't map I/O space");
274c5904c10Smacallan 	rv = bus_space_map(sc->sc_iot, 0x15000000, 0x6000, 0, &sc->sc_reh);
275c5904c10Smacallan 	if (rv)
276c5904c10Smacallan 		panic("crmfb_attach: can't map rendering engine");
277d4c79dc9Sjmcneill 
278d4c79dc9Sjmcneill 	/* determine mode configured by firmware */
279d4c79dc9Sjmcneill 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_VT_HCMAP);
280479b0a08Sjmcneill 	sc->sc_width = (d >> CRMFB_VT_HCMAP_ON_SHIFT) & 0xfff;
281479b0a08Sjmcneill 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_VT_VCMAP);
282479b0a08Sjmcneill 	sc->sc_height = (d >> CRMFB_VT_VCMAP_ON_SHIFT) & 0xfff;
283d4c79dc9Sjmcneill 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_TILESIZE);
284d4c79dc9Sjmcneill 	h = (d >> CRMFB_FRM_TILESIZE_DEPTH_SHIFT) & 0x3;
285d4c79dc9Sjmcneill 	if (h == 0)
286d4c79dc9Sjmcneill 		sc->sc_depth = 8;
287d4c79dc9Sjmcneill 	else if (h == 1)
288d4c79dc9Sjmcneill 		sc->sc_depth = 16;
289d4c79dc9Sjmcneill 	else
290d4c79dc9Sjmcneill 		sc->sc_depth = 32;
291d4c79dc9Sjmcneill 
29266684959Smartin 	if (sc->sc_width == 0 || sc->sc_height == 0) {
29371305eb6Smacallan 		/*
29471305eb6Smacallan 		 * XXX
29571305eb6Smacallan 		 * actually, these days we probably could
29671305eb6Smacallan 		 */
297e313f620Smacallan 		aprint_error_dev(sc->sc_dev,
298e313f620Smacallan 		    "device unusable if not setup by firmware\n");
29966684959Smartin 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, 0 /* XXX */);
30066684959Smartin 		return;
30166684959Smartin 	}
30266684959Smartin 
303e313f620Smacallan 	aprint_normal_dev(sc->sc_dev, "initial resolution %dx%d\n",
304e313f620Smacallan 	    sc->sc_width, sc->sc_height);
305d4c79dc9Sjmcneill 
306fa88ee42Smacallan 	sc->sc_console_depth = 8;
307fa88ee42Smacallan 
308713b73c8Smacallan 	crmfb_setup_ddc(sc);
30995923c6fSjmcneill 	pmode = sc->sc_edid_info.edid_preferred_mode;
31095923c6fSjmcneill 
31195923c6fSjmcneill 	modestr = arcbios_GetEnvironmentVariable("crmfb_mode");
31295923c6fSjmcneill 	if (crmfb_parse_mode(modestr, &mode) == 0)
31395923c6fSjmcneill 		pmode = &mode;
31495923c6fSjmcneill 
31595923c6fSjmcneill 	if (pmode != NULL && crmfb_set_mode(sc, pmode))
316713b73c8Smacallan 		aprint_normal_dev(sc->sc_dev, "using %dx%d\n",
317713b73c8Smacallan 		    sc->sc_width, sc->sc_height);
31895923c6fSjmcneill 
3190904724eSmacallan 	/*
3200904724eSmacallan 	 * first determine how many tiles we need
3210904724eSmacallan 	 * in 32bit each tile is 128x128 pixels
3220904724eSmacallan 	 */
323c5904c10Smacallan 	sc->sc_tiles_x = (sc->sc_width + 127) >> 7;
324c5904c10Smacallan 	sc->sc_tiles_y = (sc->sc_height + 127) >> 7;
325c5904c10Smacallan 	sc->sc_fbsize = 0x10000 * sc->sc_tiles_x * sc->sc_tiles_y;
326d4c79dc9Sjmcneill 
3270904724eSmacallan 	sc->sc_dmai.size = 256 * sizeof(uint16_t);
328d4c79dc9Sjmcneill 	rv = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmai.size, 65536, 0,
329d4c79dc9Sjmcneill 	    sc->sc_dmai.segs,
330d4c79dc9Sjmcneill 	    sizeof(sc->sc_dmai.segs) / sizeof(sc->sc_dmai.segs[0]),
331d4c79dc9Sjmcneill 	    &sc->sc_dmai.nsegs, BUS_DMA_NOWAIT);
332d4c79dc9Sjmcneill 	if (rv)
333d4c79dc9Sjmcneill 		panic("crmfb_attach: can't allocate DMA memory");
334d4c79dc9Sjmcneill 	rv = bus_dmamem_map(sc->sc_dmat, sc->sc_dmai.segs, sc->sc_dmai.nsegs,
335d4c79dc9Sjmcneill 	    sc->sc_dmai.size, &sc->sc_dmai.addr,
336d4c79dc9Sjmcneill 	    BUS_DMA_NOWAIT);
337d4c79dc9Sjmcneill 	if (rv)
338d4c79dc9Sjmcneill 		panic("crmfb_attach: can't map DMA memory");
339d4c79dc9Sjmcneill 	rv = bus_dmamap_create(sc->sc_dmat, sc->sc_dmai.size, 1,
340d4c79dc9Sjmcneill 	    sc->sc_dmai.size, 0, BUS_DMA_NOWAIT, &sc->sc_dmai.map);
341d4c79dc9Sjmcneill 	if (rv)
342d4c79dc9Sjmcneill 		panic("crmfb_attach: can't create DMA map");
343d4c79dc9Sjmcneill 	rv = bus_dmamap_load(sc->sc_dmat, sc->sc_dmai.map, sc->sc_dmai.addr,
344d4c79dc9Sjmcneill 	    sc->sc_dmai.size, NULL, BUS_DMA_NOWAIT);
345d4c79dc9Sjmcneill 	if (rv)
346d4c79dc9Sjmcneill 		panic("crmfb_attach: can't load DMA map");
347d4c79dc9Sjmcneill 
348a2db4fbdSmacallan 	/* allocate an extra 128Kb for a linear buffer */
349a2db4fbdSmacallan 	sc->sc_dma.size = 0x10000 * (16 * sc->sc_tiles_x + 2);
350d4c79dc9Sjmcneill 	rv = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dma.size, 65536, 0,
351d4c79dc9Sjmcneill 	    sc->sc_dma.segs,
352d4c79dc9Sjmcneill 	    sizeof(sc->sc_dma.segs) / sizeof(sc->sc_dma.segs[0]),
353d4c79dc9Sjmcneill 	    &sc->sc_dma.nsegs, BUS_DMA_NOWAIT);
354d4c79dc9Sjmcneill 	if (rv)
355d4c79dc9Sjmcneill 		panic("crmfb_attach: can't allocate DMA memory");
356d4c79dc9Sjmcneill 	rv = bus_dmamem_map(sc->sc_dmat, sc->sc_dma.segs, sc->sc_dma.nsegs,
357d4c79dc9Sjmcneill 	    sc->sc_dma.size, &sc->sc_dma.addr,
358d4c79dc9Sjmcneill 	    BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
359d4c79dc9Sjmcneill 	if (rv)
360d4c79dc9Sjmcneill 		panic("crmfb_attach: can't map DMA memory");
361d4c79dc9Sjmcneill 	rv = bus_dmamap_create(sc->sc_dmat, sc->sc_dma.size, 1,
362d4c79dc9Sjmcneill 	    sc->sc_dma.size, 0, BUS_DMA_NOWAIT, &sc->sc_dma.map);
363d4c79dc9Sjmcneill 	if (rv)
364d4c79dc9Sjmcneill 		panic("crmfb_attach: can't create DMA map");
365733da76fSmacallan 
366d4c79dc9Sjmcneill 	rv = bus_dmamap_load(sc->sc_dmat, sc->sc_dma.map, sc->sc_dma.addr,
367d4c79dc9Sjmcneill 	    sc->sc_dma.size, NULL, BUS_DMA_NOWAIT);
368d4c79dc9Sjmcneill 	if (rv)
369d4c79dc9Sjmcneill 		panic("crmfb_attach: can't load DMA map");
370d4c79dc9Sjmcneill 
371d4c79dc9Sjmcneill 	p = KERNADDR(sc->sc_dmai);
372d4c79dc9Sjmcneill 	v = (unsigned long)DMAADDR(sc->sc_dma);
373c5904c10Smacallan 	for (i = 0; i < (sc->sc_tiles_x * sc->sc_tiles_y); i++) {
374d4c79dc9Sjmcneill 		p[i] = ((uint32_t)v >> 16) + i;
375d4c79dc9Sjmcneill 	}
376733da76fSmacallan 
377a8858674Stsutsui 	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmai.map, 0, sc->sc_dmai.size,
378a8858674Stsutsui 	    BUS_DMASYNC_PREWRITE);
379733da76fSmacallan 
3804750dcc2Smacallan 	sc->sc_linear = (paddr_t)DMAADDR(sc->sc_dma) + 0x100000 * sc->sc_tiles_x;
381a2db4fbdSmacallan 	sc->sc_lptr =  (char *)KERNADDR(sc->sc_dma) + (0x100000 * sc->sc_tiles_x);
382d4c79dc9Sjmcneill 
383e313f620Smacallan 	aprint_normal_dev(sc->sc_dev, "allocated %d byte fb @ %p (%p)\n",
384d4c79dc9Sjmcneill 	    sc->sc_fbsize, KERNADDR(sc->sc_dmai), KERNADDR(sc->sc_dma));
385d4c79dc9Sjmcneill 
386fa88ee42Smacallan 	crmfb_setup_video(sc, sc->sc_console_depth);
387d4c79dc9Sjmcneill 	ri = &crmfb_console_screen.scr_ri;
388d4c79dc9Sjmcneill 	memset(ri, 0, sizeof(struct rasops_info));
38971305eb6Smacallan 	sc->sc_video_on = 1;
390d4c79dc9Sjmcneill 
391d4c79dc9Sjmcneill 	vcons_init(&sc->sc_vd, sc, &crmfb_defaultscreen, &crmfb_accessops);
392d4c79dc9Sjmcneill 	sc->sc_vd.init_screen = crmfb_init_screen;
393d4c79dc9Sjmcneill 	crmfb_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
394d4c79dc9Sjmcneill 	vcons_init_screen(&sc->sc_vd, &crmfb_console_screen, 1, &defattr);
395d4c79dc9Sjmcneill 
396d4c79dc9Sjmcneill 	crmfb_defaultscreen.ncols = ri->ri_cols;
397d4c79dc9Sjmcneill 	crmfb_defaultscreen.nrows = ri->ri_rows;
398d4c79dc9Sjmcneill 	crmfb_defaultscreen.textops = &ri->ri_ops;
399d4c79dc9Sjmcneill 	crmfb_defaultscreen.capabilities = ri->ri_caps;
400d4c79dc9Sjmcneill 	crmfb_defaultscreen.modecookie = NULL;
401d4c79dc9Sjmcneill 
4020904724eSmacallan 	crmfb_setup_palette(sc);
4039a7aee14Smacallan 	crmfb_fill_rect(sc, 0, 0, sc->sc_width, sc->sc_height,
4044395e1ddSmacallan 	    ri->ri_devcmap[(defattr >> 16) & 0xff]);
405d4c79dc9Sjmcneill 
40623347d39Smatt 	consdev = arcbios_GetEnvironmentVariable("ConsoleOut");
4077abea6b5Sjmcneill 	if (consdev != NULL && strcmp(consdev, "video()") == 0) {
408d4c79dc9Sjmcneill 		wsdisplay_cnattach(&crmfb_defaultscreen, ri, 0, 0, defattr);
4095bf2d324Smacallan 		vcons_replay_msgbuf(&crmfb_console_screen);
410d4c79dc9Sjmcneill 		aa.console = 1;
4117abea6b5Sjmcneill 	} else
4127abea6b5Sjmcneill 		aa.console = 0;
413d4c79dc9Sjmcneill 	aa.scrdata = &crmfb_screenlist;
414d4c79dc9Sjmcneill 	aa.accessops = &crmfb_accessops;
415d4c79dc9Sjmcneill 	aa.accesscookie = &sc->sc_vd;
416d4c79dc9Sjmcneill 
417c7fb772bSthorpej 	config_found(self, &aa, wsemuldisplaydevprint, CFARGS_NONE);
418d4c79dc9Sjmcneill 
419d2924302Smacallan 	sc->sc_cur_x = 0;
420d2924302Smacallan 	sc->sc_cur_y = 0;
421d2924302Smacallan 	sc->sc_hot_x = 0;
422d2924302Smacallan 	sc->sc_hot_y = 0;
423d2924302Smacallan 
424d4c79dc9Sjmcneill 	return;
425d4c79dc9Sjmcneill }
426d4c79dc9Sjmcneill 
427d4c79dc9Sjmcneill int
crmfb_probe(void)428d4c79dc9Sjmcneill crmfb_probe(void)
429d4c79dc9Sjmcneill {
430d4c79dc9Sjmcneill 
431d4c79dc9Sjmcneill         if (mach_type != MACH_SGI_IP32)
432d4c79dc9Sjmcneill                 return 0;
433d4c79dc9Sjmcneill 
434d4c79dc9Sjmcneill 	return 1;
435d4c79dc9Sjmcneill }
436d4c79dc9Sjmcneill 
437d4c79dc9Sjmcneill static int
crmfb_ioctl(void * v,void * vs,u_long cmd,void * data,int flag,struct lwp * l)438d4c79dc9Sjmcneill crmfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
439d4c79dc9Sjmcneill {
440d4c79dc9Sjmcneill 	struct vcons_data *vd;
441d4c79dc9Sjmcneill 	struct crmfb_softc *sc;
442d4c79dc9Sjmcneill 	struct wsdisplay_fbinfo *wdf;
443d4c79dc9Sjmcneill 	int nmode;
444d4c79dc9Sjmcneill 
445d4c79dc9Sjmcneill 	vd = (struct vcons_data *)v;
446d4c79dc9Sjmcneill 	sc = (struct crmfb_softc *)vd->cookie;
447d4c79dc9Sjmcneill 
448d4c79dc9Sjmcneill 	switch (cmd) {
449d4c79dc9Sjmcneill 	case WSDISPLAYIO_GTYPE:
450d4c79dc9Sjmcneill 		/* not really, but who cares? */
45171305eb6Smacallan 		/* xf86-video-crime does */
4520904724eSmacallan 		*(u_int *)data = WSDISPLAY_TYPE_CRIME;
453d4c79dc9Sjmcneill 		return 0;
454d4c79dc9Sjmcneill 	case WSDISPLAYIO_GINFO:
455d4c79dc9Sjmcneill 		if (vd->active != NULL) {
456d4c79dc9Sjmcneill 			wdf = (void *)data;
457d4c79dc9Sjmcneill 			wdf->height = sc->sc_height;
458d4c79dc9Sjmcneill 			wdf->width = sc->sc_width;
4590904724eSmacallan 			wdf->depth = 32;
460d4c79dc9Sjmcneill 			wdf->cmsize = 256;
461d4c79dc9Sjmcneill 			return 0;
462d4c79dc9Sjmcneill 		} else
463d4c79dc9Sjmcneill 			return ENODEV;
464d4c79dc9Sjmcneill 	case WSDISPLAYIO_GETCMAP:
465d4c79dc9Sjmcneill 		if (sc->sc_depth == 8)
466d4c79dc9Sjmcneill 			return crmfb_getcmap(sc, (struct wsdisplay_cmap *)data);
467d4c79dc9Sjmcneill 		else
468d4c79dc9Sjmcneill 			return EINVAL;
469d4c79dc9Sjmcneill 	case WSDISPLAYIO_PUTCMAP:
470d4c79dc9Sjmcneill 		if (sc->sc_depth == 8)
471d4c79dc9Sjmcneill 			return crmfb_putcmap(sc, (struct wsdisplay_cmap *)data);
472d4c79dc9Sjmcneill 		else
473d4c79dc9Sjmcneill 			return EINVAL;
474d4c79dc9Sjmcneill 	case WSDISPLAYIO_LINEBYTES:
475d4c79dc9Sjmcneill 		*(u_int *)data = sc->sc_width * sc->sc_depth / 8;
476d4c79dc9Sjmcneill 		return 0;
477d4c79dc9Sjmcneill 	case WSDISPLAYIO_SMODE:
478d4c79dc9Sjmcneill 		nmode = *(int *)data;
479d4c79dc9Sjmcneill 		if (nmode != sc->sc_wsmode) {
480d4c79dc9Sjmcneill 			sc->sc_wsmode = nmode;
4810904724eSmacallan 			if (nmode == WSDISPLAYIO_MODE_EMUL) {
482fa88ee42Smacallan 				crmfb_setup_video(sc, sc->sc_console_depth);
4830904724eSmacallan 				crmfb_setup_palette(sc);
484d4c79dc9Sjmcneill 				vcons_redraw_screen(vd->active);
4850904724eSmacallan 			} else {
4860904724eSmacallan 				crmfb_setup_video(sc, 32);
4870904724eSmacallan 			}
488d4c79dc9Sjmcneill 		}
489d4c79dc9Sjmcneill 		return 0;
490c1ee0b42Sjmcneill 	case WSDISPLAYIO_SVIDEO:
49171305eb6Smacallan 		{
49271305eb6Smacallan 			int d = *(int *)data;
49371305eb6Smacallan 			if (d == sc->sc_video_on)
49471305eb6Smacallan 				return 0;
49571305eb6Smacallan 			sc->sc_video_on = d;
49671305eb6Smacallan 			if (d == WSDISPLAYIO_VIDEO_ON) {
49771305eb6Smacallan 				crmfb_write_reg(sc,
49871305eb6Smacallan 				    CRMFB_VT_FLAGS, sc->sc_vtflags);
49971305eb6Smacallan 			} else {
50071305eb6Smacallan 				/* turn all SYNCs off */
50171305eb6Smacallan 				crmfb_write_reg(sc, CRMFB_VT_FLAGS,
50271305eb6Smacallan 				    sc->sc_vtflags | CRMFB_VT_FLAGS_VDRV_LOW |
50371305eb6Smacallan 				     CRMFB_VT_FLAGS_HDRV_LOW |
50471305eb6Smacallan 				     CRMFB_VT_FLAGS_SYNC_LOW);
50571305eb6Smacallan 			}
50671305eb6Smacallan 		}
50771305eb6Smacallan 		return 0;
50871305eb6Smacallan 
509c1ee0b42Sjmcneill 	case WSDISPLAYIO_GVIDEO:
51071305eb6Smacallan 		*(int *)data = sc->sc_video_on;
51171305eb6Smacallan 		return 0;
512d4c79dc9Sjmcneill 
513d2924302Smacallan 	case WSDISPLAYIO_GCURPOS:
514d2924302Smacallan 		{
515d2924302Smacallan 			struct wsdisplay_curpos *pos;
516d2924302Smacallan 
517d2924302Smacallan 			pos = (struct wsdisplay_curpos *)data;
518d2924302Smacallan 			pos->x = sc->sc_cur_x;
519d2924302Smacallan 			pos->y = sc->sc_cur_y;
520d2924302Smacallan 		}
521d2924302Smacallan 		return 0;
522d2924302Smacallan 	case WSDISPLAYIO_SCURPOS:
523d2924302Smacallan 		{
524d2924302Smacallan 			struct wsdisplay_curpos *pos;
525d2924302Smacallan 
526d2924302Smacallan 			pos = (struct wsdisplay_curpos *)data;
527d2924302Smacallan 			crmfb_set_curpos(sc, pos->x, pos->y);
528d2924302Smacallan 		}
529d2924302Smacallan 		return 0;
530d2924302Smacallan 	case WSDISPLAYIO_GCURMAX:
531d2924302Smacallan 		{
532d2924302Smacallan 			struct wsdisplay_curpos *pos;
533d2924302Smacallan 
534d2924302Smacallan 			pos = (struct wsdisplay_curpos *)data;
535d2924302Smacallan 			pos->x = 32;
536d2924302Smacallan 			pos->y = 32;
537d2924302Smacallan 		}
538d2924302Smacallan 		return 0;
539d2924302Smacallan 	case WSDISPLAYIO_GCURSOR:
540d2924302Smacallan 		{
541d2924302Smacallan 			struct wsdisplay_cursor *cu;
542d2924302Smacallan 
543d2924302Smacallan 			cu = (struct wsdisplay_cursor *)data;
544d2924302Smacallan 			return crmfb_gcursor(sc, cu);
545d2924302Smacallan 		}
546d2924302Smacallan 	case WSDISPLAYIO_SCURSOR:
547d2924302Smacallan 		{
548d2924302Smacallan 			struct wsdisplay_cursor *cu;
549d2924302Smacallan 
550d2924302Smacallan 			cu = (struct wsdisplay_cursor *)data;
551d2924302Smacallan 			return crmfb_scursor(sc, cu);
552d2924302Smacallan 		}
55371305eb6Smacallan 	case WSDISPLAYIO_GET_EDID: {
55471305eb6Smacallan 		struct wsdisplayio_edid_info *d = data;
55571305eb6Smacallan 
55671305eb6Smacallan 		d->data_size = 128;
55771305eb6Smacallan 		if (d->buffer_size < 128)
55871305eb6Smacallan 			return EAGAIN;
55971305eb6Smacallan 		if (sc->sc_edid_data[1] == 0)
56071305eb6Smacallan 			return ENODATA;
56171305eb6Smacallan 		return copyout(sc->sc_edid_data, d->edid_data, 128);
56271305eb6Smacallan 	}
563d2924302Smacallan 	}
564d4c79dc9Sjmcneill 	return EPASSTHROUGH;
565d4c79dc9Sjmcneill }
566d4c79dc9Sjmcneill 
567d4c79dc9Sjmcneill static paddr_t
crmfb_mmap(void * v,void * vs,off_t offset,int prot)568d4c79dc9Sjmcneill crmfb_mmap(void *v, void *vs, off_t offset, int prot)
569d4c79dc9Sjmcneill {
570d4c79dc9Sjmcneill 	struct vcons_data *vd;
571d4c79dc9Sjmcneill 	struct crmfb_softc *sc;
572d4c79dc9Sjmcneill 	paddr_t pa;
573d4c79dc9Sjmcneill 
574d4c79dc9Sjmcneill 	vd = (struct vcons_data *)v;
575d4c79dc9Sjmcneill 	sc = (struct crmfb_softc *)vd->cookie;
576d4c79dc9Sjmcneill 
5779a7aee14Smacallan 	/* we probably shouldn't let anyone mmap the framebuffer */
5780904724eSmacallan #if 1
579b14b0cafSmacallan 	if (offset >= 0 && offset < (0x100000 * sc->sc_tiles_x)) {
580d4c79dc9Sjmcneill 		pa = bus_dmamem_mmap(sc->sc_dmat, sc->sc_dma.segs,
581d4c79dc9Sjmcneill 		    sc->sc_dma.nsegs, offset, prot,
582eb488f67Smacallan 		    BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_PREFETCHABLE);
583d4c79dc9Sjmcneill 		return pa;
584d4c79dc9Sjmcneill 	}
5850904724eSmacallan #endif
5869a7aee14Smacallan 	/*
5879a7aee14Smacallan 	 * here would the TLBs be but we don't want to show them to userland
5889a7aee14Smacallan 	 * so we return the page containing the status register
5899a7aee14Smacallan 	 */
5909a7aee14Smacallan 	if ((offset >= 0x15000000) && (offset < 0x15002000))
5919a7aee14Smacallan 		return bus_space_mmap(sc->sc_iot, 0x15004000, 0, prot, 0);
5929a7aee14Smacallan 	/* now the actual engine registers */
5939a7aee14Smacallan 	if ((offset >= 0x15002000) && (offset < 0x15005000))
5949a7aee14Smacallan 		return bus_space_mmap(sc->sc_iot, offset, 0, prot, 0);
595a2db4fbdSmacallan 	/* and now the linear area */
596a2db4fbdSmacallan 	if ((offset >= 0x15010000) && (offset < 0x15030000))
5979a7aee14Smacallan 		return bus_dmamem_mmap(sc->sc_dmat, sc->sc_dma.segs,
5984750dcc2Smacallan 		     sc->sc_dma.nsegs,
599eb488f67Smacallan 		     offset + (0x100000 * sc->sc_tiles_x) - 0x15010000, prot,
600eb488f67Smacallan 		     BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_PREFETCHABLE);
601d4c79dc9Sjmcneill 	return -1;
602d4c79dc9Sjmcneill }
603d4c79dc9Sjmcneill 
604d4c79dc9Sjmcneill static void
crmfb_init_screen(void * c,struct vcons_screen * scr,int existing,long * defattr)605d4c79dc9Sjmcneill crmfb_init_screen(void *c, struct vcons_screen *scr, int existing,
606d4c79dc9Sjmcneill     long *defattr)
607d4c79dc9Sjmcneill {
608d4c79dc9Sjmcneill 	struct crmfb_softc *sc;
609d4c79dc9Sjmcneill 	struct rasops_info *ri;
610d4c79dc9Sjmcneill 
611d4c79dc9Sjmcneill 	sc = (struct crmfb_softc *)c;
612d4c79dc9Sjmcneill 	ri = &scr->scr_ri;
613d4c79dc9Sjmcneill 
614733da76fSmacallan 	scr->scr_flags |= VCONS_LOADFONT;
615733da76fSmacallan 
616733da76fSmacallan 	ri->ri_flg = RI_CENTER | RI_FULLCLEAR |
617733da76fSmacallan 		     RI_ENABLE_ALPHA | RI_PREFER_ALPHA;
618fa88ee42Smacallan 	ri->ri_depth = sc->sc_console_depth;
619d4c79dc9Sjmcneill 	ri->ri_width = sc->sc_width;
620d4c79dc9Sjmcneill 	ri->ri_height = sc->sc_height;
621d4c79dc9Sjmcneill 	ri->ri_stride = ri->ri_width * (ri->ri_depth / 8);
62271305eb6Smacallan 
623d4c79dc9Sjmcneill 	switch (ri->ri_depth) {
62471305eb6Smacallan 	case 8:
62571305eb6Smacallan 		ri->ri_flg |= RI_8BIT_IS_RGB;
62671305eb6Smacallan 		break;
627d4c79dc9Sjmcneill 	case 16:
628d4c79dc9Sjmcneill 		ri->ri_rnum = ri->ri_gnum = ri->ri_bnum = 5;
629fa88ee42Smacallan 		ri->ri_rpos = 11;
630fa88ee42Smacallan 		ri->ri_gpos = 6;
631fa88ee42Smacallan 		ri->ri_bpos = 1;
632d4c79dc9Sjmcneill 		break;
633d4c79dc9Sjmcneill 	case 32:
634d4c79dc9Sjmcneill 		ri->ri_rnum = ri->ri_gnum = ri->ri_bnum = 8;
635d4c79dc9Sjmcneill 		ri->ri_rpos = 8;
636d4c79dc9Sjmcneill 		ri->ri_gpos = 16;
637d4c79dc9Sjmcneill 		ri->ri_bpos = 24;
638d4c79dc9Sjmcneill 		break;
639d4c79dc9Sjmcneill 	}
64071305eb6Smacallan 
641e71a84eaSmacallan 	ri->ri_bits = NULL;
642d4c79dc9Sjmcneill 
643d6837d19Smacallan 	rasops_init(ri, 0, 0);
644733da76fSmacallan 	ri->ri_caps = WSSCREEN_WSCOLORS | WSSCREEN_RESIZE;
645d4c79dc9Sjmcneill 	rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight,
646d4c79dc9Sjmcneill 	    ri->ri_width / ri->ri_font->fontwidth);
647d4c79dc9Sjmcneill 	ri->ri_hw = scr;
648d4c79dc9Sjmcneill 
649c5904c10Smacallan 	ri->ri_ops.cursor    = crmfb_cursor;
650c5904c10Smacallan 	ri->ri_ops.copyrows  = crmfb_copyrows;
651c5904c10Smacallan 	ri->ri_ops.eraserows = crmfb_eraserows;
652c5904c10Smacallan 	ri->ri_ops.copycols  = crmfb_copycols;
653c5904c10Smacallan 	ri->ri_ops.erasecols = crmfb_erasecols;
654a2db4fbdSmacallan 	if (FONT_IS_ALPHA(ri->ri_font)) {
655a2db4fbdSmacallan 		ri->ri_ops.putchar   = crmfb_putchar_aa;
656a2db4fbdSmacallan 	} else {
657c5904c10Smacallan 		ri->ri_ops.putchar   = crmfb_putchar;
658a2db4fbdSmacallan 	}
659d4c79dc9Sjmcneill 	return;
660d4c79dc9Sjmcneill }
661d4c79dc9Sjmcneill 
662d4c79dc9Sjmcneill static int
crmfb_putcmap(struct crmfb_softc * sc,struct wsdisplay_cmap * cm)663d4c79dc9Sjmcneill crmfb_putcmap(struct crmfb_softc *sc, struct wsdisplay_cmap *cm)
664d4c79dc9Sjmcneill {
665d4c79dc9Sjmcneill 	u_int idx, cnt;
666d4c79dc9Sjmcneill 	u_char r[256], g[256], b[256];
667d4c79dc9Sjmcneill 	u_char *rp, *gp, *bp;
668d4c79dc9Sjmcneill 	int rv, i;
669d4c79dc9Sjmcneill 
670d4c79dc9Sjmcneill 	idx = cm->index;
671d4c79dc9Sjmcneill 	cnt = cm->count;
672d4c79dc9Sjmcneill 
673d4c79dc9Sjmcneill 	if (idx >= 255 || cnt > 256 || idx + cnt > 256)
674d4c79dc9Sjmcneill 		return EINVAL;
675d4c79dc9Sjmcneill 
676d4c79dc9Sjmcneill 	rv = copyin(cm->red, &r[idx], cnt);
677d4c79dc9Sjmcneill 	if (rv)
678d4c79dc9Sjmcneill 		return rv;
679d4c79dc9Sjmcneill 	rv = copyin(cm->green, &g[idx], cnt);
680d4c79dc9Sjmcneill 	if (rv)
681d4c79dc9Sjmcneill 		return rv;
682d4c79dc9Sjmcneill 	rv = copyin(cm->blue, &b[idx], cnt);
683d4c79dc9Sjmcneill 	if (rv)
684d4c79dc9Sjmcneill 		return rv;
685d4c79dc9Sjmcneill 
686d4c79dc9Sjmcneill 	memcpy(&sc->sc_cmap_red[idx], &r[idx], cnt);
687d4c79dc9Sjmcneill 	memcpy(&sc->sc_cmap_green[idx], &g[idx], cnt);
688d4c79dc9Sjmcneill 	memcpy(&sc->sc_cmap_blue[idx], &b[idx], cnt);
689d4c79dc9Sjmcneill 
690d4c79dc9Sjmcneill 	rp = &sc->sc_cmap_red[idx];
691d4c79dc9Sjmcneill 	gp = &sc->sc_cmap_green[idx];
692d4c79dc9Sjmcneill 	bp = &sc->sc_cmap_blue[idx];
693d4c79dc9Sjmcneill 
694d4c79dc9Sjmcneill 	for (i = 0; i < cnt; i++) {
695d4c79dc9Sjmcneill 		crmfb_set_palette(sc, idx, *rp, *gp, *bp);
696d4c79dc9Sjmcneill 		idx++;
697d4c79dc9Sjmcneill 		rp++, gp++, bp++;
698d4c79dc9Sjmcneill 	}
699d4c79dc9Sjmcneill 
700d4c79dc9Sjmcneill 	return 0;
701d4c79dc9Sjmcneill }
702d4c79dc9Sjmcneill 
703d4c79dc9Sjmcneill static int
crmfb_getcmap(struct crmfb_softc * sc,struct wsdisplay_cmap * cm)704d4c79dc9Sjmcneill crmfb_getcmap(struct crmfb_softc *sc, struct wsdisplay_cmap *cm)
705d4c79dc9Sjmcneill {
706d4c79dc9Sjmcneill 	u_int idx, cnt;
707d4c79dc9Sjmcneill 	int rv;
708d4c79dc9Sjmcneill 
709d4c79dc9Sjmcneill 	idx = cm->index;
710d4c79dc9Sjmcneill 	cnt = cm->count;
711d4c79dc9Sjmcneill 
712d4c79dc9Sjmcneill 	if (idx >= 255 || cnt > 256 || idx + cnt > 256)
713d4c79dc9Sjmcneill 		return EINVAL;
714d4c79dc9Sjmcneill 
715d4c79dc9Sjmcneill 	rv = copyout(&sc->sc_cmap_red[idx], cm->red, cnt);
716d4c79dc9Sjmcneill 	if (rv)
717d4c79dc9Sjmcneill 		return rv;
718d4c79dc9Sjmcneill 	rv = copyout(&sc->sc_cmap_green[idx], cm->green, cnt);
719d4c79dc9Sjmcneill 	if (rv)
720d4c79dc9Sjmcneill 		return rv;
721d4c79dc9Sjmcneill 	rv = copyout(&sc->sc_cmap_blue[idx], cm->blue, cnt);
722d4c79dc9Sjmcneill 	if (rv)
723d4c79dc9Sjmcneill 		return rv;
724d4c79dc9Sjmcneill 
725d4c79dc9Sjmcneill 	return 0;
726d4c79dc9Sjmcneill }
727d4c79dc9Sjmcneill 
728d4c79dc9Sjmcneill static void
crmfb_set_palette(struct crmfb_softc * sc,int reg,uint8_t r,uint8_t g,uint8_t b)729d4c79dc9Sjmcneill crmfb_set_palette(struct crmfb_softc *sc, int reg, uint8_t r, uint8_t g,
730d4c79dc9Sjmcneill     uint8_t b)
731d4c79dc9Sjmcneill {
732d4c79dc9Sjmcneill 	uint32_t val;
733d4c79dc9Sjmcneill 
734d4c79dc9Sjmcneill 	if (reg > 255 || sc->sc_depth != 8)
735d4c79dc9Sjmcneill 		return;
736d4c79dc9Sjmcneill 
737d4c79dc9Sjmcneill 	while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_CMAP_FIFO) >= 63)
738d4c79dc9Sjmcneill 		DELAY(10);
739d4c79dc9Sjmcneill 
7405f170d1cSjmcneill 	val = (r << 8) | (g << 16) | (b << 24);
7410904724eSmacallan 	crmfb_write_reg(sc, CRMFB_CMAP + (reg * 4), val);
742d4c79dc9Sjmcneill 
743d4c79dc9Sjmcneill 	return;
744d4c79dc9Sjmcneill }
745d2924302Smacallan 
746d2924302Smacallan static int
crmfb_set_curpos(struct crmfb_softc * sc,int x,int y)747d2924302Smacallan crmfb_set_curpos(struct crmfb_softc *sc, int x, int y)
748d2924302Smacallan {
749d2924302Smacallan 	uint32_t val;
750d2924302Smacallan 
751d2924302Smacallan 	sc->sc_cur_x = x;
752d2924302Smacallan 	sc->sc_cur_y = y;
753d2924302Smacallan 
754d2924302Smacallan 	val = ((x - sc->sc_hot_x) & 0xffff) | ((y - sc->sc_hot_y) << 16);
7550904724eSmacallan 	crmfb_write_reg(sc, CRMFB_CURSOR_POS, val);
756d2924302Smacallan 
757d2924302Smacallan 	return 0;
758d2924302Smacallan }
759d2924302Smacallan 
760d2924302Smacallan static int
crmfb_gcursor(struct crmfb_softc * sc,struct wsdisplay_cursor * cur)761d2924302Smacallan crmfb_gcursor(struct crmfb_softc *sc, struct wsdisplay_cursor *cur)
762d2924302Smacallan {
763d2924302Smacallan 	/* do nothing for now */
764d2924302Smacallan 	return 0;
765d2924302Smacallan }
766d2924302Smacallan 
767d2924302Smacallan static int
crmfb_scursor(struct crmfb_softc * sc,struct wsdisplay_cursor * cur)768d2924302Smacallan crmfb_scursor(struct crmfb_softc *sc, struct wsdisplay_cursor *cur)
769d2924302Smacallan {
770d2924302Smacallan 	if (cur->which & WSDISPLAY_CURSOR_DOCUR) {
771d2924302Smacallan 
7720904724eSmacallan 		crmfb_write_reg(sc, CRMFB_CURSOR_CONTROL, cur->enable ? 1 : 0);
773d2924302Smacallan 	}
774d2924302Smacallan 	if (cur->which & WSDISPLAY_CURSOR_DOHOT) {
775d2924302Smacallan 
776d2924302Smacallan 		sc->sc_hot_x = cur->hot.x;
777d2924302Smacallan 		sc->sc_hot_y = cur->hot.y;
778d2924302Smacallan 	}
779d2924302Smacallan 	if (cur->which & WSDISPLAY_CURSOR_DOPOS) {
780d2924302Smacallan 
781d2924302Smacallan 		crmfb_set_curpos(sc, cur->pos.x, cur->pos.y);
782d2924302Smacallan 	}
783d2924302Smacallan 	if (cur->which & WSDISPLAY_CURSOR_DOCMAP) {
784d2924302Smacallan 		int i;
785d2924302Smacallan 		uint32_t val;
786d2924302Smacallan 
787d2924302Smacallan 		for (i = 0; i < cur->cmap.count; i++) {
788d2924302Smacallan 			val = (cur->cmap.red[i] << 24) |
789d2924302Smacallan 			      (cur->cmap.green[i] << 16) |
790d2924302Smacallan 			      (cur->cmap.blue[i] << 8);
7910904724eSmacallan 			crmfb_write_reg(sc, CRMFB_CURSOR_CMAP0 +
7920904724eSmacallan 			    ((i + cur->cmap.index) << 2), val);
793d2924302Smacallan 		}
794d2924302Smacallan 	}
795d2924302Smacallan 	if (cur->which & WSDISPLAY_CURSOR_DOSHAPE) {
796d2924302Smacallan 
797d2924302Smacallan 		int i, j, cnt = 0;
798d2924302Smacallan 		uint32_t latch = 0, omask;
799d2924302Smacallan 		uint8_t imask;
800d2924302Smacallan 		for (i = 0; i < 64; i++) {
801d2924302Smacallan 			omask = 0x80000000;
802d2924302Smacallan 			imask = 0x01;
803d2924302Smacallan 			cur->image[cnt] &= cur->mask[cnt];
804d2924302Smacallan 			for (j = 0; j < 8; j++) {
805d2924302Smacallan 				if (cur->image[cnt] & imask)
806d2924302Smacallan 					latch |= omask;
807d2924302Smacallan 				omask >>= 1;
808d2924302Smacallan 				if (cur->mask[cnt] & imask)
809d2924302Smacallan 					latch |= omask;
810d2924302Smacallan 				omask >>= 1;
811d2924302Smacallan 				imask <<= 1;
812d2924302Smacallan 			}
813d2924302Smacallan 			cnt++;
814d2924302Smacallan 			imask = 0x01;
815d2924302Smacallan 			cur->image[cnt] &= cur->mask[cnt];
816d2924302Smacallan 			for (j = 0; j < 8; j++) {
817d2924302Smacallan 				if (cur->image[cnt] & imask)
818d2924302Smacallan 					latch |= omask;
819d2924302Smacallan 				omask >>= 1;
820d2924302Smacallan 				if (cur->mask[cnt] & imask)
821d2924302Smacallan 					latch |= omask;
822d2924302Smacallan 				omask >>= 1;
823d2924302Smacallan 				imask <<= 1;
824d2924302Smacallan 			}
825d2924302Smacallan 			cnt++;
8260904724eSmacallan 			crmfb_write_reg(sc, CRMFB_CURSOR_BITMAP + (i << 2),
8270904724eSmacallan 			    latch);
828d2924302Smacallan 			latch = 0;
829d2924302Smacallan 		}
830d2924302Smacallan 	}
831d2924302Smacallan 	return 0;
832d2924302Smacallan }
8330904724eSmacallan 
8340904724eSmacallan static inline void
crmfb_write_reg(struct crmfb_softc * sc,int offset,uint32_t val)8350904724eSmacallan crmfb_write_reg(struct crmfb_softc *sc, int offset, uint32_t val)
8360904724eSmacallan {
8370904724eSmacallan 
8380904724eSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, val);
8390904724eSmacallan 	wbflush();
8400904724eSmacallan }
8410904724eSmacallan 
842c5904c10Smacallan static inline uint32_t
crmfb_read_reg(struct crmfb_softc * sc,int offset)843c5904c10Smacallan crmfb_read_reg(struct crmfb_softc *sc, int offset)
844c5904c10Smacallan {
845c5904c10Smacallan 
846c5904c10Smacallan 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset);
847c5904c10Smacallan }
848c5904c10Smacallan 
849a2db4fbdSmacallan static inline void
crmfb_wait_idle(struct crmfb_softc * sc)850a2db4fbdSmacallan crmfb_wait_idle(struct crmfb_softc *sc)
851a2db4fbdSmacallan {
852a2db4fbdSmacallan 	int i = 0;
853a2db4fbdSmacallan 
854a2db4fbdSmacallan 	do {
855a2db4fbdSmacallan 		i++;
856a2db4fbdSmacallan 	} while (((bus_space_read_4(sc->sc_iot, sc->sc_reh, CRIME_DE_STATUS) &
857a2db4fbdSmacallan 		   CRIME_DE_IDLE) == 0) && (i < 100000000));
858a2db4fbdSmacallan 	if (i >= 100000000)
859a2db4fbdSmacallan 		aprint_error("crmfb_wait_idle() timed out\n");
860a2db4fbdSmacallan 	sc->sc_needs_sync = 0;
861a2db4fbdSmacallan }
862a2db4fbdSmacallan 
863a2db4fbdSmacallan /* writes to CRIME_DE_MODE_* only take effect when the engine is idle */
864a2db4fbdSmacallan 
865a2db4fbdSmacallan static inline void
crmfb_src_mode(struct crmfb_softc * sc,uint32_t mode)866a2db4fbdSmacallan crmfb_src_mode(struct crmfb_softc *sc, uint32_t mode)
867a2db4fbdSmacallan {
868a2db4fbdSmacallan 	if (mode == sc->sc_src_mode)
869a2db4fbdSmacallan 		return;
870a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_MODE_SRC, mode);
871a2db4fbdSmacallan 	sc->sc_needs_sync = 1;
872a2db4fbdSmacallan 	sc->sc_src_mode = mode;
873a2db4fbdSmacallan }
874a2db4fbdSmacallan 
875a2db4fbdSmacallan static inline void
crmfb_dst_mode(struct crmfb_softc * sc,uint32_t mode)876a2db4fbdSmacallan crmfb_dst_mode(struct crmfb_softc *sc, uint32_t mode)
877a2db4fbdSmacallan {
878a2db4fbdSmacallan 	if (mode == sc->sc_dst_mode)
879a2db4fbdSmacallan 		return;
880a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_MODE_DST, mode);
881a2db4fbdSmacallan 	sc->sc_needs_sync = 1;
882a2db4fbdSmacallan 	sc->sc_dst_mode = mode;
883a2db4fbdSmacallan }
884a2db4fbdSmacallan 
885a2db4fbdSmacallan static inline void
crmfb_make_room(struct crmfb_softc * sc,int num)886a2db4fbdSmacallan crmfb_make_room(struct crmfb_softc *sc, int num)
887a2db4fbdSmacallan {
888a2db4fbdSmacallan 	int i = 0, slots;
889a2db4fbdSmacallan 	uint32_t status;
890a2db4fbdSmacallan 
891a2db4fbdSmacallan 	if (sc->sc_needs_sync != 0) {
892a2db4fbdSmacallan 		crmfb_wait_idle(sc);
893a2db4fbdSmacallan 		return;
894a2db4fbdSmacallan 	}
895a2db4fbdSmacallan 
896a2db4fbdSmacallan 	do {
897a2db4fbdSmacallan 		i++;
898a2db4fbdSmacallan 		status = bus_space_read_4(sc->sc_iot, sc->sc_reh,
899a2db4fbdSmacallan 		    CRIME_DE_STATUS);
900a2db4fbdSmacallan 		slots = 60 - CRIME_PIPE_LEVEL(status);
901a2db4fbdSmacallan 	} while (slots <= num);
902a2db4fbdSmacallan }
903a2db4fbdSmacallan 
904c5904c10Smacallan static int
crmfb_wait_dma_idle(struct crmfb_softc * sc)905c5904c10Smacallan crmfb_wait_dma_idle(struct crmfb_softc *sc)
906c5904c10Smacallan {
907c5904c10Smacallan 	int bail = 100000, idle;
908c5904c10Smacallan 
909c5904c10Smacallan 	do {
910c5904c10Smacallan 		idle = ((bus_space_read_4(sc->sc_iot, sc->sc_ioh,
911c5904c10Smacallan 		         CRMFB_OVR_CONTROL) & 1) == 0) &&
912c5904c10Smacallan 		       ((bus_space_read_4(sc->sc_iot, sc->sc_ioh,
913c5904c10Smacallan 		         CRMFB_FRM_CONTROL) & 1) == 0) &&
914c5904c10Smacallan 		       ((bus_space_read_4(sc->sc_iot, sc->sc_ioh,
915c5904c10Smacallan 		         CRMFB_DID_CONTROL) & 1) == 0);
916c5904c10Smacallan 		if (!idle)
917c5904c10Smacallan 			delay(10);
918c5904c10Smacallan 		bail--;
919c5904c10Smacallan 	} while ((!idle) && (bail > 0));
920c5904c10Smacallan 	return idle;
921c5904c10Smacallan }
922c5904c10Smacallan 
9230904724eSmacallan static int
crmfb_setup_video(struct crmfb_softc * sc,int depth)9240904724eSmacallan crmfb_setup_video(struct crmfb_softc *sc, int depth)
9250904724eSmacallan {
926c5904c10Smacallan 	uint64_t reg;
92771305eb6Smacallan 	uint32_t d, h, page;
928713b73c8Smacallan 	int i, bail, tile_width, tlbptr, lptr, j, tx, shift, overhang;
929ab0a73e2Ssekiya 	const char *wantsync;
930c5904c10Smacallan 	uint16_t v;
9310904724eSmacallan 
9320904724eSmacallan 	/* disable DMA */
9330904724eSmacallan 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_OVR_CONTROL);
9340904724eSmacallan 	d &= ~(1 << CRMFB_OVR_CONTROL_DMAEN_SHIFT);
9350904724eSmacallan 	crmfb_write_reg(sc, CRMFB_OVR_CONTROL, d);
9360904724eSmacallan 	DELAY(50000);
9370904724eSmacallan 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_CONTROL);
9380904724eSmacallan 	d &= ~(1 << CRMFB_FRM_CONTROL_DMAEN_SHIFT);
9390904724eSmacallan 	crmfb_write_reg(sc, CRMFB_FRM_CONTROL, d);
9400904724eSmacallan 	DELAY(50000);
941713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_DID_CONTROL, d);
9420904724eSmacallan 	DELAY(50000);
9430904724eSmacallan 
944c5904c10Smacallan 	if (!crmfb_wait_dma_idle(sc))
945c5904c10Smacallan 		aprint_error("crmfb: crmfb_wait_dma_idle timed out\n");
946c5904c10Smacallan 
9470904724eSmacallan 	/* ensure that CRM starts drawing at the top left of the screen
9480904724eSmacallan 	 * when we re-enable DMA later
9490904724eSmacallan 	 */
9500904724eSmacallan 	d = (1 << CRMFB_VT_XY_FREEZE_SHIFT);
9510904724eSmacallan 	crmfb_write_reg(sc, CRMFB_VT_XY, d);
9520904724eSmacallan 	delay(1000);
9530904724eSmacallan 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_DOTCLOCK);
9540904724eSmacallan 	d &= ~(1 << CRMFB_DOTCLOCK_CLKRUN_SHIFT);
9550904724eSmacallan 	crmfb_write_reg(sc, CRMFB_DOTCLOCK, d);
956c5904c10Smacallan 
957c5904c10Smacallan 	/* wait for dotclock to turn off */
958c5904c10Smacallan 	bail = 10000;
959c5904c10Smacallan 	while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_DOTCLOCK) &
960c5904c10Smacallan 	    (1 << CRMFB_DOTCLOCK_CLKRUN_SHIFT)) && (bail > 0)) {
961c5904c10Smacallan 		delay(10);
962c5904c10Smacallan 		bail--;
963c5904c10Smacallan 	}
9640904724eSmacallan 
9650904724eSmacallan 	/* reset FIFO */
9660904724eSmacallan 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_TILESIZE);
9670904724eSmacallan 	d |= (1 << CRMFB_FRM_TILESIZE_FIFOR_SHIFT);
9680904724eSmacallan 	crmfb_write_reg(sc, CRMFB_FRM_TILESIZE, d);
9690904724eSmacallan 	d &= ~(1 << CRMFB_FRM_TILESIZE_FIFOR_SHIFT);
9700904724eSmacallan 	crmfb_write_reg(sc, CRMFB_FRM_TILESIZE, d);
9710904724eSmacallan 
9720904724eSmacallan 	/* setup colour mode */
9730904724eSmacallan 	switch (depth) {
9740904724eSmacallan 	case 8:
97571305eb6Smacallan 		h = CRMFB_MODE_TYP_RG3B2;
976c5904c10Smacallan 		tile_width = 512;
9770904724eSmacallan 		break;
9780904724eSmacallan 	case 16:
9790904724eSmacallan 		h = CRMFB_MODE_TYP_ARGB5;
980c5904c10Smacallan 		tile_width = 256;
9810904724eSmacallan 		break;
9820904724eSmacallan 	case 32:
9830904724eSmacallan 		h = CRMFB_MODE_TYP_RGB8;
984c5904c10Smacallan 		tile_width = 128;
9850904724eSmacallan 		break;
9860904724eSmacallan 	default:
9870904724eSmacallan 		panic("Unsupported depth");
9880904724eSmacallan 	}
9890904724eSmacallan 	d = h << CRMFB_MODE_TYP_SHIFT;
9900904724eSmacallan 	d |= CRMFB_MODE_BUF_BOTH << CRMFB_MODE_BUF_SHIFT;
9910904724eSmacallan 	for (i = 0; i < (32 * 4); i += 4)
9920904724eSmacallan 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, CRMFB_MODE + i, d);
9930904724eSmacallan 	wbflush();
9940904724eSmacallan 
9950904724eSmacallan 	/* setup tile pointer, but don't turn on DMA yet! */
9960904724eSmacallan 	h = DMAADDR(sc->sc_dmai);
9970904724eSmacallan 	d = (h >> 9) << CRMFB_FRM_CONTROL_TILEPTR_SHIFT;
9980904724eSmacallan 	crmfb_write_reg(sc, CRMFB_FRM_CONTROL, d);
9990904724eSmacallan 
10000904724eSmacallan 	/* init framebuffer width and pixel size */
1001c5904c10Smacallan 	/*d = (1 << CRMFB_FRM_TILESIZE_WIDTH_SHIFT);*/
1002c5904c10Smacallan 
1003c5904c10Smacallan 	d = ((int)(sc->sc_width / tile_width)) <<
1004c5904c10Smacallan 	    CRMFB_FRM_TILESIZE_WIDTH_SHIFT;
1005713b73c8Smacallan 	overhang = sc->sc_width % tile_width;
1006713b73c8Smacallan 	if (overhang != 0) {
1007713b73c8Smacallan 		uint32_t val;
1008713b73c8Smacallan 		DPRINTF("tile width: %d\n", tile_width);
1009713b73c8Smacallan 		DPRINTF("overhang: %d\n", overhang);
1010713b73c8Smacallan 		val = (overhang * (depth >> 3)) >> 5;
1011713b73c8Smacallan 		DPRINTF("reg: %08x\n", val);
1012713b73c8Smacallan 		d |= (val & 0x1f);
1013713b73c8Smacallan 		DPRINTF("d: %08x\n", d);
1014713b73c8Smacallan 	}
1015c5904c10Smacallan 
10160904724eSmacallan 	switch (depth) {
10170904724eSmacallan 	case 8:
10180904724eSmacallan 		h = CRMFB_FRM_TILESIZE_DEPTH_8;
10190904724eSmacallan 		break;
10200904724eSmacallan 	case 16:
10210904724eSmacallan 		h = CRMFB_FRM_TILESIZE_DEPTH_16;
10220904724eSmacallan 		break;
10230904724eSmacallan 	case 32:
10240904724eSmacallan 		h = CRMFB_FRM_TILESIZE_DEPTH_32;
10250904724eSmacallan 		break;
10260904724eSmacallan 	default:
10270904724eSmacallan 		panic("Unsupported depth");
10280904724eSmacallan 	}
10290904724eSmacallan 	d |= (h << CRMFB_FRM_TILESIZE_DEPTH_SHIFT);
10300904724eSmacallan 	crmfb_write_reg(sc, CRMFB_FRM_TILESIZE, d);
10310904724eSmacallan 
1032c5904c10Smacallan 	/*h = sc->sc_width * sc->sc_height / (512 / (depth >> 3));*/
1033c5904c10Smacallan 	h = sc->sc_height;
10340904724eSmacallan 	d = h << CRMFB_FRM_PIXSIZE_HEIGHT_SHIFT;
10350904724eSmacallan 	crmfb_write_reg(sc, CRMFB_FRM_PIXSIZE, d);
10360904724eSmacallan 
10370904724eSmacallan 	/* turn off firmware overlay and hardware cursor */
10380904724eSmacallan 	crmfb_write_reg(sc, CRMFB_OVR_WIDTH_TILE, 0);
10390904724eSmacallan 	crmfb_write_reg(sc, CRMFB_CURSOR_CONTROL, 0);
10400904724eSmacallan 
10410904724eSmacallan 	/* turn on DMA for the framebuffer */
10420904724eSmacallan 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_CONTROL);
10430904724eSmacallan 	d |= (1 << CRMFB_FRM_CONTROL_DMAEN_SHIFT);
10440904724eSmacallan 	crmfb_write_reg(sc, CRMFB_FRM_CONTROL, d);
10450904724eSmacallan 
1046713b73c8Smacallan 	/* enable drawing again */
1047713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_XY, 0);
1048713b73c8Smacallan 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_DOTCLOCK);
1049713b73c8Smacallan 	d |= (1 << CRMFB_DOTCLOCK_CLKRUN_SHIFT);
1050713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_DOTCLOCK, d);
1051713b73c8Smacallan 
1052ab0a73e2Ssekiya 	/* turn off sync-on-green */
1053ab0a73e2Ssekiya 
105423347d39Smatt 	wantsync = arcbios_GetEnvironmentVariable("SyncOnGreen");
1055ab0a73e2Ssekiya 	if ( (wantsync != NULL) && (wantsync[0] == 'n') ) {
105671305eb6Smacallan 		sc->sc_vtflags |= CRMFB_VT_FLAGS_SYNC_LOW;
1057ab0a73e2Ssekiya 		crmfb_write_reg(sc, CRMFB_VT_FLAGS, d);
1058ab0a73e2Ssekiya 	}
1059ab0a73e2Ssekiya 
10600904724eSmacallan 	sc->sc_depth = depth;
1061c5904c10Smacallan 
1062c5904c10Smacallan 	/* finally set up the drawing engine's TLB A */
1063c5904c10Smacallan 	v = (DMAADDR(sc->sc_dma) >> 16) & 0xffff;
1064c5904c10Smacallan 	tlbptr = 0;
1065eceb35ecSmacallan 	tx = ((sc->sc_width + (tile_width - 1)) & ~(tile_width - 1)) /
1066eceb35ecSmacallan 	    tile_width;
10679a7aee14Smacallan 
1068713b73c8Smacallan 	DPRINTF("tx: %d\n", tx);
10694750dcc2Smacallan 
10704750dcc2Smacallan 	for (i = 0; i < 16; i++) {
1071c5904c10Smacallan 		reg = 0;
1072c5904c10Smacallan 		shift = 64;
10734750dcc2Smacallan 		lptr = 0;
1074c5904c10Smacallan 		for (j = 0; j < tx; j++) {
1075c5904c10Smacallan 			shift -= 16;
1076c5904c10Smacallan 			reg |= (((uint64_t)(v | 0x8000)) << shift);
1077c5904c10Smacallan 			if (shift == 0) {
1078c5904c10Smacallan 				shift = 64;
1079c5904c10Smacallan 				bus_space_write_8(sc->sc_iot, sc->sc_reh,
10804750dcc2Smacallan 				    CRIME_RE_TLB_A + tlbptr + lptr,
1081eceb35ecSmacallan 				    reg);
10827ed121adSplunky 				DPRINTF("%04x: %016"PRIx64"\n", tlbptr + lptr, reg);
10834750dcc2Smacallan 				reg = 0;
10844750dcc2Smacallan 				lptr += 8;
1085c5904c10Smacallan 			}
1086c5904c10Smacallan 			v++;
1087c5904c10Smacallan 		}
1088c5904c10Smacallan 		if (shift != 64) {
1089c5904c10Smacallan 			bus_space_write_8(sc->sc_iot, sc->sc_reh,
10904750dcc2Smacallan 			    CRIME_RE_TLB_A + tlbptr + lptr, reg);
10917ed121adSplunky 			DPRINTF("%04x: %016"PRIx64"\n", tlbptr + lptr, reg);
1092c5904c10Smacallan 		}
1093c5904c10Smacallan 		tlbptr += 32;
1094c5904c10Smacallan 	}
1095c5904c10Smacallan 
1096a2db4fbdSmacallan 	/* now put the last 128kB into the 1st linear TLB */
10974750dcc2Smacallan 	page = (sc->sc_linear >> 12) | 0x80000000;
10984750dcc2Smacallan 	tlbptr = 0;
1099a2db4fbdSmacallan 	for (i = 0; i < 16; i++) {
11004750dcc2Smacallan 		reg = ((uint64_t)page << 32) | (page + 1);
11014750dcc2Smacallan 		bus_space_write_8(sc->sc_iot, sc->sc_reh,
11024750dcc2Smacallan 		    CRIME_RE_LINEAR_A + tlbptr, reg);
11034750dcc2Smacallan 		page += 2;
11044750dcc2Smacallan 		tlbptr += 8;
11054750dcc2Smacallan 	}
1106c5904c10Smacallan 	wbflush();
1107c5904c10Smacallan 
1108c5904c10Smacallan 	/* do some very basic engine setup */
1109c5904c10Smacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_CLIPMODE, 0);
1110c5904c10Smacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_WINOFFSET_SRC, 0);
1111c5904c10Smacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_WINOFFSET_DST, 0);
1112eceb35ecSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_PLANEMASK,
1113eceb35ecSmacallan 	    0xffffffff);
1114c5904c10Smacallan 
1115c5904c10Smacallan 	bus_space_write_8(sc->sc_iot, sc->sc_reh, 0x20, 0);
1116c5904c10Smacallan 	bus_space_write_8(sc->sc_iot, sc->sc_reh, 0x28, 0);
1117c5904c10Smacallan 	bus_space_write_8(sc->sc_iot, sc->sc_reh, 0x30, 0);
1118c5904c10Smacallan 	bus_space_write_8(sc->sc_iot, sc->sc_reh, 0x38, 0);
1119c5904c10Smacallan 	bus_space_write_8(sc->sc_iot, sc->sc_reh, 0x40, 0);
1120c5904c10Smacallan 
1121c5904c10Smacallan 	switch (depth) {
1122c5904c10Smacallan 	case 8:
112371305eb6Smacallan 		sc->sc_de_mode = DE_MODE_TLB_A | DE_MODE_BUFDEPTH_8 |
1124c5904c10Smacallan 		    DE_MODE_TYPE_CI | DE_MODE_PIXDEPTH_8;
1125fa88ee42Smacallan 		sc->sc_mte_mode = MTE_MODE_DST_ECC |
1126fa88ee42Smacallan 		    (MTE_TLB_A << MTE_DST_TLB_SHIFT) |
1127fa88ee42Smacallan 		    (MTE_TLB_A << MTE_SRC_TLB_SHIFT) |
1128fa88ee42Smacallan 		    (MTE_DEPTH_8 << MTE_DEPTH_SHIFT);
1129fa88ee42Smacallan 		sc->sc_mte_x_shift = 0;
1130c5904c10Smacallan 		break;
1131c5904c10Smacallan 	case 16:
113271305eb6Smacallan 		sc->sc_de_mode = DE_MODE_TLB_A | DE_MODE_BUFDEPTH_16 |
1133fa88ee42Smacallan 		    DE_MODE_TYPE_RGBA | DE_MODE_PIXDEPTH_16;
1134fa88ee42Smacallan 		sc->sc_mte_mode = MTE_MODE_DST_ECC |
1135fa88ee42Smacallan 		    (MTE_TLB_A << MTE_DST_TLB_SHIFT) |
1136fa88ee42Smacallan 		    (MTE_TLB_A << MTE_SRC_TLB_SHIFT) |
1137fa88ee42Smacallan 		    (MTE_DEPTH_16 << MTE_DEPTH_SHIFT);
1138fa88ee42Smacallan 		sc->sc_mte_x_shift = 1;
11399a7aee14Smacallan 		break;
1140c5904c10Smacallan 	case 32:
114171305eb6Smacallan 		sc->sc_de_mode = DE_MODE_TLB_A | DE_MODE_BUFDEPTH_32 |
1142c5904c10Smacallan 		    DE_MODE_TYPE_RGBA | DE_MODE_PIXDEPTH_32;
1143fa88ee42Smacallan 		sc->sc_mte_mode = MTE_MODE_DST_ECC |
1144fa88ee42Smacallan 		    (MTE_TLB_A << MTE_DST_TLB_SHIFT) |
1145fa88ee42Smacallan 		    (MTE_TLB_A << MTE_SRC_TLB_SHIFT) |
1146fa88ee42Smacallan 		    (MTE_DEPTH_32 << MTE_DEPTH_SHIFT);
1147fa88ee42Smacallan 		sc->sc_mte_x_shift = 2;
1148451f7eb5Schristos 		break;
1149c5904c10Smacallan 	default:
1150e9921331Smsaitoh 		panic("%s: unsupported colour depth %d\n", __func__,
1151c5904c10Smacallan 		    depth);
1152c5904c10Smacallan 	}
1153a2db4fbdSmacallan 	sc->sc_needs_sync = 0;
1154a2db4fbdSmacallan 	sc->sc_src_mode = 0xffffffff;
1155a2db4fbdSmacallan 	sc->sc_dst_mode = 0xffffffff;
1156a2db4fbdSmacallan 
1157a2db4fbdSmacallan 	crmfb_src_mode(sc, sc->sc_de_mode);
1158a2db4fbdSmacallan 	crmfb_dst_mode(sc, sc->sc_de_mode);
1159a2db4fbdSmacallan 
1160c5904c10Smacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_XFER_STEP_X, 1);
1161c5904c10Smacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_XFER_STEP_Y, 1);
1162eceb35ecSmacallan 
1163eceb35ecSmacallan 	/* initialize memory transfer engine */
1164eceb35ecSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_MODE,
1165fa88ee42Smacallan 	    sc->sc_mte_mode | MTE_MODE_COPY);
1166dca5aa4eSmacallan 	sc->sc_mte_direction = 1;
1167d1dee129Smacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_DST_Y_STEP, 1);
1168d1dee129Smacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_SRC_Y_STEP, 1);
1169eceb35ecSmacallan 
11700904724eSmacallan 	return 0;
11710904724eSmacallan }
11720904724eSmacallan 
1173104b3395Smacallan static void
crmfb_set_mte_direction(struct crmfb_softc * sc,int dir)1174dca5aa4eSmacallan crmfb_set_mte_direction(struct crmfb_softc *sc, int dir)
1175dca5aa4eSmacallan {
1176dca5aa4eSmacallan 	if (dir == sc->sc_mte_direction)
1177dca5aa4eSmacallan 		return;
1178dca5aa4eSmacallan 
1179a2db4fbdSmacallan 	crmfb_make_room(sc, 2);
1180dca5aa4eSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_DST_Y_STEP, dir);
1181dca5aa4eSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_SRC_Y_STEP, dir);
1182864b6f1cSmacallan 	sc->sc_mte_direction = dir;
1183dca5aa4eSmacallan }
1184dca5aa4eSmacallan 
11850904724eSmacallan static void
crmfb_setup_palette(struct crmfb_softc * sc)11860904724eSmacallan crmfb_setup_palette(struct crmfb_softc *sc)
11870904724eSmacallan {
1188a2db4fbdSmacallan 	int i, j, x;
1189a2db4fbdSmacallan 	uint32_t col;
1190a2db4fbdSmacallan 	struct rasops_info *ri = &crmfb_console_screen.scr_ri;
11910904724eSmacallan 
11920904724eSmacallan 	for (i = 0; i < 256; i++) {
11930904724eSmacallan 		crmfb_set_palette(sc, i, rasops_cmap[(i * 3) + 2],
11940904724eSmacallan 		    rasops_cmap[(i * 3) + 1], rasops_cmap[(i * 3) + 0]);
11950904724eSmacallan 		sc->sc_cmap_red[i] = rasops_cmap[(i * 3) + 2];
11960904724eSmacallan 		sc->sc_cmap_green[i] = rasops_cmap[(i * 3) + 1];
11970904724eSmacallan 		sc->sc_cmap_blue[i] = rasops_cmap[(i * 3) + 0];
11980904724eSmacallan 	}
1199a2db4fbdSmacallan 
1200a2db4fbdSmacallan 	if (FONT_IS_ALPHA(ri->ri_font)) {
1201a2db4fbdSmacallan 		sc->sc_de_mode =
1202a2db4fbdSmacallan 		    (sc->sc_de_mode & ~DE_MODE_TYPE_MASK) | DE_MODE_TYPE_RGB;
12030904724eSmacallan 	}
1204c5904c10Smacallan 
1205a2db4fbdSmacallan 	/* draw 16 character cells in 32bit RGBA for alpha blending */
1206a2db4fbdSmacallan 	crmfb_make_room(sc, 3);
1207a2db4fbdSmacallan 	crmfb_dst_mode(sc,
1208a2db4fbdSmacallan 	    DE_MODE_TLB_A |
1209a2db4fbdSmacallan 	    DE_MODE_BUFDEPTH_32 |
1210a2db4fbdSmacallan 	    DE_MODE_TYPE_RGBA |
1211a2db4fbdSmacallan 	    DE_MODE_PIXDEPTH_32);
1212a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_DRAWMODE,
1213a2db4fbdSmacallan 	    DE_DRAWMODE_PLANEMASK | DE_DRAWMODE_BYTEMASK);
1214a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_PRIMITIVE,
1215a2db4fbdSmacallan 	    DE_PRIM_RECTANGLE | DE_PRIM_TB);
1216a2db4fbdSmacallan 	j = 0;
1217a2db4fbdSmacallan 	x = 0;
1218a2db4fbdSmacallan 	for (i = 0; i < 16; i++) {
1219a2db4fbdSmacallan 		crmfb_make_room(sc, 2);
1220a2db4fbdSmacallan 		col = (rasops_cmap[j] << 24) |
1221a2db4fbdSmacallan 		      (rasops_cmap[j + 1] << 16) |
1222a2db4fbdSmacallan 		      (rasops_cmap[j + 2] << 8);
1223a2db4fbdSmacallan 		bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_FG, col);
1224a2db4fbdSmacallan 		bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_X_VERTEX_0,
1225a2db4fbdSmacallan 	    	    (x << 16) | ((sc->sc_height - 500) & 0xffff));
1226a2db4fbdSmacallan 		bus_space_write_4(sc->sc_iot, sc->sc_reh,
1227a2db4fbdSmacallan 	    	    CRIME_DE_X_VERTEX_1 | CRIME_DE_START,
1228a2db4fbdSmacallan 		    ((x + ri->ri_font->fontwidth - 1)  << 16) |
1229a2db4fbdSmacallan 		    ((sc->sc_height + ri->ri_font->fontheight - 1) & 0xffff));
1230a2db4fbdSmacallan 		j += 3;
1231a2db4fbdSmacallan 		x += ri->ri_font->fontwidth;
1232a2db4fbdSmacallan 	}
1233a2db4fbdSmacallan 	crmfb_dst_mode(sc, sc->sc_de_mode);
1234c5904c10Smacallan }
1235c5904c10Smacallan 
1236c5904c10Smacallan static void
crmfb_fill_rect(struct crmfb_softc * sc,int x,int y,int width,int height,uint32_t colour)1237c5904c10Smacallan crmfb_fill_rect(struct crmfb_softc *sc, int x, int y, int width, int height,
1238c5904c10Smacallan     uint32_t colour)
1239c5904c10Smacallan {
1240fa88ee42Smacallan 	int rxa, rxe;
1241fa88ee42Smacallan 
1242fa88ee42Smacallan 	rxa = x << sc->sc_mte_x_shift;
1243fa88ee42Smacallan 	rxe = ((x + width) << sc->sc_mte_x_shift) - 1;
1244dca5aa4eSmacallan 	crmfb_set_mte_direction(sc, 1);
1245a2db4fbdSmacallan 	crmfb_make_room(sc, 4);
12464395e1ddSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_MODE,
1247fa88ee42Smacallan 	    sc->sc_mte_mode | 0);
12484395e1ddSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_BG, colour);
12494395e1ddSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_DST0,
1250fa88ee42Smacallan 	    (rxa << 16) | (y & 0xffff));
1251c5904c10Smacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh,
12524395e1ddSmacallan 	    CRIME_MTE_DST1 | CRIME_DE_START,
1253fa88ee42Smacallan 	    (rxe << 16) | ((y + height - 1) & 0xffff));
1254c5904c10Smacallan }
1255c5904c10Smacallan 
1256c5904c10Smacallan static void
crmfb_bitblt(struct crmfb_softc * sc,int xs,int ys,int xd,int yd,int wi,int he,uint32_t rop)1257c5904c10Smacallan crmfb_bitblt(struct crmfb_softc *sc, int xs, int ys, int xd, int yd,
1258c5904c10Smacallan     int wi, int he, uint32_t rop)
1259c5904c10Smacallan {
1260c5904c10Smacallan 	uint32_t prim = DE_PRIM_RECTANGLE;
1261c5904c10Smacallan 	int rxa, rya, rxe, rye, rxs, rys;
1262a2db4fbdSmacallan 	crmfb_make_room(sc, 2);
1263a2db4fbdSmacallan 	crmfb_src_mode(sc, sc->sc_de_mode);
1264a2db4fbdSmacallan 	crmfb_make_room(sc, 6);
1265c5904c10Smacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_DRAWMODE,
1266c5904c10Smacallan 	    DE_DRAWMODE_PLANEMASK | DE_DRAWMODE_BYTEMASK | DE_DRAWMODE_ROP |
1267c5904c10Smacallan 	    DE_DRAWMODE_XFER_EN);
1268c5904c10Smacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_ROP, rop);
1269c5904c10Smacallan 	if (xs < xd) {
1270c5904c10Smacallan 		prim |= DE_PRIM_RL;
1271c5904c10Smacallan 		rxe = xd;
1272c5904c10Smacallan 		rxa = xd + wi - 1;
1273c5904c10Smacallan 		rxs = xs + wi - 1;
1274c5904c10Smacallan 	} else {
1275c5904c10Smacallan 		prim |= DE_PRIM_LR;
1276c5904c10Smacallan 		rxe = xd + wi - 1;
1277c5904c10Smacallan 		rxa = xd;
1278c5904c10Smacallan 		rxs = xs;
1279c5904c10Smacallan 	}
1280c5904c10Smacallan 	if (ys < yd) {
1281c5904c10Smacallan 		prim |= DE_PRIM_BT;
1282c5904c10Smacallan 		rye = yd;
1283c5904c10Smacallan 		rya = yd + he - 1;
1284c5904c10Smacallan 		rys = ys + he - 1;
1285c5904c10Smacallan 	} else {
1286c5904c10Smacallan 		prim |= DE_PRIM_TB;
1287c5904c10Smacallan 		rye = yd + he - 1;
1288c5904c10Smacallan 		rya = yd;
1289c5904c10Smacallan 		rys = ys;
1290c5904c10Smacallan 	}
1291c5904c10Smacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_PRIMITIVE, prim);
1292c5904c10Smacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_XFER_ADDR_SRC,
1293c5904c10Smacallan 	    (rxs << 16) | (rys & 0xffff));
1294c5904c10Smacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_X_VERTEX_0,
1295c5904c10Smacallan 	    (rxa << 16) | (rya & 0xffff));
1296c5904c10Smacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh,
1297c5904c10Smacallan 	    CRIME_DE_X_VERTEX_1 | CRIME_DE_START,
1298c5904c10Smacallan 	    (rxe << 16) | (rye & 0xffff));
1299c5904c10Smacallan }
1300c5904c10Smacallan 
1301eceb35ecSmacallan static void
crmfb_scroll(struct crmfb_softc * sc,int xs,int ys,int xd,int yd,int wi,int he)1302eceb35ecSmacallan crmfb_scroll(struct crmfb_softc *sc, int xs, int ys, int xd, int yd,
1303eceb35ecSmacallan     int wi, int he)
1304eceb35ecSmacallan {
1305eceb35ecSmacallan 	int rxa, rya, rxe, rye, rxd, ryd, rxde, ryde;
1306eceb35ecSmacallan 
1307fa88ee42Smacallan 	rxa = xs << sc->sc_mte_x_shift;
1308fa88ee42Smacallan 	rxd = xd << sc->sc_mte_x_shift;
1309fa88ee42Smacallan 	rxe = ((xs + wi) << sc->sc_mte_x_shift) - 1;
1310fa88ee42Smacallan 	rxde = ((xd + wi) << sc->sc_mte_x_shift) - 1;
1311d1dee129Smacallan 
1312a2db4fbdSmacallan 	crmfb_make_room(sc, 1);
1313d1dee129Smacallan 
13144395e1ddSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_MODE,
1315fa88ee42Smacallan 	    sc->sc_mte_mode | MTE_MODE_COPY);
13164395e1ddSmacallan 
1317eceb35ecSmacallan 	if (ys < yd) {
1318eceb35ecSmacallan 		/* bottom to top */
1319eceb35ecSmacallan 		rye = ys;
1320eceb35ecSmacallan 		rya = ys + he - 1;
1321eceb35ecSmacallan 		ryd = yd + he - 1;
1322eceb35ecSmacallan 		ryde = yd;
1323dca5aa4eSmacallan 		crmfb_set_mte_direction(sc, -1);
1324eceb35ecSmacallan 	} else {
1325eceb35ecSmacallan 		/* top to bottom */
1326eceb35ecSmacallan 		rye = ys + he - 1;
1327eceb35ecSmacallan 		rya = ys;
1328eceb35ecSmacallan 		ryd = yd;
1329eceb35ecSmacallan 		ryde = yd + he - 1;
1330dca5aa4eSmacallan 		crmfb_set_mte_direction(sc, 1);
1331eceb35ecSmacallan 	}
1332a2db4fbdSmacallan 	crmfb_make_room(sc, 4);
1333eceb35ecSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_SRC0,
1334eceb35ecSmacallan 	    (rxa << 16) | rya);
1335eceb35ecSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_SRC1,
1336eceb35ecSmacallan 	    (rxe << 16) | rye);
1337eceb35ecSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh,
1338eceb35ecSmacallan 	    CRIME_MTE_DST0,
1339eceb35ecSmacallan 	    (rxd << 16) | ryd);
1340eceb35ecSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_MTE_DST1 |
1341eceb35ecSmacallan 	    CRIME_DE_START,
1342eceb35ecSmacallan 	    (rxde << 16) | ryde);
1343eceb35ecSmacallan }
1344eceb35ecSmacallan 
1345c5904c10Smacallan static void
crmfb_copycols(void * cookie,int row,int srccol,int dstcol,int ncols)1346c5904c10Smacallan crmfb_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
1347c5904c10Smacallan {
1348c5904c10Smacallan 	struct rasops_info *ri = cookie;
1349c5904c10Smacallan 	struct vcons_screen *scr = ri->ri_hw;
1350c5904c10Smacallan 	int32_t xs, xd, y, width, height;
1351c5904c10Smacallan 
1352c5904c10Smacallan 	xs = ri->ri_xorigin + ri->ri_font->fontwidth * srccol;
1353c5904c10Smacallan 	xd = ri->ri_xorigin + ri->ri_font->fontwidth * dstcol;
1354c5904c10Smacallan 	y = ri->ri_yorigin + ri->ri_font->fontheight * row;
1355c5904c10Smacallan 	width = ri->ri_font->fontwidth * ncols;
1356c5904c10Smacallan 	height = ri->ri_font->fontheight;
1357c5904c10Smacallan 	crmfb_bitblt(scr->scr_cookie, xs, y, xd, y, width, height, 3);
1358c5904c10Smacallan }
1359c5904c10Smacallan 
1360c5904c10Smacallan static void
crmfb_erasecols(void * cookie,int row,int startcol,int ncols,long fillattr)1361c5904c10Smacallan crmfb_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
1362c5904c10Smacallan {
1363c5904c10Smacallan 	struct rasops_info *ri = cookie;
1364c5904c10Smacallan 	struct vcons_screen *scr = ri->ri_hw;
1365c5904c10Smacallan 	int32_t x, y, width, height, bg;
1366c5904c10Smacallan 
1367c5904c10Smacallan 	x = ri->ri_xorigin + ri->ri_font->fontwidth * startcol;
1368c5904c10Smacallan 	y = ri->ri_yorigin + ri->ri_font->fontheight * row;
1369c5904c10Smacallan 	width = ri->ri_font->fontwidth * ncols;
1370c5904c10Smacallan 	height = ri->ri_font->fontheight;
1371c5904c10Smacallan 	bg = (uint32_t)ri->ri_devcmap[(fillattr >> 16) & 0xff];
1372c5904c10Smacallan 	crmfb_fill_rect(scr->scr_cookie, x, y, width, height, bg);
1373c5904c10Smacallan }
1374c5904c10Smacallan 
1375c5904c10Smacallan static void
crmfb_copyrows(void * cookie,int srcrow,int dstrow,int nrows)1376c5904c10Smacallan crmfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
1377c5904c10Smacallan {
1378c5904c10Smacallan 	struct rasops_info *ri = cookie;
1379c5904c10Smacallan 	struct vcons_screen *scr = ri->ri_hw;
1380c5904c10Smacallan 	int32_t x, ys, yd, width, height;
1381c5904c10Smacallan 
1382c5904c10Smacallan 	x = ri->ri_xorigin;
1383c5904c10Smacallan 	ys = ri->ri_yorigin + ri->ri_font->fontheight * srcrow;
1384c5904c10Smacallan 	yd = ri->ri_yorigin + ri->ri_font->fontheight * dstrow;
1385c5904c10Smacallan 	width = ri->ri_emuwidth;
1386c5904c10Smacallan 	height = ri->ri_font->fontheight * nrows;
1387eceb35ecSmacallan 
1388eceb35ecSmacallan 	crmfb_scroll(scr->scr_cookie, x, ys, x, yd, width, height);
1389eceb35ecSmacallan }
1390c5904c10Smacallan 
1391c5904c10Smacallan static void
crmfb_eraserows(void * cookie,int row,int nrows,long fillattr)1392c5904c10Smacallan crmfb_eraserows(void *cookie, int row, int nrows, long fillattr)
1393c5904c10Smacallan {
1394c5904c10Smacallan 	struct rasops_info *ri = cookie;
1395c5904c10Smacallan 	struct vcons_screen *scr = ri->ri_hw;
1396c5904c10Smacallan 	int32_t x, y, width, height, bg;
1397c5904c10Smacallan 
1398c5904c10Smacallan 	if ((row == 0) && (nrows == ri->ri_rows)) {
1399c5904c10Smacallan 		x = y = 0;
1400c5904c10Smacallan 		width = ri->ri_width;
1401c5904c10Smacallan 		height = ri->ri_height;
1402c5904c10Smacallan 	} else {
1403c5904c10Smacallan 		x = ri->ri_xorigin;
1404c5904c10Smacallan 		y = ri->ri_yorigin + ri->ri_font->fontheight * row;
1405c5904c10Smacallan 		width = ri->ri_emuwidth;
1406c5904c10Smacallan 		height = ri->ri_font->fontheight * nrows;
1407c5904c10Smacallan 	}
1408c5904c10Smacallan 	bg = (uint32_t)ri->ri_devcmap[(fillattr >> 16) & 0xff];
1409c5904c10Smacallan 	crmfb_fill_rect(scr->scr_cookie, x, y, width, height, bg);
1410c5904c10Smacallan }
1411c5904c10Smacallan 
1412c5904c10Smacallan static void
crmfb_cursor(void * cookie,int on,int row,int col)1413c5904c10Smacallan crmfb_cursor(void *cookie, int on, int row, int col)
1414c5904c10Smacallan {
1415c5904c10Smacallan 	struct rasops_info *ri = cookie;
1416c5904c10Smacallan 	struct vcons_screen *scr = ri->ri_hw;
1417c5904c10Smacallan 	struct crmfb_softc *sc = scr->scr_cookie;
1418c5904c10Smacallan 	int x, y, wi,he;
1419c5904c10Smacallan 
1420c5904c10Smacallan 	wi = ri->ri_font->fontwidth;
1421c5904c10Smacallan 	he = ri->ri_font->fontheight;
1422c5904c10Smacallan 
1423c5904c10Smacallan 	if (ri->ri_flg & RI_CURSOR) {
1424c5904c10Smacallan 		x = ri->ri_ccol * wi + ri->ri_xorigin;
1425c5904c10Smacallan 		y = ri->ri_crow * he + ri->ri_yorigin;
1426c5904c10Smacallan 		crmfb_bitblt(sc, x, y, x, y, wi, he, 12);
1427c5904c10Smacallan 		ri->ri_flg &= ~RI_CURSOR;
1428c5904c10Smacallan 	}
1429c5904c10Smacallan 
1430c5904c10Smacallan 	ri->ri_crow = row;
1431c5904c10Smacallan 	ri->ri_ccol = col;
1432c5904c10Smacallan 
1433c5904c10Smacallan 	if (on)
1434c5904c10Smacallan 	{
1435c5904c10Smacallan 		x = ri->ri_ccol * wi + ri->ri_xorigin;
1436c5904c10Smacallan 		y = ri->ri_crow * he + ri->ri_yorigin;
1437c5904c10Smacallan 		crmfb_bitblt(sc, x, y, x, y, wi, he, 12);
1438c5904c10Smacallan 		ri->ri_flg |= RI_CURSOR;
1439c5904c10Smacallan 	}
1440c5904c10Smacallan }
1441c5904c10Smacallan 
1442c5904c10Smacallan static void
crmfb_putchar(void * cookie,int row,int col,u_int c,long attr)1443c5904c10Smacallan crmfb_putchar(void *cookie, int row, int col, u_int c, long attr)
1444c5904c10Smacallan {
1445c5904c10Smacallan 	struct rasops_info *ri = cookie;
1446c5904c10Smacallan 	struct vcons_screen *scr = ri->ri_hw;
1447c5904c10Smacallan 	struct crmfb_softc *sc = scr->scr_cookie;
14485bf2d324Smacallan 	struct wsdisplay_font *font = PICK_FONT(ri, c);
14495bf2d324Smacallan 	uint32_t bg, fg;
14505bf2d324Smacallan 	int x, y, wi, he, i, uc;
14515bf2d324Smacallan 	uint8_t *fd8;
14525bf2d324Smacallan 	uint16_t *fd16;
14535bf2d324Smacallan 	void *fd;
1454c5904c10Smacallan 
14555bf2d324Smacallan 	wi = font->fontwidth;
14565bf2d324Smacallan 	he = font->fontheight;
1457c5904c10Smacallan 
1458c5904c10Smacallan 	x = ri->ri_xorigin + col * wi;
1459c5904c10Smacallan 	y = ri->ri_yorigin + row * he;
1460c5904c10Smacallan 
14614395e1ddSmacallan 	bg = ri->ri_devcmap[(attr >> 16) & 0xff];
14625bf2d324Smacallan 	fg = ri->ri_devcmap[(attr >> 24) & 0xff];
14635bf2d324Smacallan 	uc = c - font->firstchar;
14645bf2d324Smacallan 	fd = (uint8_t *)font->data + uc * ri->ri_fontscale;
1465c5904c10Smacallan 	if (c == 0x20) {
1466c5904c10Smacallan 		crmfb_fill_rect(sc, x, y, wi, he, bg);
1467c5904c10Smacallan 	} else {
1468a2db4fbdSmacallan 		crmfb_make_room(sc, 6);
14695bf2d324Smacallan 		/* setup */
14705bf2d324Smacallan 		bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_DRAWMODE,
14715bf2d324Smacallan 		    DE_DRAWMODE_PLANEMASK | DE_DRAWMODE_BYTEMASK |
14725bf2d324Smacallan 		    DE_DRAWMODE_ROP |
14735bf2d324Smacallan 		    DE_DRAWMODE_OPAQUE_STIP | DE_DRAWMODE_POLY_STIP);
1474fa88ee42Smacallan 		wbflush();
1475fa88ee42Smacallan 		bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_ROP, 3);
1476fa88ee42Smacallan 		bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_FG, fg);
1477fa88ee42Smacallan 		bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_BG, bg);
14785bf2d324Smacallan 		bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_PRIMITIVE,
14795bf2d324Smacallan 		    DE_PRIM_RECTANGLE | DE_PRIM_LR | DE_PRIM_TB);
14805bf2d324Smacallan 		bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_STIPPLE_MODE,
14815bf2d324Smacallan 		    0x001f0000);
14825bf2d324Smacallan 		/* now let's feed the engine */
1483a2db4fbdSmacallan 		crmfb_make_room(sc, 30);
14845bf2d324Smacallan 		if (font->stride == 1) {
14855bf2d324Smacallan 			/* shovel in 8 bit quantities */
14865bf2d324Smacallan 			fd8 = fd;
14875bf2d324Smacallan 			for (i = 0; i < he; i++) {
1488a2db4fbdSmacallan 				if (i & 8)
1489a2db4fbdSmacallan 					crmfb_make_room(sc, 30);
14905bf2d324Smacallan 				bus_space_write_4(sc->sc_iot, sc->sc_reh,
14915bf2d324Smacallan 				    CRIME_DE_STIPPLE_PAT, *fd8 << 24);
14925bf2d324Smacallan 				bus_space_write_4(sc->sc_iot, sc->sc_reh,
14935bf2d324Smacallan 				    CRIME_DE_X_VERTEX_0, (x << 16) | y);
14945bf2d324Smacallan 				bus_space_write_4(sc->sc_iot, sc->sc_reh,
14955bf2d324Smacallan 				    CRIME_DE_X_VERTEX_1 | CRIME_DE_START,
14965bf2d324Smacallan 				    ((x + wi) << 16) | y);
14975bf2d324Smacallan 				y++;
14985bf2d324Smacallan 				fd8++;
14995bf2d324Smacallan 			}
15005bf2d324Smacallan 		} else if (font->stride == 2) {
15015bf2d324Smacallan 			/* shovel in 16 bit quantities */
15025bf2d324Smacallan 			fd16 = fd;
15035bf2d324Smacallan 			for (i = 0; i < he; i++) {
1504a2db4fbdSmacallan 				if (i & 8)
1505a2db4fbdSmacallan 					crmfb_make_room(sc, 30);
15065bf2d324Smacallan 				bus_space_write_4(sc->sc_iot, sc->sc_reh,
15075bf2d324Smacallan 				    CRIME_DE_STIPPLE_PAT, *fd16 << 16);
15085bf2d324Smacallan 				bus_space_write_4(sc->sc_iot, sc->sc_reh,
15095bf2d324Smacallan 				    CRIME_DE_X_VERTEX_0, (x << 16) | y);
15105bf2d324Smacallan 				bus_space_write_4(sc->sc_iot, sc->sc_reh,
15115bf2d324Smacallan 				    CRIME_DE_X_VERTEX_1 | CRIME_DE_START,
15125bf2d324Smacallan 				    ((x + wi) << 16) | y);
15135bf2d324Smacallan 				y++;
15145bf2d324Smacallan 				fd16++;
15155bf2d324Smacallan 			}
15165bf2d324Smacallan 		}
1517c5904c10Smacallan 	}
1518c5904c10Smacallan }
15199b94525aSmacallan 
15209b94525aSmacallan static void
crmfb_putchar_aa(void * cookie,int row,int col,u_int c,long attr)1521a2db4fbdSmacallan crmfb_putchar_aa(void *cookie, int row, int col, u_int c, long attr)
1522a2db4fbdSmacallan {
1523a2db4fbdSmacallan 	struct rasops_info *ri = cookie;
1524a2db4fbdSmacallan 	struct vcons_screen *scr = ri->ri_hw;
1525a2db4fbdSmacallan 	struct crmfb_softc *sc = scr->scr_cookie;
1526a2db4fbdSmacallan 	struct wsdisplay_font *font = PICK_FONT(ri, c);
1527a2db4fbdSmacallan 	uint32_t bg, fg;
1528a2db4fbdSmacallan 	int x, y, wi, he, uc, xx;
1529a2db4fbdSmacallan 	void *fd;
1530a2db4fbdSmacallan 
1531a2db4fbdSmacallan 	wi = font->fontwidth;
1532a2db4fbdSmacallan 	he = font->fontheight;
1533a2db4fbdSmacallan 
1534a2db4fbdSmacallan 	x = ri->ri_xorigin + col * wi;
1535a2db4fbdSmacallan 	y = ri->ri_yorigin + row * he;
1536a2db4fbdSmacallan 
1537a2db4fbdSmacallan 	bg = ri->ri_devcmap[(attr >> 16) & 0xff];
1538a2db4fbdSmacallan 	fg = (attr >> 24);
1539a2db4fbdSmacallan 	uc = c - font->firstchar;
1540a2db4fbdSmacallan 	fd = (uint8_t *)font->data + uc * ri->ri_fontscale;
1541a2db4fbdSmacallan 
1542a2db4fbdSmacallan 	/* fill the cell with the background colour */
1543a2db4fbdSmacallan 	crmfb_fill_rect(sc, x, y, wi, he, bg);
1544a2db4fbdSmacallan 
1545a2db4fbdSmacallan 	/* if all we draw is a space we're done */
1546a2db4fbdSmacallan 	if (c == 0x20)
1547a2db4fbdSmacallan 		return;
1548a2db4fbdSmacallan 
1549a2db4fbdSmacallan 	/* copy the glyph into the linear buffer */
1550a2db4fbdSmacallan 	memcpy(sc->sc_lptr, fd, ri->ri_fontscale);
1551a2db4fbdSmacallan 	wbflush();
1552a2db4fbdSmacallan 
1553a2db4fbdSmacallan 	/* now blit it on top of the requested fg colour cell */
1554a2db4fbdSmacallan 	xx = fg * wi;
1555a2db4fbdSmacallan 	crmfb_make_room(sc, 2);
1556a2db4fbdSmacallan 	crmfb_src_mode(sc,
1557a2db4fbdSmacallan 	    DE_MODE_LIN_A |
1558a2db4fbdSmacallan 	    DE_MODE_BUFDEPTH_8 |
1559a2db4fbdSmacallan 	    DE_MODE_TYPE_CI |
1560a2db4fbdSmacallan 	    DE_MODE_PIXDEPTH_8);
1561a2db4fbdSmacallan 	crmfb_dst_mode(sc,
1562a2db4fbdSmacallan 	    DE_MODE_TLB_A |
1563a2db4fbdSmacallan 	    DE_MODE_BUFDEPTH_32 |
1564a2db4fbdSmacallan 	    DE_MODE_TYPE_CI |
1565a2db4fbdSmacallan 	    DE_MODE_PIXDEPTH_8);
1566a2db4fbdSmacallan 
1567a2db4fbdSmacallan 	crmfb_make_room(sc, 6);
1568a2db4fbdSmacallan 	/* only write into the alpha channel */
1569a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_DRAWMODE,
1570a2db4fbdSmacallan 	    DE_DRAWMODE_PLANEMASK | 0x08 |
1571a2db4fbdSmacallan 	    DE_DRAWMODE_XFER_EN);
1572a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_PRIMITIVE,
1573a2db4fbdSmacallan 	    DE_PRIM_RECTANGLE | DE_PRIM_TB);
1574a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_XFER_STRD_SRC, 1);
1575a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_XFER_ADDR_SRC, 0);
1576a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_X_VERTEX_0,
1577a2db4fbdSmacallan 	    (xx << 16) | (sc->sc_height & 0xffff));
1578a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh,
1579a2db4fbdSmacallan 	    CRIME_DE_X_VERTEX_1 | CRIME_DE_START,
1580a2db4fbdSmacallan 	    ((xx + wi - 1) << 16) | ((sc->sc_height + he - 1) & 0xffff));
1581a2db4fbdSmacallan 
1582a2db4fbdSmacallan 	/* now draw the actual character */
1583a2db4fbdSmacallan 	crmfb_make_room(sc, 2);
1584a2db4fbdSmacallan 	crmfb_src_mode(sc,
1585a2db4fbdSmacallan 	    DE_MODE_TLB_A |
1586a2db4fbdSmacallan 	    DE_MODE_BUFDEPTH_32 |
1587a2db4fbdSmacallan 	    DE_MODE_TYPE_RGBA |
1588a2db4fbdSmacallan 	    DE_MODE_PIXDEPTH_32);
1589a2db4fbdSmacallan 	crmfb_dst_mode(sc, sc->sc_de_mode);
1590a2db4fbdSmacallan 
1591a2db4fbdSmacallan 	crmfb_make_room(sc, 6);
1592a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_DRAWMODE,
1593a2db4fbdSmacallan 	    DE_DRAWMODE_PLANEMASK | DE_DRAWMODE_BYTEMASK |
1594a2db4fbdSmacallan 	    DE_DRAWMODE_ALPHA_BLEND |
1595a2db4fbdSmacallan 	    DE_DRAWMODE_XFER_EN);
1596a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_ALPHA_FUNC,
1597a2db4fbdSmacallan 	    DE_ALPHA_ADD |
1598a2db4fbdSmacallan 	    (DE_ALPHA_OP_SRC_ALPHA << DE_ALPHA_OP_SRC_SHIFT) |
1599a2db4fbdSmacallan 	    (DE_ALPHA_OP_1_MINUS_SRC_ALPHA << DE_ALPHA_OP_DST_SHIFT));
1600a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_PRIMITIVE,
1601a2db4fbdSmacallan 	    DE_PRIM_RECTANGLE | DE_PRIM_TB);
1602a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_XFER_ADDR_SRC,
1603a2db4fbdSmacallan 	    (xx << 16) | (sc->sc_height & 0xffff));
1604a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh, CRIME_DE_X_VERTEX_0,
1605a2db4fbdSmacallan 	    (x << 16) | (y & 0xffff));
1606a2db4fbdSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_reh,
1607a2db4fbdSmacallan 	    CRIME_DE_X_VERTEX_1 | CRIME_DE_START,
1608a2db4fbdSmacallan 	    ((x + wi - 1) << 16) | ((y + he - 1) & 0xffff));
1609a2db4fbdSmacallan }
1610a2db4fbdSmacallan 
1611a2db4fbdSmacallan static void
crmfb_setup_ddc(struct crmfb_softc * sc)16129b94525aSmacallan crmfb_setup_ddc(struct crmfb_softc *sc)
16139b94525aSmacallan {
16149b94525aSmacallan 	int i;
16159b94525aSmacallan 
161671305eb6Smacallan 	memset(sc->sc_edid_data, 0, 128);
1617601e1783Sthorpej 	iic_tag_init(&sc->sc_i2c);
16189b94525aSmacallan 	sc->sc_i2c.ic_cookie = sc;
16199b94525aSmacallan 	sc->sc_i2c.ic_send_start = crmfb_i2c_send_start;
16209b94525aSmacallan 	sc->sc_i2c.ic_send_stop = crmfb_i2c_send_stop;
16219b94525aSmacallan 	sc->sc_i2c.ic_initiate_xfer = crmfb_i2c_initiate_xfer;
16229b94525aSmacallan 	sc->sc_i2c.ic_read_byte = crmfb_i2c_read_byte;
16239b94525aSmacallan 	sc->sc_i2c.ic_write_byte = crmfb_i2c_write_byte;
16249b94525aSmacallan 	i = 0;
162571305eb6Smacallan 	while (sc->sc_edid_data[1] == 0 && i++ < 10)
162671305eb6Smacallan 		ddc_read_edid(&sc->sc_i2c, sc->sc_edid_data, 128);
16279b94525aSmacallan 	if (i > 1)
16289b94525aSmacallan 		aprint_debug_dev(sc->sc_dev,
16299b94525aSmacallan 		    "had to try %d times to get EDID data\n", i);
16309b94525aSmacallan 	if (i < 11) {
163171305eb6Smacallan 		edid_parse(sc->sc_edid_data, &sc->sc_edid_info);
1632713b73c8Smacallan 		edid_print(&sc->sc_edid_info);
16339b94525aSmacallan 	}
16349b94525aSmacallan }
16359b94525aSmacallan 
16369b94525aSmacallan /* I2C bitbanging */
16379b94525aSmacallan static void
crmfb_i2cbb_set_bits(void * cookie,uint32_t bits)16389b94525aSmacallan crmfb_i2cbb_set_bits(void *cookie, uint32_t bits)
16399b94525aSmacallan {
16409b94525aSmacallan 	struct crmfb_softc *sc = cookie;
16419b94525aSmacallan 
16429b94525aSmacallan 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, CRMFB_I2C_VGA, bits ^ 3);
16439b94525aSmacallan }
16449b94525aSmacallan 
16459b94525aSmacallan static void
crmfb_i2cbb_set_dir(void * cookie,uint32_t dir)16469b94525aSmacallan crmfb_i2cbb_set_dir(void *cookie, uint32_t dir)
16479b94525aSmacallan {
16489b94525aSmacallan 
16499b94525aSmacallan 	/* Nothing to do */
16509b94525aSmacallan }
16519b94525aSmacallan 
16529b94525aSmacallan static uint32_t
crmfb_i2cbb_read(void * cookie)16539b94525aSmacallan crmfb_i2cbb_read(void *cookie)
16549b94525aSmacallan {
16559b94525aSmacallan 	struct crmfb_softc *sc = cookie;
16569b94525aSmacallan 
16579b94525aSmacallan 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_I2C_VGA) ^ 3;
16589b94525aSmacallan }
16599b94525aSmacallan 
16609b94525aSmacallan static int
crmfb_i2c_send_start(void * cookie,int flags)16619b94525aSmacallan crmfb_i2c_send_start(void *cookie, int flags)
16629b94525aSmacallan {
16639b94525aSmacallan 
16649b94525aSmacallan 	return i2c_bitbang_send_start(cookie, flags, &crmfb_i2cbb_ops);
16659b94525aSmacallan }
16669b94525aSmacallan 
16679b94525aSmacallan static int
crmfb_i2c_send_stop(void * cookie,int flags)16689b94525aSmacallan crmfb_i2c_send_stop(void *cookie, int flags)
16699b94525aSmacallan {
16709b94525aSmacallan 
16719b94525aSmacallan 	return i2c_bitbang_send_stop(cookie, flags, &crmfb_i2cbb_ops);
16729b94525aSmacallan }
16739b94525aSmacallan 
16749b94525aSmacallan static int
crmfb_i2c_initiate_xfer(void * cookie,i2c_addr_t addr,int flags)16759b94525aSmacallan crmfb_i2c_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
16769b94525aSmacallan {
16779b94525aSmacallan 
16789b94525aSmacallan 	return i2c_bitbang_initiate_xfer(cookie, addr, flags,
16799b94525aSmacallan 	    &crmfb_i2cbb_ops);
16809b94525aSmacallan }
16819b94525aSmacallan 
16829b94525aSmacallan static int
crmfb_i2c_read_byte(void * cookie,uint8_t * valp,int flags)16839b94525aSmacallan crmfb_i2c_read_byte(void *cookie, uint8_t *valp, int flags)
16849b94525aSmacallan {
16859b94525aSmacallan 
16869b94525aSmacallan 	return i2c_bitbang_read_byte(cookie, valp, flags, &crmfb_i2cbb_ops);
16879b94525aSmacallan }
16889b94525aSmacallan 
16899b94525aSmacallan static int
crmfb_i2c_write_byte(void * cookie,uint8_t val,int flags)16909b94525aSmacallan crmfb_i2c_write_byte(void *cookie, uint8_t val, int flags)
16919b94525aSmacallan {
16929b94525aSmacallan 
16939b94525aSmacallan 	return i2c_bitbang_write_byte(cookie, val, flags, &crmfb_i2cbb_ops);
16949b94525aSmacallan }
1695713b73c8Smacallan 
1696713b73c8Smacallan /* mode setting stuff */
1697713b73c8Smacallan static uint32_t
calc_pll(int f_out)1698713b73c8Smacallan calc_pll(int f_out)
1699713b73c8Smacallan {
1700713b73c8Smacallan 	uint32_t ret;
1701713b73c8Smacallan 	int f_in = 20000;	/* 20MHz in */
1702713b73c8Smacallan 	int M, N, P;
1703713b73c8Smacallan 	int error, div, best = 9999999;
1704713b73c8Smacallan 	int ff1, ff2;
1705713b73c8Smacallan 	int MM = 0, NN = 0, PP = 0, ff = 0;
1706713b73c8Smacallan 
1707713b73c8Smacallan 	/* f_out = M * f_in / (N * (1 << P) */
1708713b73c8Smacallan 
1709713b73c8Smacallan 	for (P = 0; P < 4; P++) {
1710713b73c8Smacallan 		for (N = 64; N > 0; N--) {
1711713b73c8Smacallan 			div = N * (1 << P);
1712713b73c8Smacallan 			M = f_out * div / f_in;
1713713b73c8Smacallan 			if ((M < 257) && (M > 100)) {
1714713b73c8Smacallan 				ff1 = M * f_in / div;
1715713b73c8Smacallan 				ff2 = (M + 1) * f_in / div;
1716713b73c8Smacallan 				error = abs(ff1 - f_out);
1717713b73c8Smacallan 				if (error < best) {
1718713b73c8Smacallan 					MM = M;
1719713b73c8Smacallan 					NN = N;
1720713b73c8Smacallan 					PP = P;
1721713b73c8Smacallan 					ff = ff1;
1722713b73c8Smacallan 					best = error;
1723713b73c8Smacallan 				}
1724713b73c8Smacallan 				error = abs(ff2 - f_out);
1725713b73c8Smacallan 				if ((error < best) && ( M < 256)){
1726713b73c8Smacallan 					MM = M + 1;
1727713b73c8Smacallan 					NN = N;
1728713b73c8Smacallan 					PP = P;
1729713b73c8Smacallan 					ff = ff2;
1730713b73c8Smacallan 					best = error;
1731713b73c8Smacallan 				}
1732713b73c8Smacallan 			}
1733713b73c8Smacallan 		}
1734713b73c8Smacallan 	}
1735713b73c8Smacallan 	DPRINTF("%d: M %d N %d P %d -> %d\n", f_out, MM, NN, PP, ff);
1736713b73c8Smacallan 	/* now shove the parameters into the register's format */
1737713b73c8Smacallan 	ret = (MM - 1) | ((NN - 1) << 8) | (P << 14);
1738713b73c8Smacallan 	return ret;
1739713b73c8Smacallan }
1740713b73c8Smacallan 
1741713b73c8Smacallan static int
crmfb_set_mode(struct crmfb_softc * sc,const struct videomode * mode)1742713b73c8Smacallan crmfb_set_mode(struct crmfb_softc *sc, const struct videomode *mode)
1743713b73c8Smacallan {
1744713b73c8Smacallan 	uint32_t d, dc;
1745713b73c8Smacallan 	int tmp, diff;
1746713b73c8Smacallan 
1747fa88ee42Smacallan 	switch (mode->hdisplay % 32) {
1748fa88ee42Smacallan 		case 0:
1749fa88ee42Smacallan 			sc->sc_console_depth = 8;
1750fa88ee42Smacallan 			break;
1751fa88ee42Smacallan 		case 16:
1752fa88ee42Smacallan 			sc->sc_console_depth = 16;
1753fa88ee42Smacallan 			break;
1754fa88ee42Smacallan 		case 8:
1755fa88ee42Smacallan 		case 24:
1756fa88ee42Smacallan 			sc->sc_console_depth = 32;
1757fa88ee42Smacallan 			break;
1758fa88ee42Smacallan 		default:
1759fa88ee42Smacallan 			aprint_error_dev(sc->sc_dev,
1760fa88ee42Smacallan 			    "hdisplay (%d) is not a multiple of 32\n",
1761fa88ee42Smacallan 			    mode->hdisplay);
1762713b73c8Smacallan 			return FALSE;
1763713b73c8Smacallan 	}
1764fa88ee42Smacallan 	if (mode->dot_clock > 150000) {
1765fa88ee42Smacallan 		aprint_error_dev(sc->sc_dev,
1766fa88ee42Smacallan 		    "requested dot clock is too high ( %d MHz )\n",
1767fa88ee42Smacallan 		    mode->dot_clock / 1000);
1768713b73c8Smacallan 		return FALSE;
1769713b73c8Smacallan 	}
1770713b73c8Smacallan 
1771713b73c8Smacallan 	/* disable DMA */
1772713b73c8Smacallan 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_OVR_CONTROL);
1773713b73c8Smacallan 	d &= ~(1 << CRMFB_OVR_CONTROL_DMAEN_SHIFT);
1774713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_OVR_CONTROL, d);
1775713b73c8Smacallan 	DELAY(50000);
1776713b73c8Smacallan 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_CONTROL);
1777713b73c8Smacallan 	d &= ~(1 << CRMFB_FRM_CONTROL_DMAEN_SHIFT);
1778713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_FRM_CONTROL, d);
1779713b73c8Smacallan 	DELAY(50000);
1780713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_DID_CONTROL, d);
1781713b73c8Smacallan 	DELAY(50000);
1782713b73c8Smacallan 
1783713b73c8Smacallan 	if (!crmfb_wait_dma_idle(sc))
1784713b73c8Smacallan 		aprint_error("crmfb: crmfb_wait_dma_idle timed out\n");
1785713b73c8Smacallan 
1786713b73c8Smacallan 	/* ok, now we're good to go */
1787713b73c8Smacallan 	dc = calc_pll(mode->dot_clock);
1788713b73c8Smacallan 
1789713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_XY, 1 << CRMFB_VT_XY_FREEZE_SHIFT);
1790713b73c8Smacallan 	delay(1000);
1791713b73c8Smacallan 
1792713b73c8Smacallan 	/* set the dot clock pll but don't start it yet */
1793713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_DOTCLOCK, dc);
1794713b73c8Smacallan 	delay(10000);
1795713b73c8Smacallan 
1796713b73c8Smacallan 	/* pixel counter */
1797713b73c8Smacallan 	d = mode->htotal | (mode->vtotal << 12);
1798713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_XYMAX, d);
1799713b73c8Smacallan 
1800713b73c8Smacallan 	/* video timings */
1801713b73c8Smacallan 	d = mode->vsync_end | (mode->vsync_start << 12);
1802713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_VSYNC, d);
1803713b73c8Smacallan 
1804713b73c8Smacallan 	d = mode->hsync_end | (mode->hsync_start << 12);
1805713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_HSYNC, d);
1806713b73c8Smacallan 
1807713b73c8Smacallan 	d = mode->vtotal | (mode->vdisplay << 12);
1808713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_VBLANK, d);
1809713b73c8Smacallan 
1810713b73c8Smacallan 	d = (mode->htotal - 5) | ((mode->hdisplay - 5) << 12);
1811713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_HBLANK, d);
1812713b73c8Smacallan 
1813713b73c8Smacallan 	d = mode->vtotal | (mode->vdisplay << 12);
1814713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_VCMAP, d);
1815713b73c8Smacallan 	d = mode->htotal | (mode->hdisplay << 12);
1816713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_HCMAP, d);
1817713b73c8Smacallan 
1818713b73c8Smacallan 	d = 0;
1819713b73c8Smacallan 	if (mode->flags & VID_NHSYNC) d |= CRMFB_VT_FLAGS_HDRV_INVERT;
1820713b73c8Smacallan 	if (mode->flags & VID_NVSYNC) d |= CRMFB_VT_FLAGS_VDRV_INVERT;
1821713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_FLAGS, d);
182271305eb6Smacallan 	sc->sc_vtflags = d;
1823713b73c8Smacallan 
1824713b73c8Smacallan 	diff = -abs(mode->vtotal - mode->vdisplay - 1);
1825713b73c8Smacallan 	d = ((uint32_t)diff << 12) & 0x00fff000;
1826713b73c8Smacallan 	d |= (mode->htotal - 20);
1827713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_DID_STARTXY, d);
1828713b73c8Smacallan 
1829713b73c8Smacallan 	d = ((uint32_t)(diff + 1) << 12) & 0x00fff000;
1830713b73c8Smacallan 	d |= (mode->htotal - 54);
1831713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_CRS_STARTXY, d);
1832713b73c8Smacallan 
1833713b73c8Smacallan 	d = ((uint32_t)diff << 12) & 0x00fff000;
1834713b73c8Smacallan 	d |= (mode->htotal - 4);
1835713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_VC_STARTXY, d);
1836713b73c8Smacallan 
1837713b73c8Smacallan 	tmp = mode->htotal - 19;
1838713b73c8Smacallan 	d = tmp << 12;
1839713b73c8Smacallan 	d |= ((tmp + mode->hdisplay - 2) % mode->htotal);
1840713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_HPIX_EN, d);
1841713b73c8Smacallan 
1842713b73c8Smacallan 	d = mode->vdisplay | (mode->vtotal << 12);
1843713b73c8Smacallan 	crmfb_write_reg(sc, CRMFB_VT_VPIX_EN, d);
1844713b73c8Smacallan 
1845713b73c8Smacallan 	sc->sc_width = mode->hdisplay;
1846713b73c8Smacallan 	sc->sc_height = mode->vdisplay;
1847713b73c8Smacallan 
1848713b73c8Smacallan 	return TRUE;
1849713b73c8Smacallan }
185095923c6fSjmcneill 
185195923c6fSjmcneill /*
185295923c6fSjmcneill  * Parse a mode string in the form WIDTHxHEIGHT[@REFRESH] and return
185395923c6fSjmcneill  * monitor timings generated using the VESA GTF formula.
185495923c6fSjmcneill  */
185595923c6fSjmcneill static int
crmfb_parse_mode(const char * modestr,struct videomode * mode)185695923c6fSjmcneill crmfb_parse_mode(const char *modestr, struct videomode *mode)
185795923c6fSjmcneill {
185895923c6fSjmcneill 	char *x, *at;
185995923c6fSjmcneill 	int width, height, refresh;
186095923c6fSjmcneill 
186195923c6fSjmcneill 	if (modestr == NULL)
186295923c6fSjmcneill 		return EINVAL;
186395923c6fSjmcneill 
186495923c6fSjmcneill 	x = strchr(modestr, 'x');
186595923c6fSjmcneill 	at = strchr(modestr, '@');
186695923c6fSjmcneill 
186795923c6fSjmcneill 	if (x == NULL || (at != NULL && at < x))
186895923c6fSjmcneill 		return EINVAL;
186995923c6fSjmcneill 
187095923c6fSjmcneill 	width = strtoul(modestr, NULL, 10);
187195923c6fSjmcneill 	height = strtoul(x + 1, NULL, 10);
187295923c6fSjmcneill 	refresh = at ? strtoul(at + 1, NULL, 10) : 60;
187395923c6fSjmcneill 
187495923c6fSjmcneill 	if (width == 0 || height == 0 || refresh == 0)
187595923c6fSjmcneill 		return EINVAL;
187695923c6fSjmcneill 
187795923c6fSjmcneill 	vesagtf_mode(width, height, refresh, mode);
187895923c6fSjmcneill 
187995923c6fSjmcneill 	return 0;
188095923c6fSjmcneill }
1881