xref: /netbsd-src/sys/dev/tc/cfb.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /* $NetBSD: cfb.c,v 1.59 2010/05/15 08:53:27 tsutsui 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.59 2010/05/15 08:53:27 tsutsui 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  *			uint8_t u0;
72  *			uint8_t u1;
73  *			uint8_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  *		   uint32_t	   bt_lo;
80  *		   uint32_t	   bt_hi;
81  *		   uint32_t	   bt_reg;
82  *		   uint32_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 uint32_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 	uint8_t r[CMAP_SIZE];
103 	uint8_t g[CMAP_SIZE];
104 	uint8_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 	uint8_t cc_color[6];
114 	uint64_t cc_image[CURSOR_MAX_SIZE];
115 	uint64_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 uint8_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 		ri->ri_flg &= ~RI_NO_AUTO;
259 		sc->nscreens = 1;
260 	}
261 	else {
262 		ri = malloc(sizeof(struct rasops_info),
263 			M_DEVBUF, M_NOWAIT|M_ZERO);
264 		if (ri == NULL) {
265 			printf(": can't alloc memory\n");
266 			return;
267 		}
268 
269 		ri->ri_hw = (void *)ta->ta_addr;
270 		cfb_common_init(ri);
271 		sc->sc_ri = ri;
272 	}
273 	printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth);
274 
275 	cfb_cmap_init(sc);
276 
277 	sc->sc_vaddr = ta->ta_addr;
278 	sc->sc_cursor.cc_magic.x = CX_MAGIC_X;
279 	sc->sc_cursor.cc_magic.y = CX_MAGIC_Y;
280 	sc->sc_blanked = sc->sc_curenb = 0;
281 
282 	tc_intr_establish(parent, ta->ta_cookie, IPL_TTY, cfbintr, sc);
283 
284 	/* clear any pending interrupts */
285 	*(volatile uint8_t *)((char *)ri->ri_hw + CX_OFFSET_IREQ) = 0;
286 
287 	waa.console = console;
288 	waa.scrdata = &cfb_screenlist;
289 	waa.accessops = &cfb_accessops;
290 	waa.accesscookie = sc;
291 
292 	config_found(self, &waa, wsemuldisplaydevprint);
293 }
294 
295 static void
296 cfb_cmap_init(struct cfb_softc *sc)
297 {
298 	struct hwcmap256 *cm;
299 	const uint8_t *p;
300 	int index;
301 
302 	cm = &sc->sc_cmap;
303 	p = rasops_cmap;
304 	for (index = 0; index < CMAP_SIZE; index++, p += 3) {
305 		cm->r[index] = p[0];
306 		cm->g[index] = p[1];
307 		cm->b[index] = p[2];
308 	}
309 }
310 
311 static void
312 cfb_common_init(struct rasops_info *ri)
313 {
314 	char *base;
315 	int cookie;
316 
317 	base = (void *)ri->ri_hw;
318 
319 	/* initialize colormap and cursor hardware */
320 	cfbhwinit(base);
321 
322 	ri->ri_flg = RI_CENTER;
323 	if (ri == &cfb_console_ri)
324 		ri->ri_flg |= RI_NO_AUTO;
325 	ri->ri_depth = 8;
326 	ri->ri_width = 1024;
327 	ri->ri_height = 864;
328 	ri->ri_stride = 1024;
329 	ri->ri_bits = base + CX_FB_OFFSET;
330 
331 	/* clear the screen */
332 	memset(ri->ri_bits, 0, ri->ri_stride * ri->ri_height);
333 
334 	wsfont_init();
335 	/* prefer 12 pixel wide font */
336 	cookie = wsfont_find(NULL, 12, 0, 0, WSDISPLAY_FONTORDER_L2R,
337 	    WSDISPLAY_FONTORDER_L2R);
338 	if (cookie <= 0)
339 		cookie = wsfont_find(NULL, 0, 0, 0, WSDISPLAY_FONTORDER_L2R,
340 		    WSDISPLAY_FONTORDER_L2R);
341 	if (cookie <= 0) {
342 		printf("cfb: font table is empty\n");
343 		return;
344 	}
345 
346 	if (wsfont_lock(cookie, &ri->ri_font)) {
347 		printf("cfb: couldn't lock font\n");
348 		return;
349 	}
350 	ri->ri_wsfcookie = cookie;
351 
352 	rasops_init(ri, 34, 80);
353 
354 	/* XXX shouldn't be global */
355 	cfb_stdscreen.nrows = ri->ri_rows;
356 	cfb_stdscreen.ncols = ri->ri_cols;
357 	cfb_stdscreen.textops = &ri->ri_ops;
358 	cfb_stdscreen.capabilities = ri->ri_caps;
359 }
360 
361 static int
362 cfbioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
363 {
364 	struct cfb_softc *sc = v;
365 	struct rasops_info *ri = sc->sc_ri;
366 	int turnoff, s;
367 
368 	switch (cmd) {
369 	case WSDISPLAYIO_GTYPE:
370 		*(u_int *)data = WSDISPLAY_TYPE_CFB;
371 		return (0);
372 
373 	case WSDISPLAYIO_GINFO:
374 #define	wsd_fbip ((struct wsdisplay_fbinfo *)data)
375 		wsd_fbip->height = ri->ri_height;
376 		wsd_fbip->width = ri->ri_width;
377 		wsd_fbip->depth = ri->ri_depth;
378 		wsd_fbip->cmsize = CMAP_SIZE;
379 #undef fbt
380 		return (0);
381 
382 	case WSDISPLAYIO_GETCMAP:
383 		return get_cmap(sc, (struct wsdisplay_cmap *)data);
384 
385 	case WSDISPLAYIO_PUTCMAP:
386 		return set_cmap(sc, (struct wsdisplay_cmap *)data);
387 
388 	case WSDISPLAYIO_SVIDEO:
389 		turnoff = *(int *)data == WSDISPLAYIO_VIDEO_OFF;
390 		if (sc->sc_blanked != turnoff) {
391 			sc->sc_blanked = turnoff;
392 			/* XXX later XXX */
393 		}
394 		return (0);
395 
396 	case WSDISPLAYIO_GVIDEO:
397 		*(u_int *)data = sc->sc_blanked ?
398 		    WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON;
399 		return (0);
400 
401 	case WSDISPLAYIO_GCURPOS:
402 		*(struct wsdisplay_curpos *)data = sc->sc_cursor.cc_pos;
403 		return (0);
404 
405 	case WSDISPLAYIO_SCURPOS:
406 		s = spltty();
407 		set_curpos(sc, (struct wsdisplay_curpos *)data);
408 		sc->sc_changed |= WSDISPLAY_CURSOR_DOPOS;
409 		splx(s);
410 		return (0);
411 
412 	case WSDISPLAYIO_GCURMAX:
413 		((struct wsdisplay_curpos *)data)->x =
414 		((struct wsdisplay_curpos *)data)->y = CURSOR_MAX_SIZE;
415 		return (0);
416 
417 	case WSDISPLAYIO_GCURSOR:
418 		return get_cursor(sc, (struct wsdisplay_cursor *)data);
419 
420 	case WSDISPLAYIO_SCURSOR:
421 		return set_cursor(sc, (struct wsdisplay_cursor *)data);
422 
423 	case WSDISPLAYIO_SMODE:
424 		if (*(int *)data == WSDISPLAYIO_MODE_EMUL) {
425 			s = spltty();
426 			cfb_cmap_init(sc);
427 			sc->sc_curenb = 0;
428 			sc->sc_blanked = 0;
429 			sc->sc_changed |= (WSDISPLAY_CURSOR_DOCUR |
430 			    WSDISPLAY_CMAP_DOLUT);
431 			splx(s);
432 		}
433 		return (0);
434 	}
435 	return EPASSTHROUGH;
436 }
437 
438 paddr_t
439 cfbmmap(void *v, void *vs, off_t offset, int prot)
440 {
441 	struct cfb_softc *sc = v;
442 
443 	if (offset >= CX_FB_SIZE || offset < 0)
444 		return (-1);
445 	return machine_btop(sc->sc_vaddr + CX_FB_OFFSET + offset);
446 }
447 
448 static int
449 cfb_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
450     int *curxp, int *curyp, long *attrp)
451 {
452 	struct cfb_softc *sc = v;
453 	struct rasops_info *ri = sc->sc_ri;
454 	long defattr;
455 
456 	if (sc->nscreens > 0)
457 		return (ENOMEM);
458 
459 	*cookiep = ri;	 /* one and only for now */
460 	*curxp = 0;
461 	*curyp = 0;
462 	(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
463 	*attrp = defattr;
464 	sc->nscreens++;
465 	return (0);
466 }
467 
468 static void
469 cfb_free_screen(void *v, void *cookie)
470 {
471 	struct cfb_softc *sc = v;
472 
473 	if (sc->sc_ri == &cfb_console_ri)
474 		panic("cfb_free_screen: console");
475 
476 	sc->nscreens--;
477 }
478 
479 static int
480 cfb_show_screen(void *v, void *cookie, int waitok,
481     void (*cb)(void *, int, int), void *cbarg)
482 {
483 
484 	return (0);
485 }
486 
487 /* EXPORT */ int
488 cfb_cnattach(tc_addr_t addr)
489 {
490 	struct rasops_info *ri;
491 	long defattr;
492 
493 	ri = &cfb_console_ri;
494 	ri->ri_hw = (void *)addr;
495 	cfb_common_init(ri);
496 	(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
497 	wsdisplay_cnattach(&cfb_stdscreen, ri, 0, 0, defattr);
498 	cfb_consaddr = addr;
499 	return(0);
500 }
501 
502 static int
503 cfbintr(void *arg)
504 {
505 	struct cfb_softc *sc = arg;
506 	char *base, *vdac;
507 	int v;
508 
509 	base = (void *)sc->sc_ri->ri_hw;
510 	*(uint8_t *)(base + CX_OFFSET_IREQ) = 0;
511 	if (sc->sc_changed == 0)
512 		return (1);
513 
514 	vdac = base + CX_BT459_OFFSET;
515 	v = sc->sc_changed;
516 	if (v & WSDISPLAY_CURSOR_DOCUR) {
517 		VDACSELECT(vdac, BT459_IREG_CCR);
518 		REGWRITE32(vdac, bt_reg, (sc->sc_curenb) ? 0xc0 : 0x00);
519 	}
520 	if (v & (WSDISPLAY_CURSOR_DOPOS | WSDISPLAY_CURSOR_DOHOT)) {
521 		int x, y;
522 
523 		x = sc->sc_cursor.cc_pos.x - sc->sc_cursor.cc_hot.x;
524 		y = sc->sc_cursor.cc_pos.y - sc->sc_cursor.cc_hot.y;
525 
526 		x += sc->sc_cursor.cc_magic.x;
527 		y += sc->sc_cursor.cc_magic.y;
528 
529 		VDACSELECT(vdac, BT459_IREG_CURSOR_X_LOW);
530 		REGWRITE32(vdac, bt_reg, x);
531 		REGWRITE32(vdac, bt_reg, x >> 8);
532 		REGWRITE32(vdac, bt_reg, y);
533 		REGWRITE32(vdac, bt_reg, y >> 8);
534 	}
535 	if (v & WSDISPLAY_CURSOR_DOCMAP) {
536 		uint8_t *cp = sc->sc_cursor.cc_color;
537 
538 		VDACSELECT(vdac, BT459_IREG_CCOLOR_2);
539 		REGWRITE32(vdac, bt_reg, cp[1]);
540 		REGWRITE32(vdac, bt_reg, cp[3]);
541 		REGWRITE32(vdac, bt_reg, cp[5]);
542 
543 		REGWRITE32(vdac, bt_reg, cp[0]);
544 		REGWRITE32(vdac, bt_reg, cp[2]);
545 		REGWRITE32(vdac, bt_reg, cp[4]);
546 	}
547 	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
548 		uint8_t *ip, *mp, img, msk;
549 		uint8_t u;
550 		int bcnt;
551 
552 		ip = (uint8_t *)sc->sc_cursor.cc_image;
553 		mp = (uint8_t *)sc->sc_cursor.cc_mask;
554 
555 		bcnt = 0;
556 		VDACSELECT(vdac, BT459_IREG_CRAM_BASE+0);
557 		/* 64 pixel scan line is consisted with 16 byte cursor ram */
558 		while (bcnt < sc->sc_cursor.cc_size.y * 16) {
559 			/* pad right half 32 pixel when smaller than 33 */
560 			if ((bcnt & 0x8) && sc->sc_cursor.cc_size.x < 33) {
561 				REGWRITE32(vdac, bt_reg, 0);
562 				REGWRITE32(vdac, bt_reg, 0);
563 			}
564 			else {
565 				img = *ip++;
566 				msk = *mp++;
567 				img &= msk;	/* cookie off image */
568 				u = (msk & 0x0f) << 4 | (img & 0x0f);
569 				REGWRITE32(vdac, bt_reg, shuffle[u]);
570 				u = (msk & 0xf0) | (img & 0xf0) >> 4;
571 				REGWRITE32(vdac, bt_reg, shuffle[u]);
572 			}
573 			bcnt += 2;
574 		}
575 		/* pad unoccupied scan lines */
576 		while (bcnt < CURSOR_MAX_SIZE * 16) {
577 			REGWRITE32(vdac, bt_reg, 0);
578 			REGWRITE32(vdac, bt_reg, 0);
579 			bcnt += 2;
580 		}
581 	}
582 	if (v & WSDISPLAY_CMAP_DOLUT) {
583 		struct hwcmap256 *cm = &sc->sc_cmap;
584 		int index;
585 
586 		VDACSELECT(vdac, 0);
587 		for (index = 0; index < CMAP_SIZE; index++) {
588 			REGWRITE32(vdac, bt_cmap, cm->r[index]);
589 			REGWRITE32(vdac, bt_cmap, cm->g[index]);
590 			REGWRITE32(vdac, bt_cmap, cm->b[index]);
591 		}
592 	}
593 	sc->sc_changed = 0;
594 	return (1);
595 }
596 
597 static void
598 cfbhwinit(void *cfbbase)
599 {
600 	char *vdac = (char *)cfbbase + CX_BT459_OFFSET;
601 	const uint8_t *p;
602 	int i;
603 
604 	VDACSELECT(vdac, BT459_IREG_COMMAND_0);
605 	REGWRITE32(vdac, bt_reg, 0x40); /* CMD0 */
606 	REGWRITE32(vdac, bt_reg, 0x0);  /* CMD1 */
607 	REGWRITE32(vdac, bt_reg, 0xc0); /* CMD2 */
608 	REGWRITE32(vdac, bt_reg, 0xff); /* PRM */
609 	REGWRITE32(vdac, bt_reg, 0);    /* 205 */
610 	REGWRITE32(vdac, bt_reg, 0x0);  /* PBM */
611 	REGWRITE32(vdac, bt_reg, 0);    /* 207 */
612 	REGWRITE32(vdac, bt_reg, 0x0);  /* ORM */
613 	REGWRITE32(vdac, bt_reg, 0x0);  /* OBM */
614 	REGWRITE32(vdac, bt_reg, 0x0);  /* ILV */
615 	REGWRITE32(vdac, bt_reg, 0x0);  /* TEST */
616 
617 	VDACSELECT(vdac, BT459_IREG_CCR);
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 	REGWRITE32(vdac, bt_reg, 0x0);
629 	REGWRITE32(vdac, bt_reg, 0x0);
630 	REGWRITE32(vdac, bt_reg, 0x0);
631 
632 	/* build sane colormap */
633 	VDACSELECT(vdac, 0);
634 	p = rasops_cmap;
635 	for (i = 0; i < CMAP_SIZE; i++, p += 3) {
636 		REGWRITE32(vdac, bt_cmap, p[0]);
637 		REGWRITE32(vdac, bt_cmap, p[1]);
638 		REGWRITE32(vdac, bt_cmap, p[2]);
639 	}
640 
641 	/* clear out cursor image */
642 	VDACSELECT(vdac, BT459_IREG_CRAM_BASE);
643 	for (i = 0; i < 1024; i++)
644 		REGWRITE32(vdac, bt_reg, 0xff);
645 
646 	/*
647 	 * 2 bit/pixel cursor.  Assign MSB for cursor mask and LSB for
648 	 * cursor image.  CCOLOR_2 for mask color, while CCOLOR_3 for
649 	 * image color.  CCOLOR_1 will be never used.
650 	 */
651 	VDACSELECT(vdac, BT459_IREG_CCOLOR_1);
652 	REGWRITE32(vdac, bt_reg, 0xff);
653 	REGWRITE32(vdac, bt_reg, 0xff);
654 	REGWRITE32(vdac, bt_reg, 0xff);
655 
656 	REGWRITE32(vdac, bt_reg, 0);
657 	REGWRITE32(vdac, bt_reg, 0);
658 	REGWRITE32(vdac, bt_reg, 0);
659 
660 	REGWRITE32(vdac, bt_reg, 0xff);
661 	REGWRITE32(vdac, bt_reg, 0xff);
662 	REGWRITE32(vdac, bt_reg, 0xff);
663 }
664 
665 static int
666 get_cmap(struct cfb_softc *sc, struct wsdisplay_cmap *p)
667 {
668 	u_int index = p->index, count = p->count;
669 	int error;
670 
671 	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
672 		return (EINVAL);
673 
674 	error = copyout(&sc->sc_cmap.r[index], p->red, count);
675 	if (error)
676 		return error;
677 	error = copyout(&sc->sc_cmap.g[index], p->green, count);
678 	if (error)
679 		return error;
680 	error = copyout(&sc->sc_cmap.b[index], p->blue, count);
681 	return error;
682 }
683 
684 static int
685 set_cmap(struct cfb_softc *sc, struct wsdisplay_cmap *p)
686 {
687 	struct hwcmap256 cmap;
688 	u_int index = p->index, count = p->count;
689 	int error, s;
690 
691 	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
692 		return (EINVAL);
693 
694 	error = copyin(p->red, &cmap.r[index], count);
695 	if (error)
696 		return error;
697 	error = copyin(p->green, &cmap.g[index], count);
698 	if (error)
699 		return error;
700 	error = copyin(p->blue, &cmap.b[index], count);
701 	if (error)
702 		return error;
703 	s = spltty();
704 	memcpy(&sc->sc_cmap.r[index], &cmap.r[index], count);
705 	memcpy(&sc->sc_cmap.g[index], &cmap.g[index], count);
706 	memcpy(&sc->sc_cmap.b[index], &cmap.b[index], count);
707 	sc->sc_changed |= WSDISPLAY_CMAP_DOLUT;
708 	splx(s);
709 	return (0);
710 }
711 
712 static int
713 set_cursor(struct cfb_softc *sc, struct wsdisplay_cursor *p)
714 {
715 #define	cc (&sc->sc_cursor)
716 	u_int v, index = 0, count = 0, icount = 0;
717 	uint8_t r[2], g[2], b[2], image[512], mask[512];
718 	int error, s;
719 
720 	v = p->which;
721 	if (v & WSDISPLAY_CURSOR_DOCMAP) {
722 		index = p->cmap.index;
723 		count = p->cmap.count;
724 		if (index >= 2 || (index + count) > 2)
725 			return (EINVAL);
726 		error = copyin(p->cmap.red, &r[index], count);
727 		if (error)
728 			return error;
729 		error = copyin(p->cmap.green, &g[index], count);
730 		if (error)
731 			return error;
732 		error = copyin(p->cmap.blue, &b[index], count);
733 		if (error)
734 			return error;
735 	}
736 	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
737 		if (p->size.x > CURSOR_MAX_SIZE || p->size.y > CURSOR_MAX_SIZE)
738 			return (EINVAL);
739 		icount = ((p->size.x < 33) ? 4 : 8) * p->size.y;
740 		error = copyin(p->image, image, icount);
741 		if (error)
742 			return error;
743 		error = copyin(p->mask, mask, icount);
744 		if (error)
745 			return error;
746 	}
747 
748 	s = spltty();
749 	if (v & WSDISPLAY_CURSOR_DOCUR)
750 		sc->sc_curenb = p->enable;
751 	if (v & WSDISPLAY_CURSOR_DOPOS)
752 		set_curpos(sc, &p->pos);
753 	if (v & WSDISPLAY_CURSOR_DOHOT)
754 		cc->cc_hot = p->hot;
755 	if (v & WSDISPLAY_CURSOR_DOCMAP) {
756 		memcpy(&cc->cc_color[index], &r[index], count);
757 		memcpy(&cc->cc_color[index + 2], &g[index], count);
758 		memcpy(&cc->cc_color[index + 4], &b[index], count);
759 	}
760 	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
761 		cc->cc_size = p->size;
762 		memset(cc->cc_image, 0, sizeof cc->cc_image);
763 		memcpy(cc->cc_image, image, icount);
764 		memset(cc->cc_mask, 0, sizeof cc->cc_mask);
765 		memcpy(cc->cc_mask, mask, icount);
766 	}
767 	sc->sc_changed |= v;
768 	splx(s);
769 
770 	return (0);
771 #undef cc
772 }
773 
774 static int
775 get_cursor(struct cfb_softc *sc, struct wsdisplay_cursor *p)
776 {
777 	return (EPASSTHROUGH); /* XXX */
778 }
779 
780 static void
781 set_curpos(struct cfb_softc *sc, struct wsdisplay_curpos *curpos)
782 {
783 	struct rasops_info *ri = sc->sc_ri;
784 	int x = curpos->x, y = curpos->y;
785 
786 	if (y < 0)
787 		y = 0;
788 	else if (y > ri->ri_height)
789 		y = ri->ri_height;
790 	if (x < 0)
791 		x = 0;
792 	else if (x > ri->ri_width)
793 		x = ri->ri_width;
794 	sc->sc_cursor.cc_pos.x = x;
795 	sc->sc_cursor.cc_pos.y = y;
796 }
797