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