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