xref: /netbsd-src/sys/arch/sgimips/dev/crmfb.c (revision 0df165c04d0a9ca1adde9ed2b890344c937954a6)
1 /* $NetBSD: crmfb.c,v 1.8 2007/10/17 19:57:03 garbled Exp $ */
2 
3 /*-
4  * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *        This product includes software developed by Jared D. McNeill.
18  * 4. Neither the name of The NetBSD Foundation nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 /*
36  * SGI-CRM (O2) Framebuffer driver
37  */
38 
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: crmfb.c,v 1.8 2007/10/17 19:57:03 garbled Exp $");
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/device.h>
45 #include <sys/malloc.h>
46 
47 #define _SGIMIPS_BUS_DMA_PRIVATE
48 #include <machine/autoconf.h>
49 #include <machine/bus.h>
50 #include <machine/machtype.h>
51 #include <machine/vmparam.h>
52 
53 #include <dev/arcbios/arcbios.h>
54 #include <dev/arcbios/arcbiosvar.h>
55 
56 #include <dev/wscons/wsdisplayvar.h>
57 #include <dev/wscons/wsconsio.h>
58 #include <dev/wsfont/wsfont.h>
59 #include <dev/rasops/rasops.h>
60 #include <dev/wscons/wsdisplay_vconsvar.h>
61 
62 #include <arch/sgimips/dev/crmfbreg.h>
63 
64 #define CRMFB_SHADOWFB
65 
66 struct wsscreen_descr crmfb_defaultscreen = {
67 	"default",
68 	0, 0,
69 	NULL,
70 	8, 16,
71 	WSSCREEN_WSCOLORS,
72 	NULL,
73 };
74 
75 const struct wsscreen_descr *_crmfb_scrlist[] = {
76 	&crmfb_defaultscreen,
77 };
78 
79 struct wsscreen_list crmfb_screenlist = {
80 	sizeof(_crmfb_scrlist) / sizeof(struct wsscreen_descr *),
81 	_crmfb_scrlist
82 };
83 
84 static struct vcons_screen crmfb_console_screen;
85 
86 static int	crmfb_ioctl(void *, void *, u_long, void *, int, struct lwp *);
87 static paddr_t	crmfb_mmap(void *, void *, off_t, int);
88 static void	crmfb_init_screen(void *, struct vcons_screen *, int, long *);
89 
90 struct wsdisplay_accessops crmfb_accessops = {
91 	crmfb_ioctl,
92 	crmfb_mmap,
93 	NULL,	/* alloc_screen */
94 	NULL,	/* free_screen */
95 	NULL,	/* show_screen */
96 	NULL,	/* load_font */
97 	NULL,	/* pollc */
98 	NULL,	/* scroll */
99 };
100 
101 /* Memory to allocate to SGI-CRM -- remember, this is stolen from
102  * host memory!
103  */
104 #define CRMFB_TILESIZE	(512*128)
105 
106 static int	crmfb_match(struct device *, struct cfdata *, void *);
107 static void	crmfb_attach(struct device *, struct device *, void *);
108 int		crmfb_probe(void);
109 
110 #define KERNADDR(p)	((void *)((p).addr))
111 #define DMAADDR(p)	((p).map->dm_segs[0].ds_addr)
112 
113 struct crmfb_dma {
114 	bus_dmamap_t		map;
115 	void			*addr;
116 	bus_dma_segment_t	segs[1];
117 	int			nsegs;
118 	size_t			size;
119 };
120 
121 struct crmfb_softc {
122 	struct device		sc_dev;
123 	struct vcons_data	sc_vd;
124 
125 	bus_space_tag_t		sc_iot;
126 	bus_space_handle_t	sc_ioh;
127 
128 	bus_dma_tag_t		sc_dmat;
129 
130 	struct crmfb_dma	sc_dma;
131 	struct crmfb_dma	sc_dmai;
132 
133 	int			sc_width;
134 	int			sc_height;
135 	int			sc_depth;
136 	uint32_t		sc_fbsize;
137 	uint8_t			*sc_shadowfb;
138 
139 	int			sc_wsmode;
140 
141 	/* cursor stuff */
142 	int			sc_cur_x;
143 	int			sc_cur_y;
144 	int			sc_hot_x;
145 	int			sc_hot_y;
146 
147 	u_char			sc_cmap_red[256];
148 	u_char			sc_cmap_green[256];
149 	u_char			sc_cmap_blue[256];
150 };
151 
152 static int	crmfb_putcmap(struct crmfb_softc *, struct wsdisplay_cmap *);
153 static int	crmfb_getcmap(struct crmfb_softc *, struct wsdisplay_cmap *);
154 static void	crmfb_set_palette(struct crmfb_softc *,
155 				  int, uint8_t, uint8_t, uint8_t);
156 static int	crmfb_set_curpos(struct crmfb_softc *, int, int);
157 static int	crmfb_gcursor(struct crmfb_softc *, struct wsdisplay_cursor *);
158 static int	crmfb_scursor(struct crmfb_softc *, struct wsdisplay_cursor *);
159 static inline void	crmfb_write_reg(struct crmfb_softc *, int, uint32_t);
160 
161 /* setup video hw in given colour depth */
162 static int	crmfb_setup_video(struct crmfb_softc *, int);
163 static void	crmfb_setup_palette(struct crmfb_softc *);
164 
165 CFATTACH_DECL(crmfb, sizeof(struct crmfb_softc),
166     crmfb_match, crmfb_attach, NULL, NULL);
167 
168 static int
169 crmfb_match(struct device *parent, struct cfdata *cf, void *opaque)
170 {
171 	return crmfb_probe();
172 }
173 
174 static void
175 crmfb_attach(struct device *parent, struct device *self, void *opaque)
176 {
177 	struct mainbus_attach_args *ma;
178 	struct crmfb_softc *sc;
179 	struct rasops_info *ri;
180 	struct wsemuldisplaydev_attach_args aa;
181 	uint32_t d, h;
182 	uint16_t *p;
183 	unsigned long v;
184 	long defattr;
185 	const char *consdev;
186 	int rv, i, tiles_x, tiles_y;
187 
188 	sc = (struct crmfb_softc *)self;
189 	ma = (struct mainbus_attach_args *)opaque;
190 
191 	sc->sc_iot = SGIMIPS_BUS_SPACE_CRIME;
192 	sc->sc_dmat = &sgimips_default_bus_dma_tag;
193 	sc->sc_wsmode = WSDISPLAYIO_MODE_EMUL;
194 
195 	aprint_normal(": SGI CRIME Graphics Display Engine\n");
196 	rv = bus_space_map(sc->sc_iot, ma->ma_addr, 0 /* XXX */,
197 	    BUS_SPACE_MAP_LINEAR, &sc->sc_ioh);
198 	if (rv)
199 		panic("crmfb_attach: can't map I/O space");
200 
201 	/* determine mode configured by firmware */
202 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_VT_VCMAP);
203 	sc->sc_width = (d >> CRMFB_VT_VCMAP_ON_SHIFT) & 0xfff;
204 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_VT_HCMAP);
205 	sc->sc_height = (d >> CRMFB_VT_HCMAP_ON_SHIFT) & 0xfff;
206 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_TILESIZE);
207 	h = (d >> CRMFB_FRM_TILESIZE_DEPTH_SHIFT) & 0x3;
208 	if (h == 0)
209 		sc->sc_depth = 8;
210 	else if (h == 1)
211 		sc->sc_depth = 16;
212 	else
213 		sc->sc_depth = 32;
214 
215 	if (sc->sc_width == 0 || sc->sc_height == 0) {
216 		printf("%s: device unusable if not setup by firmware\n",
217 		    sc->sc_dev.dv_xname);
218 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, 0 /* XXX */);
219 		return;
220 	}
221 
222 	printf("%s: initial resolution %dx%d\n",
223 	    sc->sc_dev.dv_xname, sc->sc_width, sc->sc_height);
224 
225 	/*
226 	 * first determine how many tiles we need
227 	 * in 32bit each tile is 128x128 pixels
228 	 */
229 	tiles_x = (sc->sc_width + 127) >> 7;
230 	tiles_y = (sc->sc_height + 127) >> 7;
231 	sc->sc_fbsize = 0x10000 * tiles_x * tiles_y;
232 	printf("so we need %d x %d tiles -> %08x\n", tiles_x, tiles_y,
233 	    sc->sc_fbsize);
234 
235 	sc->sc_dmai.size = 256 * sizeof(uint16_t);
236 	rv = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmai.size, 65536, 0,
237 	    sc->sc_dmai.segs,
238 	    sizeof(sc->sc_dmai.segs) / sizeof(sc->sc_dmai.segs[0]),
239 	    &sc->sc_dmai.nsegs, BUS_DMA_NOWAIT);
240 	if (rv)
241 		panic("crmfb_attach: can't allocate DMA memory");
242 	rv = bus_dmamem_map(sc->sc_dmat, sc->sc_dmai.segs, sc->sc_dmai.nsegs,
243 	    sc->sc_dmai.size, &sc->sc_dmai.addr,
244 	    BUS_DMA_NOWAIT);
245 	if (rv)
246 		panic("crmfb_attach: can't map DMA memory");
247 	rv = bus_dmamap_create(sc->sc_dmat, sc->sc_dmai.size, 1,
248 	    sc->sc_dmai.size, 0, BUS_DMA_NOWAIT, &sc->sc_dmai.map);
249 	if (rv)
250 		panic("crmfb_attach: can't create DMA map");
251 	rv = bus_dmamap_load(sc->sc_dmat, sc->sc_dmai.map, sc->sc_dmai.addr,
252 	    sc->sc_dmai.size, NULL, BUS_DMA_NOWAIT);
253 	if (rv)
254 		panic("crmfb_attach: can't load DMA map");
255 
256 	sc->sc_dma.size = sc->sc_fbsize;
257 	rv = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dma.size, 65536, 0,
258 	    sc->sc_dma.segs,
259 	    sizeof(sc->sc_dma.segs) / sizeof(sc->sc_dma.segs[0]),
260 	    &sc->sc_dma.nsegs, BUS_DMA_NOWAIT);
261 	if (rv)
262 		panic("crmfb_attach: can't allocate DMA memory");
263 	rv = bus_dmamem_map(sc->sc_dmat, sc->sc_dma.segs, sc->sc_dma.nsegs,
264 	    sc->sc_dma.size, &sc->sc_dma.addr,
265 	    BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
266 	if (rv)
267 		panic("crmfb_attach: can't map DMA memory");
268 	rv = bus_dmamap_create(sc->sc_dmat, sc->sc_dma.size, 1,
269 	    sc->sc_dma.size, 0, BUS_DMA_NOWAIT, &sc->sc_dma.map);
270 	if (rv)
271 		panic("crmfb_attach: can't create DMA map");
272 	rv = bus_dmamap_load(sc->sc_dmat, sc->sc_dma.map, sc->sc_dma.addr,
273 	    sc->sc_dma.size, NULL, BUS_DMA_NOWAIT);
274 	if (rv)
275 		panic("crmfb_attach: can't load DMA map");
276 
277 	p = KERNADDR(sc->sc_dmai);
278 	v = (unsigned long)DMAADDR(sc->sc_dma);
279 	for (i = 0; i < (sc->sc_fbsize >> 16); i++) {
280 		p[i] = ((uint32_t)v >> 16) + i;
281 	}
282 
283 	memset(KERNADDR(sc->sc_dma), 0x00, sc->sc_fbsize);
284 
285 	printf("%s: allocated %d byte fb @ %p (%p)\n", sc->sc_dev.dv_xname,
286 	    sc->sc_fbsize, KERNADDR(sc->sc_dmai), KERNADDR(sc->sc_dma));
287 
288 #ifdef CRMFB_SHADOWFB
289 	/* setup shadow framebuffer */
290 	sc->sc_shadowfb = malloc(sc->sc_fbsize, M_DEVBUF, M_NOWAIT | M_ZERO);
291 	if (sc->sc_shadowfb == NULL)
292 		aprint_error("%s: WARNING: couldn't allocate shadow fb\n",
293 		    sc->sc_dev.dv_xname);
294 #endif
295 
296 	ri = &crmfb_console_screen.scr_ri;
297 	memset(ri, 0, sizeof(struct rasops_info));
298 
299 	vcons_init(&sc->sc_vd, sc, &crmfb_defaultscreen, &crmfb_accessops);
300 	sc->sc_vd.init_screen = crmfb_init_screen;
301 	crmfb_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
302 	vcons_init_screen(&sc->sc_vd, &crmfb_console_screen, 1, &defattr);
303 
304 	crmfb_defaultscreen.ncols = ri->ri_cols;
305 	crmfb_defaultscreen.nrows = ri->ri_rows;
306 	crmfb_defaultscreen.textops = &ri->ri_ops;
307 	crmfb_defaultscreen.capabilities = ri->ri_caps;
308 	crmfb_defaultscreen.modecookie = NULL;
309 
310 	crmfb_setup_video(sc, 8);
311 	crmfb_setup_palette(sc);
312 
313 	consdev = ARCBIOS->GetEnvironmentVariable("ConsoleOut");
314 	if (consdev != NULL && strcmp(consdev, "video()") == 0) {
315 		wsdisplay_cnattach(&crmfb_defaultscreen, ri, 0, 0, defattr);
316 		aa.console = 1;
317 	} else
318 		aa.console = 0;
319 	aa.scrdata = &crmfb_screenlist;
320 	aa.accessops = &crmfb_accessops;
321 	aa.accesscookie = &sc->sc_vd;
322 
323 	config_found(self, &aa, wsemuldisplaydevprint);
324 
325 	sc->sc_cur_x = 0;
326 	sc->sc_cur_y = 0;
327 	sc->sc_hot_x = 0;
328 	sc->sc_hot_y = 0;
329 
330 	return;
331 }
332 
333 int
334 crmfb_probe(void)
335 {
336 
337         if (mach_type != MACH_SGI_IP32)
338                 return 0;
339 
340 	return 1;
341 }
342 
343 static int
344 crmfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
345 {
346 	struct vcons_data *vd;
347 	struct crmfb_softc *sc;
348 	struct vcons_screen *ms;
349 	struct wsdisplay_fbinfo *wdf;
350 	int nmode;
351 
352 	vd = (struct vcons_data *)v;
353 	sc = (struct crmfb_softc *)vd->cookie;
354 	ms = (struct vcons_screen *)vd->active;
355 
356 	switch (cmd) {
357 	case WSDISPLAYIO_GTYPE:
358 		/* not really, but who cares? */
359 		/* wsfb does */
360 		*(u_int *)data = WSDISPLAY_TYPE_CRIME;
361 		return 0;
362 	case WSDISPLAYIO_GINFO:
363 		if (vd->active != NULL) {
364 			wdf = (void *)data;
365 			wdf->height = sc->sc_height;
366 			wdf->width = sc->sc_width;
367 			wdf->depth = 32;
368 			wdf->cmsize = 256;
369 			return 0;
370 		} else
371 			return ENODEV;
372 	case WSDISPLAYIO_GETCMAP:
373 		if (sc->sc_depth == 8)
374 			return crmfb_getcmap(sc, (struct wsdisplay_cmap *)data);
375 		else
376 			return EINVAL;
377 	case WSDISPLAYIO_PUTCMAP:
378 		if (sc->sc_depth == 8)
379 			return crmfb_putcmap(sc, (struct wsdisplay_cmap *)data);
380 		else
381 			return EINVAL;
382 	case WSDISPLAYIO_LINEBYTES:
383 		*(u_int *)data = sc->sc_width * sc->sc_depth / 8;
384 		return 0;
385 	case WSDISPLAYIO_SMODE:
386 		nmode = *(int *)data;
387 		if (nmode != sc->sc_wsmode) {
388 			sc->sc_wsmode = nmode;
389 			if (nmode == WSDISPLAYIO_MODE_EMUL) {
390 				crmfb_setup_video(sc, 8);
391 				crmfb_setup_palette(sc);
392 				vcons_redraw_screen(vd->active);
393 			} else {
394 				crmfb_setup_video(sc, 32);
395 			}
396 		}
397 		return 0;
398 	case WSDISPLAYIO_SVIDEO:
399 	case WSDISPLAYIO_GVIDEO:
400 		return ENODEV;	/* not supported yet */
401 
402 	case WSDISPLAYIO_GCURPOS:
403 		{
404 			struct wsdisplay_curpos *pos;
405 
406 			pos = (struct wsdisplay_curpos *)data;
407 			pos->x = sc->sc_cur_x;
408 			pos->y = sc->sc_cur_y;
409 		}
410 		return 0;
411 	case WSDISPLAYIO_SCURPOS:
412 		{
413 			struct wsdisplay_curpos *pos;
414 
415 			pos = (struct wsdisplay_curpos *)data;
416 			crmfb_set_curpos(sc, pos->x, pos->y);
417 		}
418 		return 0;
419 	case WSDISPLAYIO_GCURMAX:
420 		{
421 			struct wsdisplay_curpos *pos;
422 
423 			pos = (struct wsdisplay_curpos *)data;
424 			pos->x = 32;
425 			pos->y = 32;
426 		}
427 		return 0;
428 	case WSDISPLAYIO_GCURSOR:
429 		{
430 			struct wsdisplay_cursor *cu;
431 
432 			cu = (struct wsdisplay_cursor *)data;
433 			return crmfb_gcursor(sc, cu);
434 		}
435 	case WSDISPLAYIO_SCURSOR:
436 		{
437 			struct wsdisplay_cursor *cu;
438 
439 			cu = (struct wsdisplay_cursor *)data;
440 			return crmfb_scursor(sc, cu);
441 		}
442 	}
443 	return EPASSTHROUGH;
444 }
445 
446 static paddr_t
447 crmfb_mmap(void *v, void *vs, off_t offset, int prot)
448 {
449 	struct vcons_data *vd;
450 	struct crmfb_softc *sc;
451 	paddr_t pa;
452 
453 	vd = (struct vcons_data *)v;
454 	sc = (struct crmfb_softc *)vd->cookie;
455 
456 #if 1
457 	if (offset >= 0 && offset < sc->sc_fbsize) {
458 		pa = bus_dmamem_mmap(sc->sc_dmat, sc->sc_dma.segs,
459 		    sc->sc_dma.nsegs, offset, prot,
460 		    BUS_DMA_WAITOK | BUS_DMA_COHERENT);
461 		return pa;
462 	}
463 #else
464 	if (offset >= 0 && offset < sc->sc_fbsize) {
465 		pa = bus_space_mmap(SGIMIPS_BUS_SPACE_NORMAL, sc->sc_fbh,
466 		    offset, prot, SGIMIPS_BUS_SPACE_NORMAL);
467 		printf("%s: %08llx -> %llx\n", __func__, offset, pa);
468 		return pa;
469 	}
470 #endif
471 	return -1;
472 }
473 
474 static void
475 crmfb_init_screen(void *c, struct vcons_screen *scr, int existing,
476     long *defattr)
477 {
478 	struct crmfb_softc *sc;
479 	struct rasops_info *ri;
480 
481 	sc = (struct crmfb_softc *)c;
482 	ri = &scr->scr_ri;
483 
484 	ri->ri_flg = RI_CENTER;
485 	ri->ri_depth = sc->sc_depth;
486 	ri->ri_width = sc->sc_width;
487 	ri->ri_height = sc->sc_height;
488 	ri->ri_stride = ri->ri_width * (ri->ri_depth / 8);
489 
490 	switch (ri->ri_depth) {
491 	case 16:
492 		ri->ri_rnum = ri->ri_gnum = ri->ri_bnum = 5;
493 		ri->ri_rpos = 10;
494 		ri->ri_gpos = 5;
495 		ri->ri_bpos = 0;
496 		break;
497 	case 32:
498 		ri->ri_rnum = ri->ri_gnum = ri->ri_bnum = 8;
499 		ri->ri_rpos = 8;
500 		ri->ri_gpos = 16;
501 		ri->ri_bpos = 24;
502 		break;
503 	}
504 
505 	if (sc->sc_shadowfb == NULL)
506 		ri->ri_bits = KERNADDR(sc->sc_dma);
507 	else {
508 		ri->ri_bits = sc->sc_shadowfb;
509 		ri->ri_hwbits = KERNADDR(sc->sc_dma);
510 	}
511 
512 	if (existing)
513 		ri->ri_flg |= RI_CLEAR;
514 
515 	rasops_init(ri, ri->ri_height / 16, ri->ri_width / 8);
516 	ri->ri_caps = WSSCREEN_WSCOLORS;
517 	rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight,
518 	    ri->ri_width / ri->ri_font->fontwidth);
519 	ri->ri_hw = scr;
520 
521 	return;
522 }
523 
524 static int
525 crmfb_putcmap(struct crmfb_softc *sc, struct wsdisplay_cmap *cm)
526 {
527 	u_int idx, cnt;
528 	u_char r[256], g[256], b[256];
529 	u_char *rp, *gp, *bp;
530 	int rv, i;
531 
532 	idx = cm->index;
533 	cnt = cm->count;
534 
535 	if (idx >= 255 || cnt > 256 || idx + cnt > 256)
536 		return EINVAL;
537 
538 	rv = copyin(cm->red, &r[idx], cnt);
539 	if (rv)
540 		return rv;
541 	rv = copyin(cm->green, &g[idx], cnt);
542 	if (rv)
543 		return rv;
544 	rv = copyin(cm->blue, &b[idx], cnt);
545 	if (rv)
546 		return rv;
547 
548 	memcpy(&sc->sc_cmap_red[idx], &r[idx], cnt);
549 	memcpy(&sc->sc_cmap_green[idx], &g[idx], cnt);
550 	memcpy(&sc->sc_cmap_blue[idx], &b[idx], cnt);
551 
552 	rp = &sc->sc_cmap_red[idx];
553 	gp = &sc->sc_cmap_green[idx];
554 	bp = &sc->sc_cmap_blue[idx];
555 
556 	for (i = 0; i < cnt; i++) {
557 		crmfb_set_palette(sc, idx, *rp, *gp, *bp);
558 		idx++;
559 		rp++, gp++, bp++;
560 	}
561 
562 	return 0;
563 }
564 
565 static int
566 crmfb_getcmap(struct crmfb_softc *sc, struct wsdisplay_cmap *cm)
567 {
568 	u_int idx, cnt;
569 	int rv;
570 
571 	idx = cm->index;
572 	cnt = cm->count;
573 
574 	if (idx >= 255 || cnt > 256 || idx + cnt > 256)
575 		return EINVAL;
576 
577 	rv = copyout(&sc->sc_cmap_red[idx], cm->red, cnt);
578 	if (rv)
579 		return rv;
580 	rv = copyout(&sc->sc_cmap_green[idx], cm->green, cnt);
581 	if (rv)
582 		return rv;
583 	rv = copyout(&sc->sc_cmap_blue[idx], cm->blue, cnt);
584 	if (rv)
585 		return rv;
586 
587 	return 0;
588 }
589 
590 static void
591 crmfb_set_palette(struct crmfb_softc *sc, int reg, uint8_t r, uint8_t g,
592     uint8_t b)
593 {
594 	uint32_t val;
595 
596 	if (reg > 255 || sc->sc_depth != 8)
597 		return;
598 
599 	while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_CMAP_FIFO) >= 63)
600 		DELAY(10);
601 
602 	val = (r << 24) | (g << 16) | (b << 8);
603 	crmfb_write_reg(sc, CRMFB_CMAP + (reg * 4), val);
604 
605 	return;
606 }
607 
608 static int
609 crmfb_set_curpos(struct crmfb_softc *sc, int x, int y)
610 {
611 	uint32_t val;
612 
613 	sc->sc_cur_x = x;
614 	sc->sc_cur_y = y;
615 
616 	val = ((x - sc->sc_hot_x) & 0xffff) | ((y - sc->sc_hot_y) << 16);
617 	crmfb_write_reg(sc, CRMFB_CURSOR_POS, val);
618 
619 	return 0;
620 }
621 
622 static int
623 crmfb_gcursor(struct crmfb_softc *sc, struct wsdisplay_cursor *cur)
624 {
625 	/* do nothing for now */
626 	return 0;
627 }
628 
629 static int
630 crmfb_scursor(struct crmfb_softc *sc, struct wsdisplay_cursor *cur)
631 {
632 	if (cur->which & WSDISPLAY_CURSOR_DOCUR) {
633 
634 		crmfb_write_reg(sc, CRMFB_CURSOR_CONTROL, cur->enable ? 1 : 0);
635 	}
636 	if (cur->which & WSDISPLAY_CURSOR_DOHOT) {
637 
638 		sc->sc_hot_x = cur->hot.x;
639 		sc->sc_hot_y = cur->hot.y;
640 	}
641 	if (cur->which & WSDISPLAY_CURSOR_DOPOS) {
642 
643 		crmfb_set_curpos(sc, cur->pos.x, cur->pos.y);
644 	}
645 	if (cur->which & WSDISPLAY_CURSOR_DOCMAP) {
646 		int i;
647 		uint32_t val;
648 
649 		for (i = 0; i < cur->cmap.count; i++) {
650 			val = (cur->cmap.red[i] << 24) |
651 			      (cur->cmap.green[i] << 16) |
652 			      (cur->cmap.blue[i] << 8);
653 			crmfb_write_reg(sc, CRMFB_CURSOR_CMAP0 +
654 			    ((i + cur->cmap.index) << 2), val);
655 		}
656 	}
657 	if (cur->which & WSDISPLAY_CURSOR_DOSHAPE) {
658 
659 		int i, j, cnt = 0;
660 		uint32_t latch = 0, omask;
661 		uint8_t imask;
662 		for (i = 0; i < 64; i++) {
663 			omask = 0x80000000;
664 			imask = 0x01;
665 			cur->image[cnt] &= cur->mask[cnt];
666 			for (j = 0; j < 8; j++) {
667 				if (cur->image[cnt] & imask)
668 					latch |= omask;
669 				omask >>= 1;
670 				if (cur->mask[cnt] & imask)
671 					latch |= omask;
672 				omask >>= 1;
673 				imask <<= 1;
674 			}
675 			cnt++;
676 			imask = 0x01;
677 			cur->image[cnt] &= cur->mask[cnt];
678 			for (j = 0; j < 8; j++) {
679 				if (cur->image[cnt] & imask)
680 					latch |= omask;
681 				omask >>= 1;
682 				if (cur->mask[cnt] & imask)
683 					latch |= omask;
684 				omask >>= 1;
685 				imask <<= 1;
686 			}
687 			cnt++;
688 			crmfb_write_reg(sc, CRMFB_CURSOR_BITMAP + (i << 2),
689 			    latch);
690 			latch = 0;
691 		}
692 	}
693 	return 0;
694 }
695 
696 static inline void
697 crmfb_write_reg(struct crmfb_softc *sc, int offset, uint32_t val)
698 {
699 
700 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, val);
701 	wbflush();
702 }
703 
704 static int
705 crmfb_setup_video(struct crmfb_softc *sc, int depth)
706 {
707 	uint32_t d, h;
708 	int i;
709 
710 	/* disable DMA */
711 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_OVR_CONTROL);
712 	d &= ~(1 << CRMFB_OVR_CONTROL_DMAEN_SHIFT);
713 	crmfb_write_reg(sc, CRMFB_OVR_CONTROL, d);
714 	DELAY(50000);
715 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_CONTROL);
716 	d &= ~(1 << CRMFB_FRM_CONTROL_DMAEN_SHIFT);
717 	crmfb_write_reg(sc, CRMFB_FRM_CONTROL, d);
718 	DELAY(50000);
719 	crmfb_write_reg(sc, CRMFB_DID_CONTROL, 0);
720 	DELAY(50000);
721 
722 	/* ensure that CRM starts drawing at the top left of the screen
723 	 * when we re-enable DMA later
724 	 */
725 	d = (1 << CRMFB_VT_XY_FREEZE_SHIFT);
726 	crmfb_write_reg(sc, CRMFB_VT_XY, d);
727 	delay(1000);
728 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_DOTCLOCK);
729 	d &= ~(1 << CRMFB_DOTCLOCK_CLKRUN_SHIFT);
730 	crmfb_write_reg(sc, CRMFB_DOTCLOCK, d);
731 	delay(1000);
732 
733 	/* reset FIFO */
734 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_TILESIZE);
735 	d |= (1 << CRMFB_FRM_TILESIZE_FIFOR_SHIFT);
736 	crmfb_write_reg(sc, CRMFB_FRM_TILESIZE, d);
737 	d &= ~(1 << CRMFB_FRM_TILESIZE_FIFOR_SHIFT);
738 	crmfb_write_reg(sc, CRMFB_FRM_TILESIZE, d);
739 
740 	/* setup colour mode */
741 	switch (depth) {
742 	case 8:
743 		h = CRMFB_MODE_TYP_I8;
744 		break;
745 	case 16:
746 		h = CRMFB_MODE_TYP_ARGB5;
747 		break;
748 	case 32:
749 		h = CRMFB_MODE_TYP_RGB8;
750 		break;
751 	default:
752 		panic("Unsupported depth");
753 	}
754 	d = h << CRMFB_MODE_TYP_SHIFT;
755 	d |= CRMFB_MODE_BUF_BOTH << CRMFB_MODE_BUF_SHIFT;
756 	for (i = 0; i < (32 * 4); i += 4)
757 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, CRMFB_MODE + i, d);
758 	wbflush();
759 
760 	/* setup tile pointer, but don't turn on DMA yet! */
761 	h = DMAADDR(sc->sc_dmai);
762 	d = (h >> 9) << CRMFB_FRM_CONTROL_TILEPTR_SHIFT;
763 	crmfb_write_reg(sc, CRMFB_FRM_CONTROL, d);
764 
765 	/* init framebuffer width and pixel size */
766 	d = (1 << CRMFB_FRM_TILESIZE_WIDTH_SHIFT);
767 	switch (depth) {
768 	case 8:
769 		h = CRMFB_FRM_TILESIZE_DEPTH_8;
770 		break;
771 	case 16:
772 		h = CRMFB_FRM_TILESIZE_DEPTH_16;
773 		break;
774 	case 32:
775 		h = CRMFB_FRM_TILESIZE_DEPTH_32;
776 		break;
777 	default:
778 		panic("Unsupported depth");
779 	}
780 	d |= (h << CRMFB_FRM_TILESIZE_DEPTH_SHIFT);
781 	crmfb_write_reg(sc, CRMFB_FRM_TILESIZE, d);
782 
783 	/* init framebuffer height, we use the trick that the Linux
784 	 * driver uses to fool the CRM out of tiled mode and into
785 	 * linear mode
786 	 */
787 	h = sc->sc_width * sc->sc_height / (512 / (depth >> 3));
788 	d = h << CRMFB_FRM_PIXSIZE_HEIGHT_SHIFT;
789 	crmfb_write_reg(sc, CRMFB_FRM_PIXSIZE, d);
790 
791 	/* turn off firmware overlay and hardware cursor */
792 	crmfb_write_reg(sc, CRMFB_OVR_WIDTH_TILE, 0);
793 	crmfb_write_reg(sc, CRMFB_CURSOR_CONTROL, 0);
794 
795 	/* enable drawing again */
796 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_DOTCLOCK);
797 	d |= (1 << CRMFB_DOTCLOCK_CLKRUN_SHIFT);
798 	crmfb_write_reg(sc, CRMFB_DOTCLOCK, d);
799 	crmfb_write_reg(sc, CRMFB_VT_XY, 0);
800 
801 	/* turn on DMA for the framebuffer */
802 	d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_CONTROL);
803 	d |= (1 << CRMFB_FRM_CONTROL_DMAEN_SHIFT);
804 	crmfb_write_reg(sc, CRMFB_FRM_CONTROL, d);
805 
806 	sc->sc_depth = depth;
807 	return 0;
808 }
809 
810 static void
811 crmfb_setup_palette(struct crmfb_softc *sc)
812 {
813 	int i;
814 
815 	for (i = 0; i < 256; i++) {
816 		crmfb_set_palette(sc, i, rasops_cmap[(i * 3) + 2],
817 		    rasops_cmap[(i * 3) + 1], rasops_cmap[(i * 3) + 0]);
818 		sc->sc_cmap_red[i] = rasops_cmap[(i * 3) + 2];
819 		sc->sc_cmap_green[i] = rasops_cmap[(i * 3) + 1];
820 		sc->sc_cmap_blue[i] = rasops_cmap[(i * 3) + 0];
821 	}
822 }
823