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