xref: /netbsd-src/sys/dev/tc/cfb.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /* $NetBSD: cfb.c,v 1.56 2008/12/17 20:51:34 cegger Exp $ */
2 
3 /*-
4  * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Tohru Nishimura.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: cfb.c,v 1.56 2008/12/17 20:51:34 cegger Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/device.h>
39 #include <sys/malloc.h>
40 #include <sys/buf.h>
41 #include <sys/ioctl.h>
42 
43 #include <sys/bus.h>
44 #include <sys/intr.h>
45 
46 #include <dev/wscons/wsconsio.h>
47 #include <dev/wscons/wsdisplayvar.h>
48 
49 #include <dev/rasops/rasops.h>
50 #include <dev/wsfont/wsfont.h>
51 
52 #include <dev/tc/tcvar.h>
53 #include <dev/ic/bt459reg.h>
54 
55 #include <uvm/uvm_extern.h>
56 
57 #if defined(pmax)
58 #define	machine_btop(x) mips_btop(MIPS_KSEG1_TO_PHYS(x))
59 #endif
60 
61 #if defined(alpha)
62 #define	machine_btop(x) alpha_btop(ALPHA_K0SEG_TO_PHYS(x))
63 #endif
64 
65 /*
66  * N.B., Bt459 registers are 8bit width.  Some of TC framebuffers have
67  * obscure register layout such as 2nd and 3rd Bt459 registers are
68  * adjacent each other in a word, i.e.,
69  *	struct bt459triplet {
70  * 		struct {
71  *			u_int8_t u0;
72  *			u_int8_t u1;
73  *			u_int8_t u2;
74  *			unsigned :8;
75  *		} bt_lo;
76  *		...
77  * Although CX has single Bt459, 32bit R/W can be done w/o any trouble.
78  *	struct bt459reg {
79  *		   u_int32_t	   bt_lo;
80  *		   u_int32_t	   bt_hi;
81  *		   u_int32_t	   bt_reg;
82  *		   u_int32_t	   bt_cmap;
83  *	};
84  */
85 
86 /* Bt459 hardware registers, memory-mapped in 32bit stride */
87 #define	bt_lo	0x0
88 #define	bt_hi	0x4
89 #define	bt_reg	0x8
90 #define	bt_cmap 0xc
91 
92 #define	REGWRITE32(p,i,v) do {					\
93 	*(volatile u_int32_t *)((p) + (i)) = (v); tc_wmb();	\
94     } while (0)
95 #define	VDACSELECT(p,r) do {					\
96 	REGWRITE32(p, bt_lo, 0xff & (r));			\
97 	REGWRITE32(p, bt_hi, 0x0f & ((r)>>8));			\
98    } while (0)
99 
100 struct hwcmap256 {
101 #define	CMAP_SIZE	256	/* 256 R/G/B entries */
102 	u_int8_t r[CMAP_SIZE];
103 	u_int8_t g[CMAP_SIZE];
104 	u_int8_t b[CMAP_SIZE];
105 };
106 
107 struct hwcursor64 {
108 	struct wsdisplay_curpos cc_pos;
109 	struct wsdisplay_curpos cc_hot;
110 	struct wsdisplay_curpos cc_size;
111 	struct wsdisplay_curpos cc_magic;
112 #define	CURSOR_MAX_SIZE	64
113 	u_int8_t cc_color[6];
114 	u_int64_t cc_image[CURSOR_MAX_SIZE];
115 	u_int64_t cc_mask[CURSOR_MAX_SIZE];
116 };
117 
118 struct cfb_softc {
119 	vaddr_t sc_vaddr;
120 	size_t sc_size;
121 	struct rasops_info *sc_ri;
122 	struct hwcmap256 sc_cmap;	/* software copy of colormap */
123 	struct hwcursor64 sc_cursor;	/* software copy of cursor */
124 	int sc_blanked;
125 	int sc_curenb;			/* cursor sprite enabled */
126 	int sc_changed;			/* need update of hardware */
127 #define	WSDISPLAY_CMAP_DOLUT	0x20
128 	int nscreens;
129 };
130 
131 #define	CX_MAGIC_X	220
132 #define	CX_MAGIC_Y 	35
133 
134 #define	CX_FB_OFFSET	0x000000
135 #define	CX_FB_SIZE	0x100000
136 #define	CX_BT459_OFFSET	0x200000
137 #define	CX_OFFSET_IREQ	0x300000	/* Interrupt req. control */
138 
139 static int  cfbmatch(device_t, cfdata_t, void *);
140 static void cfbattach(device_t, device_t, void *);
141 
142 CFATTACH_DECL_NEW(cfb, sizeof(struct cfb_softc),
143     cfbmatch, cfbattach, NULL, NULL);
144 
145 static void cfb_common_init(struct rasops_info *);
146 static struct rasops_info cfb_console_ri;
147 static tc_addr_t cfb_consaddr;
148 
149 static struct wsscreen_descr cfb_stdscreen = {
150 	"std", 0, 0,
151 	0, /* textops */
152 	0, 0,
153 	WSSCREEN_REVERSE
154 };
155 
156 static const struct wsscreen_descr *_cfb_scrlist[] = {
157 	&cfb_stdscreen,
158 };
159 
160 static const struct wsscreen_list cfb_screenlist = {
161 	sizeof(_cfb_scrlist) / sizeof(struct wsscreen_descr *), _cfb_scrlist
162 };
163 
164 static int	cfbioctl(void *, void *, u_long, void *, int, struct lwp *);
165 static paddr_t	cfbmmap(void *, void *, off_t, int);
166 
167 static int	cfb_alloc_screen(void *, const struct wsscreen_descr *,
168 				      void **, int *, int *, long *);
169 static void	cfb_free_screen(void *, void *);
170 static int	cfb_show_screen(void *, void *, int,
171 				     void (*) (void *, int, int), void *);
172 
173 static const struct wsdisplay_accessops cfb_accessops = {
174 	cfbioctl,
175 	cfbmmap,
176 	cfb_alloc_screen,
177 	cfb_free_screen,
178 	cfb_show_screen,
179 	0 /* load_font */
180 };
181 
182 int  cfb_cnattach(tc_addr_t);
183 static int  cfbintr(void *);
184 static void cfbhwinit(void *);
185 static void cfb_cmap_init(struct cfb_softc *);
186 
187 static int  get_cmap(struct cfb_softc *, struct wsdisplay_cmap *);
188 static int  set_cmap(struct cfb_softc *, struct wsdisplay_cmap *);
189 static int  set_cursor(struct cfb_softc *, struct wsdisplay_cursor *);
190 static int  get_cursor(struct cfb_softc *, struct wsdisplay_cursor *);
191 static void set_curpos(struct cfb_softc *, struct wsdisplay_curpos *);
192 
193 /*
194  * Compose 2 bit/pixel cursor image.  Bit order will be reversed.
195  *   M M M M I I I I		M I M I M I M I
196  *	[ before ]		   [ after ]
197  *   3 2 1 0 3 2 1 0		0 0 1 1 2 2 3 3
198  *   7 6 5 4 7 6 5 4		4 4 5 5 6 6 7 7
199  */
200 static const u_int8_t shuffle[256] = {
201 	0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54,
202 	0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55,
203 	0x80, 0xc0, 0x90, 0xd0, 0x84, 0xc4, 0x94, 0xd4,
204 	0x81, 0xc1, 0x91, 0xd1, 0x85, 0xc5, 0x95, 0xd5,
205 	0x20, 0x60, 0x30, 0x70, 0x24, 0x64, 0x34, 0x74,
206 	0x21, 0x61, 0x31, 0x71, 0x25, 0x65, 0x35, 0x75,
207 	0xa0, 0xe0, 0xb0, 0xf0, 0xa4, 0xe4, 0xb4, 0xf4,
208 	0xa1, 0xe1, 0xb1, 0xf1, 0xa5, 0xe5, 0xb5, 0xf5,
209 	0x08, 0x48, 0x18, 0x58, 0x0c, 0x4c, 0x1c, 0x5c,
210 	0x09, 0x49, 0x19, 0x59, 0x0d, 0x4d, 0x1d, 0x5d,
211 	0x88, 0xc8, 0x98, 0xd8, 0x8c, 0xcc, 0x9c, 0xdc,
212 	0x89, 0xc9, 0x99, 0xd9, 0x8d, 0xcd, 0x9d, 0xdd,
213 	0x28, 0x68, 0x38, 0x78, 0x2c, 0x6c, 0x3c, 0x7c,
214 	0x29, 0x69, 0x39, 0x79, 0x2d, 0x6d, 0x3d, 0x7d,
215 	0xa8, 0xe8, 0xb8, 0xf8, 0xac, 0xec, 0xbc, 0xfc,
216 	0xa9, 0xe9, 0xb9, 0xf9, 0xad, 0xed, 0xbd, 0xfd,
217 	0x02, 0x42, 0x12, 0x52, 0x06, 0x46, 0x16, 0x56,
218 	0x03, 0x43, 0x13, 0x53, 0x07, 0x47, 0x17, 0x57,
219 	0x82, 0xc2, 0x92, 0xd2, 0x86, 0xc6, 0x96, 0xd6,
220 	0x83, 0xc3, 0x93, 0xd3, 0x87, 0xc7, 0x97, 0xd7,
221 	0x22, 0x62, 0x32, 0x72, 0x26, 0x66, 0x36, 0x76,
222 	0x23, 0x63, 0x33, 0x73, 0x27, 0x67, 0x37, 0x77,
223 	0xa2, 0xe2, 0xb2, 0xf2, 0xa6, 0xe6, 0xb6, 0xf6,
224 	0xa3, 0xe3, 0xb3, 0xf3, 0xa7, 0xe7, 0xb7, 0xf7,
225 	0x0a, 0x4a, 0x1a, 0x5a, 0x0e, 0x4e, 0x1e, 0x5e,
226 	0x0b, 0x4b, 0x1b, 0x5b, 0x0f, 0x4f, 0x1f, 0x5f,
227 	0x8a, 0xca, 0x9a, 0xda, 0x8e, 0xce, 0x9e, 0xde,
228 	0x8b, 0xcb, 0x9b, 0xdb, 0x8f, 0xcf, 0x9f, 0xdf,
229 	0x2a, 0x6a, 0x3a, 0x7a, 0x2e, 0x6e, 0x3e, 0x7e,
230 	0x2b, 0x6b, 0x3b, 0x7b, 0x2f, 0x6f, 0x3f, 0x7f,
231 	0xaa, 0xea, 0xba, 0xfa, 0xae, 0xee, 0xbe, 0xfe,
232 	0xab, 0xeb, 0xbb, 0xfb, 0xaf, 0xef, 0xbf, 0xff,
233 };
234 
235 static int
236 cfbmatch(device_t parent, cfdata_t match, void *aux)
237 {
238 	struct tc_attach_args *ta = aux;
239 
240 	if (strncmp("PMAG-BA ", ta->ta_modname, TC_ROM_LLEN) != 0)
241 		return (0);
242 
243 	return (1);
244 }
245 
246 static void
247 cfbattach(device_t parent, device_t self, void *aux)
248 {
249 	struct cfb_softc *sc = device_private(self);
250 	struct tc_attach_args *ta = aux;
251 	struct rasops_info *ri;
252 	struct wsemuldisplaydev_attach_args waa;
253 	int console;
254 
255 	console = (ta->ta_addr == cfb_consaddr);
256 	if (console) {
257 		sc->sc_ri = ri = &cfb_console_ri;
258 		sc->nscreens = 1;
259 	}
260 	else {
261 		ri = malloc(sizeof(struct rasops_info),
262 			M_DEVBUF, M_NOWAIT|M_ZERO);
263 		if (ri == NULL) {
264 			printf(": can't alloc memory\n");
265 			return;
266 		}
267 
268 		ri->ri_hw = (void *)ta->ta_addr;
269 		cfb_common_init(ri);
270 		sc->sc_ri = ri;
271 	}
272 	printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth);
273 
274 	cfb_cmap_init(sc);
275 
276 	sc->sc_vaddr = ta->ta_addr;
277 	sc->sc_cursor.cc_magic.x = CX_MAGIC_X;
278 	sc->sc_cursor.cc_magic.y = CX_MAGIC_Y;
279 	sc->sc_blanked = sc->sc_curenb = 0;
280 
281 	tc_intr_establish(parent, ta->ta_cookie, IPL_TTY, cfbintr, sc);
282 
283 	/* clear any pending interrupts */
284 	*(volatile u_int8_t *)((char *)ri->ri_hw + CX_OFFSET_IREQ) = 0;
285 
286 	waa.console = console;
287 	waa.scrdata = &cfb_screenlist;
288 	waa.accessops = &cfb_accessops;
289 	waa.accesscookie = sc;
290 
291 	config_found(self, &waa, wsemuldisplaydevprint);
292 }
293 
294 static void
295 cfb_cmap_init(struct cfb_softc *sc)
296 {
297 	struct hwcmap256 *cm;
298 	const u_int8_t *p;
299 	int index;
300 
301 	cm = &sc->sc_cmap;
302 	p = rasops_cmap;
303 	for (index = 0; index < CMAP_SIZE; index++, p += 3) {
304 		cm->r[index] = p[0];
305 		cm->g[index] = p[1];
306 		cm->b[index] = p[2];
307 	}
308 }
309 
310 static void
311 cfb_common_init(struct rasops_info *ri)
312 {
313 	char *base;
314 	int cookie;
315 
316 	base = (void *)ri->ri_hw;
317 
318 	/* initialize colormap and cursor hardware */
319 	cfbhwinit(base);
320 
321 	ri->ri_flg = RI_CENTER;
322 	ri->ri_depth = 8;
323 	ri->ri_width = 1024;
324 	ri->ri_height = 864;
325 	ri->ri_stride = 1024;
326 	ri->ri_bits = base + CX_FB_OFFSET;
327 
328 	/* clear the screen */
329 	memset(ri->ri_bits, 0, ri->ri_stride * ri->ri_height);
330 
331 	wsfont_init();
332 	/* prefer 12 pixel wide font */
333 	cookie = wsfont_find(NULL, 12, 0, 0, WSDISPLAY_FONTORDER_L2R,
334 	    WSDISPLAY_FONTORDER_L2R);
335 	if (cookie <= 0)
336 		cookie = wsfont_find(NULL, 0, 0, 0, WSDISPLAY_FONTORDER_L2R,
337 		    WSDISPLAY_FONTORDER_L2R);
338 	if (cookie <= 0) {
339 		printf("cfb: font table is empty\n");
340 		return;
341 	}
342 
343 	if (wsfont_lock(cookie, &ri->ri_font)) {
344 		printf("cfb: couldn't lock font\n");
345 		return;
346 	}
347 	ri->ri_wsfcookie = cookie;
348 
349 	rasops_init(ri, 34, 80);
350 
351 	/* XXX shouldn't be global */
352 	cfb_stdscreen.nrows = ri->ri_rows;
353 	cfb_stdscreen.ncols = ri->ri_cols;
354 	cfb_stdscreen.textops = &ri->ri_ops;
355 	cfb_stdscreen.capabilities = ri->ri_caps;
356 }
357 
358 static int
359 cfbioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
360 {
361 	struct cfb_softc *sc = v;
362 	struct rasops_info *ri = sc->sc_ri;
363 	int turnoff, s;
364 
365 	switch (cmd) {
366 	case WSDISPLAYIO_GTYPE:
367 		*(u_int *)data = WSDISPLAY_TYPE_CFB;
368 		return (0);
369 
370 	case WSDISPLAYIO_GINFO:
371 #define	wsd_fbip ((struct wsdisplay_fbinfo *)data)
372 		wsd_fbip->height = ri->ri_height;
373 		wsd_fbip->width = ri->ri_width;
374 		wsd_fbip->depth = ri->ri_depth;
375 		wsd_fbip->cmsize = CMAP_SIZE;
376 #undef fbt
377 		return (0);
378 
379 	case WSDISPLAYIO_GETCMAP:
380 		return get_cmap(sc, (struct wsdisplay_cmap *)data);
381 
382 	case WSDISPLAYIO_PUTCMAP:
383 		return set_cmap(sc, (struct wsdisplay_cmap *)data);
384 
385 	case WSDISPLAYIO_SVIDEO:
386 		turnoff = *(int *)data == WSDISPLAYIO_VIDEO_OFF;
387 		if (sc->sc_blanked != turnoff) {
388 			sc->sc_blanked = turnoff;
389 			/* XXX later XXX */
390 		}
391 		return (0);
392 
393 	case WSDISPLAYIO_GVIDEO:
394 		*(u_int *)data = sc->sc_blanked ?
395 		    WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON;
396 		return (0);
397 
398 	case WSDISPLAYIO_GCURPOS:
399 		*(struct wsdisplay_curpos *)data = sc->sc_cursor.cc_pos;
400 		return (0);
401 
402 	case WSDISPLAYIO_SCURPOS:
403 		s = spltty();
404 		set_curpos(sc, (struct wsdisplay_curpos *)data);
405 		sc->sc_changed |= WSDISPLAY_CURSOR_DOPOS;
406 		splx(s);
407 		return (0);
408 
409 	case WSDISPLAYIO_GCURMAX:
410 		((struct wsdisplay_curpos *)data)->x =
411 		((struct wsdisplay_curpos *)data)->y = CURSOR_MAX_SIZE;
412 		return (0);
413 
414 	case WSDISPLAYIO_GCURSOR:
415 		return get_cursor(sc, (struct wsdisplay_cursor *)data);
416 
417 	case WSDISPLAYIO_SCURSOR:
418 		return set_cursor(sc, (struct wsdisplay_cursor *)data);
419 
420 	case WSDISPLAYIO_SMODE:
421 		if (*(int *)data == WSDISPLAYIO_MODE_EMUL) {
422 			s = spltty();
423 			cfb_cmap_init(sc);
424 			sc->sc_curenb = 0;
425 			sc->sc_blanked = 0;
426 			sc->sc_changed |= (WSDISPLAY_CURSOR_DOCUR |
427 			    WSDISPLAY_CMAP_DOLUT);
428 			splx(s);
429 		}
430 		return (0);
431 	}
432 	return EPASSTHROUGH;
433 }
434 
435 paddr_t
436 cfbmmap(void *v, void *vs, off_t offset, int prot)
437 {
438 	struct cfb_softc *sc = v;
439 
440 	if (offset >= CX_FB_SIZE || offset < 0)
441 		return (-1);
442 	return machine_btop(sc->sc_vaddr + CX_FB_OFFSET + offset);
443 }
444 
445 static int
446 cfb_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
447     int *curxp, int *curyp, long *attrp)
448 {
449 	struct cfb_softc *sc = v;
450 	struct rasops_info *ri = sc->sc_ri;
451 	long defattr;
452 
453 	if (sc->nscreens > 0)
454 		return (ENOMEM);
455 
456 	*cookiep = ri;	 /* one and only for now */
457 	*curxp = 0;
458 	*curyp = 0;
459 	(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
460 	*attrp = defattr;
461 	sc->nscreens++;
462 	return (0);
463 }
464 
465 static void
466 cfb_free_screen(void *v, void *cookie)
467 {
468 	struct cfb_softc *sc = v;
469 
470 	if (sc->sc_ri == &cfb_console_ri)
471 		panic("cfb_free_screen: console");
472 
473 	sc->nscreens--;
474 }
475 
476 static int
477 cfb_show_screen(void *v, void *cookie, int waitok,
478     void (*cb)(void *, int, int), void *cbarg)
479 {
480 
481 	return (0);
482 }
483 
484 /* EXPORT */ int
485 cfb_cnattach(tc_addr_t addr)
486 {
487 	struct rasops_info *ri;
488 	long defattr;
489 
490 	ri = &cfb_console_ri;
491 	ri->ri_hw = (void *)addr;
492 	cfb_common_init(ri);
493 	(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
494 	wsdisplay_cnattach(&cfb_stdscreen, ri, 0, 0, defattr);
495 	cfb_consaddr = addr;
496 	return(0);
497 }
498 
499 static int
500 cfbintr(void *arg)
501 {
502 	struct cfb_softc *sc = arg;
503 	char *base, *vdac;
504 	int v;
505 
506 	base = (void *)sc->sc_ri->ri_hw;
507 	*(u_int8_t *)(base + CX_OFFSET_IREQ) = 0;
508 	if (sc->sc_changed == 0)
509 		return (1);
510 
511 	vdac = base + CX_BT459_OFFSET;
512 	v = sc->sc_changed;
513 	if (v & WSDISPLAY_CURSOR_DOCUR) {
514 		VDACSELECT(vdac, BT459_IREG_CCR);
515 		REGWRITE32(vdac, bt_reg, (sc->sc_curenb) ? 0xc0 : 0x00);
516 	}
517 	if (v & (WSDISPLAY_CURSOR_DOPOS | WSDISPLAY_CURSOR_DOHOT)) {
518 		int x, y;
519 
520 		x = sc->sc_cursor.cc_pos.x - sc->sc_cursor.cc_hot.x;
521 		y = sc->sc_cursor.cc_pos.y - sc->sc_cursor.cc_hot.y;
522 
523 		x += sc->sc_cursor.cc_magic.x;
524 		y += sc->sc_cursor.cc_magic.y;
525 
526 		VDACSELECT(vdac, BT459_IREG_CURSOR_X_LOW);
527 		REGWRITE32(vdac, bt_reg, x);
528 		REGWRITE32(vdac, bt_reg, x >> 8);
529 		REGWRITE32(vdac, bt_reg, y);
530 		REGWRITE32(vdac, bt_reg, y >> 8);
531 	}
532 	if (v & WSDISPLAY_CURSOR_DOCMAP) {
533 		u_int8_t *cp = sc->sc_cursor.cc_color;
534 
535 		VDACSELECT(vdac, BT459_IREG_CCOLOR_2);
536 		REGWRITE32(vdac, bt_reg, cp[1]);
537 		REGWRITE32(vdac, bt_reg, cp[3]);
538 		REGWRITE32(vdac, bt_reg, cp[5]);
539 
540 		REGWRITE32(vdac, bt_reg, cp[0]);
541 		REGWRITE32(vdac, bt_reg, cp[2]);
542 		REGWRITE32(vdac, bt_reg, cp[4]);
543 	}
544 	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
545 		u_int8_t *ip, *mp, img, msk;
546 		u_int8_t u;
547 		int bcnt;
548 
549 		ip = (u_int8_t *)sc->sc_cursor.cc_image;
550 		mp = (u_int8_t *)sc->sc_cursor.cc_mask;
551 
552 		bcnt = 0;
553 		VDACSELECT(vdac, BT459_IREG_CRAM_BASE+0);
554 		/* 64 pixel scan line is consisted with 16 byte cursor ram */
555 		while (bcnt < sc->sc_cursor.cc_size.y * 16) {
556 			/* pad right half 32 pixel when smaller than 33 */
557 			if ((bcnt & 0x8) && sc->sc_cursor.cc_size.x < 33) {
558 				REGWRITE32(vdac, bt_reg, 0);
559 				REGWRITE32(vdac, bt_reg, 0);
560 			}
561 			else {
562 				img = *ip++;
563 				msk = *mp++;
564 				img &= msk;	/* cookie off image */
565 				u = (msk & 0x0f) << 4 | (img & 0x0f);
566 				REGWRITE32(vdac, bt_reg, shuffle[u]);
567 				u = (msk & 0xf0) | (img & 0xf0) >> 4;
568 				REGWRITE32(vdac, bt_reg, shuffle[u]);
569 			}
570 			bcnt += 2;
571 		}
572 		/* pad unoccupied scan lines */
573 		while (bcnt < CURSOR_MAX_SIZE * 16) {
574 			REGWRITE32(vdac, bt_reg, 0);
575 			REGWRITE32(vdac, bt_reg, 0);
576 			bcnt += 2;
577 		}
578 	}
579 	if (v & WSDISPLAY_CMAP_DOLUT) {
580 		struct hwcmap256 *cm = &sc->sc_cmap;
581 		int index;
582 
583 		VDACSELECT(vdac, 0);
584 		for (index = 0; index < CMAP_SIZE; index++) {
585 			REGWRITE32(vdac, bt_cmap, cm->r[index]);
586 			REGWRITE32(vdac, bt_cmap, cm->g[index]);
587 			REGWRITE32(vdac, bt_cmap, cm->b[index]);
588 		}
589 	}
590 	sc->sc_changed = 0;
591 	return (1);
592 }
593 
594 static void
595 cfbhwinit(void *cfbbase)
596 {
597 	char *vdac = (char *)cfbbase + CX_BT459_OFFSET;
598 	const u_int8_t *p;
599 	int i;
600 
601 	VDACSELECT(vdac, BT459_IREG_COMMAND_0);
602 	REGWRITE32(vdac, bt_reg, 0x40); /* CMD0 */
603 	REGWRITE32(vdac, bt_reg, 0x0);  /* CMD1 */
604 	REGWRITE32(vdac, bt_reg, 0xc0); /* CMD2 */
605 	REGWRITE32(vdac, bt_reg, 0xff); /* PRM */
606 	REGWRITE32(vdac, bt_reg, 0);    /* 205 */
607 	REGWRITE32(vdac, bt_reg, 0x0);  /* PBM */
608 	REGWRITE32(vdac, bt_reg, 0);    /* 207 */
609 	REGWRITE32(vdac, bt_reg, 0x0);  /* ORM */
610 	REGWRITE32(vdac, bt_reg, 0x0);  /* OBM */
611 	REGWRITE32(vdac, bt_reg, 0x0);  /* ILV */
612 	REGWRITE32(vdac, bt_reg, 0x0);  /* TEST */
613 
614 	VDACSELECT(vdac, BT459_IREG_CCR);
615 	REGWRITE32(vdac, bt_reg, 0x0);
616 	REGWRITE32(vdac, bt_reg, 0x0);
617 	REGWRITE32(vdac, bt_reg, 0x0);
618 	REGWRITE32(vdac, bt_reg, 0x0);
619 	REGWRITE32(vdac, bt_reg, 0x0);
620 	REGWRITE32(vdac, bt_reg, 0x0);
621 	REGWRITE32(vdac, bt_reg, 0x0);
622 	REGWRITE32(vdac, bt_reg, 0x0);
623 	REGWRITE32(vdac, bt_reg, 0x0);
624 	REGWRITE32(vdac, bt_reg, 0x0);
625 	REGWRITE32(vdac, bt_reg, 0x0);
626 	REGWRITE32(vdac, bt_reg, 0x0);
627 	REGWRITE32(vdac, bt_reg, 0x0);
628 
629 	/* build sane colormap */
630 	VDACSELECT(vdac, 0);
631 	p = rasops_cmap;
632 	for (i = 0; i < CMAP_SIZE; i++, p += 3) {
633 		REGWRITE32(vdac, bt_cmap, p[0]);
634 		REGWRITE32(vdac, bt_cmap, p[1]);
635 		REGWRITE32(vdac, bt_cmap, p[2]);
636 	}
637 
638 	/* clear out cursor image */
639 	VDACSELECT(vdac, BT459_IREG_CRAM_BASE);
640 	for (i = 0; i < 1024; i++)
641 		REGWRITE32(vdac, bt_reg, 0xff);
642 
643 	/*
644 	 * 2 bit/pixel cursor.  Assign MSB for cursor mask and LSB for
645 	 * cursor image.  CCOLOR_2 for mask color, while CCOLOR_3 for
646 	 * image color.  CCOLOR_1 will be never used.
647 	 */
648 	VDACSELECT(vdac, BT459_IREG_CCOLOR_1);
649 	REGWRITE32(vdac, bt_reg, 0xff);
650 	REGWRITE32(vdac, bt_reg, 0xff);
651 	REGWRITE32(vdac, bt_reg, 0xff);
652 
653 	REGWRITE32(vdac, bt_reg, 0);
654 	REGWRITE32(vdac, bt_reg, 0);
655 	REGWRITE32(vdac, bt_reg, 0);
656 
657 	REGWRITE32(vdac, bt_reg, 0xff);
658 	REGWRITE32(vdac, bt_reg, 0xff);
659 	REGWRITE32(vdac, bt_reg, 0xff);
660 }
661 
662 static int
663 get_cmap(struct cfb_softc *sc, struct wsdisplay_cmap *p)
664 {
665 	u_int index = p->index, count = p->count;
666 	int error;
667 
668 	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
669 		return (EINVAL);
670 
671 	error = copyout(&sc->sc_cmap.r[index], p->red, count);
672 	if (error)
673 		return error;
674 	error = copyout(&sc->sc_cmap.g[index], p->green, count);
675 	if (error)
676 		return error;
677 	error = copyout(&sc->sc_cmap.b[index], p->blue, count);
678 	return error;
679 }
680 
681 static int
682 set_cmap(struct cfb_softc *sc, struct wsdisplay_cmap *p)
683 {
684 	struct hwcmap256 cmap;
685 	u_int index = p->index, count = p->count;
686 	int error, s;
687 
688 	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
689 		return (EINVAL);
690 
691 	error = copyin(p->red, &cmap.r[index], count);
692 	if (error)
693 		return error;
694 	error = copyin(p->green, &cmap.g[index], count);
695 	if (error)
696 		return error;
697 	error = copyin(p->blue, &cmap.b[index], count);
698 	if (error)
699 		return error;
700 	s = spltty();
701 	memcpy(&sc->sc_cmap.r[index], &cmap.r[index], count);
702 	memcpy(&sc->sc_cmap.g[index], &cmap.g[index], count);
703 	memcpy(&sc->sc_cmap.b[index], &cmap.b[index], count);
704 	sc->sc_changed |= WSDISPLAY_CMAP_DOLUT;
705 	splx(s);
706 	return (0);
707 }
708 
709 static int
710 set_cursor(struct cfb_softc *sc, struct wsdisplay_cursor *p)
711 {
712 #define	cc (&sc->sc_cursor)
713 	u_int v, index = 0, count = 0, icount = 0;
714 	uint8_t r[2], g[2], b[2], image[512], mask[512];
715 	int error, s;
716 
717 	v = p->which;
718 	if (v & WSDISPLAY_CURSOR_DOCMAP) {
719 		index = p->cmap.index;
720 		count = p->cmap.count;
721 		if (index >= 2 || (index + count) > 2)
722 			return (EINVAL);
723 		error = copyin(p->cmap.red, &r[index], count);
724 		if (error)
725 			return error;
726 		error = copyin(p->cmap.green, &g[index], count);
727 		if (error)
728 			return error;
729 		error = copyin(p->cmap.blue, &b[index], count);
730 		if (error)
731 			return error;
732 	}
733 	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
734 		if (p->size.x > CURSOR_MAX_SIZE || p->size.y > CURSOR_MAX_SIZE)
735 			return (EINVAL);
736 		icount = ((p->size.x < 33) ? 4 : 8) * p->size.y;
737 		error = copyin(p->image, image, icount);
738 		if (error)
739 			return error;
740 		error = copyin(p->mask, mask, icount);
741 		if (error)
742 			return error;
743 	}
744 
745 	s = spltty();
746 	if (v & WSDISPLAY_CURSOR_DOCUR)
747 		sc->sc_curenb = p->enable;
748 	if (v & WSDISPLAY_CURSOR_DOPOS)
749 		set_curpos(sc, &p->pos);
750 	if (v & WSDISPLAY_CURSOR_DOHOT)
751 		cc->cc_hot = p->hot;
752 	if (v & WSDISPLAY_CURSOR_DOCMAP) {
753 		memcpy(&cc->cc_color[index], &r[index], count);
754 		memcpy(&cc->cc_color[index + 2], &g[index], count);
755 		memcpy(&cc->cc_color[index + 4], &b[index], count);
756 	}
757 	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
758 		cc->cc_size = p->size;
759 		memset(cc->cc_image, 0, sizeof cc->cc_image);
760 		memcpy(cc->cc_image, image, icount);
761 		memset(cc->cc_mask, 0, sizeof cc->cc_mask);
762 		memcpy(cc->cc_mask, mask, icount);
763 	}
764 	sc->sc_changed |= v;
765 	splx(s);
766 
767 	return (0);
768 #undef cc
769 }
770 
771 static int
772 get_cursor(struct cfb_softc *sc, struct wsdisplay_cursor *p)
773 {
774 	return (EPASSTHROUGH); /* XXX */
775 }
776 
777 static void
778 set_curpos(struct cfb_softc *sc, struct wsdisplay_curpos *curpos)
779 {
780 	struct rasops_info *ri = sc->sc_ri;
781 	int x = curpos->x, y = curpos->y;
782 
783 	if (y < 0)
784 		y = 0;
785 	else if (y > ri->ri_height)
786 		y = ri->ri_height;
787 	if (x < 0)
788 		x = 0;
789 	else if (x > ri->ri_width)
790 		x = ri->ri_width;
791 	sc->sc_cursor.cc_pos.x = x;
792 	sc->sc_cursor.cc_pos.y = y;
793 }
794