cfb.c revision 1.13
1/* $NetBSD: cfb.c,v 1.13 1999/10/19 00:49:34 nisimura 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.13 1999/10/19 00:49:34 nisimura 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#include <vm/vm.h>
45
46#include <machine/bus.h>
47#include <machine/intr.h>
48
49#include <dev/rcons/raster.h>
50#include <dev/wscons/wsconsio.h>
51#include <dev/wscons/wscons_raster.h>
52#include <dev/wscons/wsdisplayvar.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
108struct 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	struct raster	dc_raster;	/* raster description */
118	struct rcons	dc_rcons;	/* raster blitter control info */
119	int	    dc_blanked;		/* currently has video disabled */
120};
121
122struct 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
129struct 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
139struct 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
162int  cfbmatch __P((struct device *, struct cfdata *, void *));
163void cfbattach __P((struct device *, struct device *, void *));
164
165struct cfattach cfb_ca = {
166	sizeof(struct cfb_softc), cfbmatch, cfbattach,
167};
168
169void cfb_getdevconfig __P((tc_addr_t, struct fb_devconfig *));
170struct fb_devconfig cfb_console_dc;
171tc_addr_t cfb_consaddr;
172
173struct wsdisplay_emulops cfb_emulops = {
174	rcons_cursor,			/* could use hardware cursor; punt */
175	rcons_mapchar,
176	rcons_putchar,
177	rcons_copycols,
178	rcons_erasecols,
179	rcons_copyrows,
180	rcons_eraserows,
181	rcons_alloc_attr
182};
183
184struct wsscreen_descr cfb_stdscreen = {
185	"std", 0, 0,
186	&cfb_emulops,
187	0, 0,
188	WSSCREEN_REVERSE
189};
190
191const struct wsscreen_descr *_cfb_scrlist[] = {
192	&cfb_stdscreen,
193};
194
195struct wsscreen_list cfb_screenlist = {
196	sizeof(_cfb_scrlist) / sizeof(struct wsscreen_descr *), _cfb_scrlist
197};
198
199int	cfbioctl __P((void *, u_long, caddr_t, int, struct proc *));
200int	cfbmmap __P((void *, off_t, int));
201
202int	cfb_alloc_screen __P((void *, const struct wsscreen_descr *,
203				      void **, int *, int *, long *));
204void	cfb_free_screen __P((void *, void *));
205void	cfb_show_screen __P((void *, void *));
206
207struct wsdisplay_accessops cfb_accessops = {
208	cfbioctl,
209	cfbmmap,
210	cfb_alloc_screen,
211	cfb_free_screen,
212	cfb_show_screen,
213	0 /* load_font */
214};
215
216int  cfb_cnattach __P((tc_addr_t));
217int  cfbintr __P((void *));
218void cfbinit __P((struct fb_devconfig *));
219
220static int  get_cmap __P((struct cfb_softc *, struct wsdisplay_cmap *));
221static int  set_cmap __P((struct cfb_softc *, struct wsdisplay_cmap *));
222static int  set_cursor __P((struct cfb_softc *, struct wsdisplay_cursor *));
223static int  get_cursor __P((struct cfb_softc *, struct wsdisplay_cursor *));
224static void set_curpos __P((struct cfb_softc *, struct wsdisplay_curpos *));
225static void bt459_set_curpos __P((struct cfb_softc *));
226
227/*
228 * Compose 2 bit/pixel cursor image.  Bit order will be reversed.
229 *   M M M M I I I I		M I M I M I M I
230 *	[ before ]		   [ after ]
231 *   3 2 1 0 3 2 1 0		0 0 1 1 2 2 3 3
232 *   7 6 5 4 7 6 5 4		4 4 5 5 6 6 7 7
233 */
234const static u_int8_t shuffle[256] = {
235	0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54,
236	0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55,
237	0x80, 0xc0, 0x90, 0xd0, 0x84, 0xc4, 0x94, 0xd4,
238	0x81, 0xc1, 0x91, 0xd1, 0x85, 0xc5, 0x95, 0xd5,
239	0x20, 0x60, 0x30, 0x70, 0x24, 0x64, 0x34, 0x74,
240	0x21, 0x61, 0x31, 0x71, 0x25, 0x65, 0x35, 0x75,
241	0xa0, 0xe0, 0xb0, 0xf0, 0xa4, 0xe4, 0xb4, 0xf4,
242	0xa1, 0xe1, 0xb1, 0xf1, 0xa5, 0xe5, 0xb5, 0xf5,
243	0x08, 0x48, 0x18, 0x58, 0x0c, 0x4c, 0x1c, 0x5c,
244	0x09, 0x49, 0x19, 0x59, 0x0d, 0x4d, 0x1d, 0x5d,
245	0x88, 0xc8, 0x98, 0xd8, 0x8c, 0xcc, 0x9c, 0xdc,
246	0x89, 0xc9, 0x99, 0xd9, 0x8d, 0xcd, 0x9d, 0xdd,
247	0x28, 0x68, 0x38, 0x78, 0x2c, 0x6c, 0x3c, 0x7c,
248	0x29, 0x69, 0x39, 0x79, 0x2d, 0x6d, 0x3d, 0x7d,
249	0xa8, 0xe8, 0xb8, 0xf8, 0xac, 0xec, 0xbc, 0xfc,
250	0xa9, 0xe9, 0xb9, 0xf9, 0xad, 0xed, 0xbd, 0xfd,
251	0x02, 0x42, 0x12, 0x52, 0x06, 0x46, 0x16, 0x56,
252	0x03, 0x43, 0x13, 0x53, 0x07, 0x47, 0x17, 0x57,
253	0x82, 0xc2, 0x92, 0xd2, 0x86, 0xc6, 0x96, 0xd6,
254	0x83, 0xc3, 0x93, 0xd3, 0x87, 0xc7, 0x97, 0xd7,
255	0x22, 0x62, 0x32, 0x72, 0x26, 0x66, 0x36, 0x76,
256	0x23, 0x63, 0x33, 0x73, 0x27, 0x67, 0x37, 0x77,
257	0xa2, 0xe2, 0xb2, 0xf2, 0xa6, 0xe6, 0xb6, 0xf6,
258	0xa3, 0xe3, 0xb3, 0xf3, 0xa7, 0xe7, 0xb7, 0xf7,
259	0x0a, 0x4a, 0x1a, 0x5a, 0x0e, 0x4e, 0x1e, 0x5e,
260	0x0b, 0x4b, 0x1b, 0x5b, 0x0f, 0x4f, 0x1f, 0x5f,
261	0x8a, 0xca, 0x9a, 0xda, 0x8e, 0xce, 0x9e, 0xde,
262	0x8b, 0xcb, 0x9b, 0xdb, 0x8f, 0xcf, 0x9f, 0xdf,
263	0x2a, 0x6a, 0x3a, 0x7a, 0x2e, 0x6e, 0x3e, 0x7e,
264	0x2b, 0x6b, 0x3b, 0x7b, 0x2f, 0x6f, 0x3f, 0x7f,
265	0xaa, 0xea, 0xba, 0xfa, 0xae, 0xee, 0xbe, 0xfe,
266	0xab, 0xeb, 0xbb, 0xfb, 0xaf, 0xef, 0xbf, 0xff,
267};
268
269int
270cfbmatch(parent, match, aux)
271	struct device *parent;
272	struct cfdata *match;
273	void *aux;
274{
275	struct tc_attach_args *ta = aux;
276
277	if (strncmp("PMAG-BA ", ta->ta_modname, TC_ROM_LLEN) != 0)
278		return (0);
279
280	return (1);
281}
282
283void
284cfb_getdevconfig(dense_addr, dc)
285	tc_addr_t dense_addr;
286	struct fb_devconfig *dc;
287{
288	struct raster *rap;
289	struct rcons *rcp;
290	int i;
291
292	dc->dc_vaddr = dense_addr;
293	dc->dc_paddr = MACHINE_KSEG0_TO_PHYS(dc->dc_vaddr + CX_FB_OFFSET);
294
295	dc->dc_wid = 1024;
296	dc->dc_ht = 864;
297	dc->dc_depth = 8;
298	dc->dc_rowbytes = 1024;
299	dc->dc_videobase = dc->dc_vaddr + CX_FB_OFFSET;
300	dc->dc_blanked = 0;
301
302	/* initialize colormap and cursor resource */
303	cfbinit(dc);
304
305	/* clear the screen */
306	for (i = 0; i < dc->dc_ht * dc->dc_rowbytes; i += sizeof(u_int32_t))
307		*(u_int32_t *)(dc->dc_videobase + i) = 0x0;
308
309	/* initialize the raster */
310	rap = &dc->dc_raster;
311	rap->width = dc->dc_wid;
312	rap->height = dc->dc_ht;
313	rap->depth = dc->dc_depth;
314	rap->linelongs = dc->dc_rowbytes / sizeof(u_int32_t);
315	rap->pixels = (u_int32_t *)dc->dc_videobase;
316
317	/* initialize the raster console blitter */
318	rcp = &dc->dc_rcons;
319	rcp->rc_sp = rap;
320	rcp->rc_crow = rcp->rc_ccol = -1;
321	rcp->rc_crowp = &rcp->rc_crow;
322	rcp->rc_ccolp = &rcp->rc_ccol;
323	rcons_init(rcp, 34, 80);
324
325	cfb_stdscreen.nrows = dc->dc_rcons.rc_maxrow;
326	cfb_stdscreen.ncols = dc->dc_rcons.rc_maxcol;
327}
328
329void
330cfbattach(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	struct hwcmap256 *cm;
338	int console;
339
340	console = (ta->ta_addr == cfb_consaddr);
341	if (console) {
342		sc->sc_dc = &cfb_console_dc;
343		sc->nscreens = 1;
344	}
345	else {
346		sc->sc_dc = (struct fb_devconfig *)
347		    malloc(sizeof(struct fb_devconfig), M_DEVBUF, M_WAITOK);
348		cfb_getdevconfig(ta->ta_addr, sc->sc_dc);
349	}
350	printf(": %d x %d, %dbpp\n", sc->sc_dc->dc_wid, sc->sc_dc->dc_ht,
351	    sc->sc_dc->dc_depth);
352
353	cm = &sc->sc_cmap;
354	memset(cm, 255, sizeof(struct hwcmap256));	/* XXX */
355	cm->r[0] = cm->g[0] = cm->b[0] = 0;		/* XXX */
356
357	sc->sc_cursor.cc_magic.x = CX_MAGIC_X;
358	sc->sc_cursor.cc_magic.y = CX_MAGIC_Y;
359
360	/* Establish an interrupt handler, and clear any pending interrupts */
361	tc_intr_establish(parent, ta->ta_cookie, TC_IPL_TTY, cfbintr, sc);
362	*(u_int8_t *)(sc->sc_dc->dc_vaddr + CX_OFFSET_IREQ) = 0;
363
364	waa.console = console;
365	waa.scrdata = &cfb_screenlist;
366	waa.accessops = &cfb_accessops;
367	waa.accesscookie = sc;
368
369	config_found(self, &waa, wsemuldisplaydevprint);
370}
371
372int
373cfbioctl(v, cmd, data, flag, p)
374	void *v;
375	u_long cmd;
376	caddr_t data;
377	int flag;
378	struct proc *p;
379{
380	struct cfb_softc *sc = v;
381	struct fb_devconfig *dc = sc->sc_dc;
382	int turnoff;
383
384	switch (cmd) {
385	case WSDISPLAYIO_GTYPE:
386		*(u_int *)data = WSDISPLAY_TYPE_CFB;
387		return (0);
388
389	case WSDISPLAYIO_GINFO:
390#define	wsd_fbip ((struct wsdisplay_fbinfo *)data)
391		wsd_fbip->height = sc->sc_dc->dc_ht;
392		wsd_fbip->width = sc->sc_dc->dc_wid;
393		wsd_fbip->depth = sc->sc_dc->dc_depth;
394		wsd_fbip->cmsize = CMAP_SIZE;
395#undef fbt
396		return (0);
397
398	case WSDISPLAYIO_GETCMAP:
399		return get_cmap(sc, (struct wsdisplay_cmap *)data);
400
401	case WSDISPLAYIO_PUTCMAP:
402		return set_cmap(sc, (struct wsdisplay_cmap *)data);
403
404	case WSDISPLAYIO_SVIDEO:
405		turnoff = *(int *)data == WSDISPLAYIO_VIDEO_OFF;
406		if ((dc->dc_blanked == 0) ^ turnoff) {
407			dc->dc_blanked = turnoff;
408			/* XXX later XXX */
409		}
410		return (0);
411
412	case WSDISPLAYIO_GVIDEO:
413		*(u_int *)data = dc->dc_blanked ?
414		    WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON;
415		return (0);
416
417	case WSDISPLAYIO_GCURPOS:
418		*(struct wsdisplay_curpos *)data = sc->sc_cursor.cc_pos;
419		return (0);
420
421	case WSDISPLAYIO_SCURPOS:
422		set_curpos(sc, (struct wsdisplay_curpos *)data);
423		bt459_set_curpos(sc);
424		return (0);
425
426	case WSDISPLAYIO_GCURMAX:
427		((struct wsdisplay_curpos *)data)->x =
428		((struct wsdisplay_curpos *)data)->y = CURSOR_MAX_SIZE;
429		return (0);
430
431	case WSDISPLAYIO_GCURSOR:
432		return get_cursor(sc, (struct wsdisplay_cursor *)data);
433
434	case WSDISPLAYIO_SCURSOR:
435		return set_cursor(sc, (struct wsdisplay_cursor *)data);
436	}
437	return ENOTTY;
438}
439
440int
441cfbmmap(v, offset, prot)
442	void *v;
443	off_t offset;
444	int prot;
445{
446	struct cfb_softc *sc = v;
447
448	if (offset >= CX_FB_SIZE || offset < 0)
449		return (-1);
450	return machine_btop(sc->sc_dc->dc_paddr + offset);
451}
452
453int
454cfb_alloc_screen(v, type, cookiep, curxp, curyp, attrp)
455	void *v;
456	const struct wsscreen_descr *type;
457	void **cookiep;
458	int *curxp, *curyp;
459	long *attrp;
460{
461	struct cfb_softc *sc = v;
462	long defattr;
463
464	if (sc->nscreens > 0)
465		return (ENOMEM);
466
467	*cookiep = &sc->sc_dc->dc_rcons; /* one and only for now */
468	*curxp = 0;
469	*curyp = 0;
470	rcons_alloc_attr(&sc->sc_dc->dc_rcons, 0, 0, 0, &defattr);
471	*attrp = defattr;
472	sc->nscreens++;
473	return (0);
474}
475
476void
477cfb_free_screen(v, cookie)
478	void *v;
479	void *cookie;
480{
481	struct cfb_softc *sc = v;
482
483	if (sc->sc_dc == &cfb_console_dc)
484		panic("cfb_free_screen: console");
485
486	sc->nscreens--;
487}
488
489void
490cfb_show_screen(v, cookie)
491	void *v;
492	void *cookie;
493{
494}
495
496int
497cfb_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
505        rcons_alloc_attr(&dcp->dc_rcons, 0, 0, 0, &defattr);
506
507        wsdisplay_cnattach(&cfb_stdscreen, &dcp->dc_rcons,
508                           0, 0, defattr);
509        cfb_consaddr = addr;
510        return(0);
511}
512
513
514int
515cfbintr(arg)
516	void *arg;
517{
518	struct cfb_softc *sc = arg;
519	caddr_t cfbbase = (caddr_t)sc->sc_dc->dc_vaddr;
520	struct bt459reg *vdac;
521	int v;
522
523	*(u_int8_t *)(cfbbase + CX_OFFSET_IREQ) = 0;
524	if (sc->sc_changed == 0)
525		return (1);
526
527	vdac = (void *)(cfbbase + CX_BT459_OFFSET);
528	v = sc->sc_changed;
529	sc->sc_changed = 0;
530	if (v & DATA_ENB_CHANGED) {
531		SELECT(vdac, BT459_REG_CCR);
532		REG(vdac, bt_reg) = (sc->sc_curenb) ? 0xc0 : 0x00;
533	}
534	if (v & DATA_CURCMAP_CHANGED) {
535		u_int8_t *cp = sc->sc_cursor.cc_color;
536
537		SELECT(vdac, BT459_REG_CCOLOR_2);
538		REG(vdac, bt_reg) = cp[1];	tc_wmb();
539		REG(vdac, bt_reg) = cp[3];	tc_wmb();
540		REG(vdac, bt_reg) = cp[5];	tc_wmb();
541
542		REG(vdac, bt_reg) = cp[0];	tc_wmb();
543		REG(vdac, bt_reg) = cp[2];	tc_wmb();
544		REG(vdac, bt_reg) = cp[4];	tc_wmb();
545	}
546	if (v & DATA_CURSHAPE_CHANGED) {
547		u_int8_t *ip, *mp, img, msk;
548		u_int8_t u;
549		int bcnt;
550
551		ip = (u_int8_t *)sc->sc_cursor.cc_image;
552		mp = (u_int8_t *)(sc->sc_cursor.cc_image + CURSOR_MAX_SIZE);
553
554		bcnt = 0;
555		SELECT(vdac, BT459_REG_CRAM_BASE+0);
556		/* 64 pixel scan line is consisted with 16 byte cursor ram */
557		while (bcnt < sc->sc_cursor.cc_size.y * 16) {
558			/* pad right half 32 pixel when smaller than 33 */
559			if ((bcnt & 0x8) && sc->sc_cursor.cc_size.x < 33) {
560				REG(vdac, bt_reg) = 0; tc_wmb();
561				REG(vdac, bt_reg) = 0; tc_wmb();
562			}
563			else {
564				img = *ip++;
565				msk = *mp++;
566				img &= msk;	/* cookie off image */
567				u = (msk & 0x0f) << 4 | (img & 0x0f);
568				REG(vdac, bt_reg) = shuffle[u];	tc_wmb();
569				u = (msk & 0xf0) | (img & 0xf0) >> 4;
570				REG(vdac, bt_reg) = shuffle[u];	tc_wmb();
571			}
572			bcnt += 2;
573		}
574		/* pad unoccupied scan lines */
575		while (bcnt < CURSOR_MAX_SIZE * 16) {
576			REG(vdac, bt_reg) = 0; tc_wmb();
577			REG(vdac, bt_reg) = 0; tc_wmb();
578			bcnt += 2;
579		}
580	}
581	if (v & DATA_CMAP_CHANGED) {
582		struct hwcmap256 *cm = &sc->sc_cmap;
583		int index;
584
585		SELECT(vdac, 0);
586		for (index = 0; index < CMAP_SIZE; index++) {
587			REG(vdac, bt_cmap) = cm->r[index];	tc_wmb();
588			REG(vdac, bt_cmap) = cm->g[index];	tc_wmb();
589			REG(vdac, bt_cmap) = cm->b[index];	tc_wmb();
590		}
591	}
592	return (1);
593}
594
595void
596cfbinit(dc)
597	struct fb_devconfig *dc;
598{
599	caddr_t cfbbase = (caddr_t)dc->dc_vaddr;
600	struct bt459reg *vdac = (void *)(cfbbase + CX_BT459_OFFSET);
601	int i;
602
603	SELECT(vdac, BT459_REG_COMMAND_0);
604	REG(vdac, bt_reg) = 0x40; /* CMD0 */	tc_wmb();
605	REG(vdac, bt_reg) = 0x0;  /* CMD1 */	tc_wmb();
606	REG(vdac, bt_reg) = 0xc0; /* CMD2 */	tc_wmb();
607	REG(vdac, bt_reg) = 0xff; /* PRM */	tc_wmb();
608	REG(vdac, bt_reg) = 0;    /* 205 */	tc_wmb();
609	REG(vdac, bt_reg) = 0x0;  /* PBM */	tc_wmb();
610	REG(vdac, bt_reg) = 0;    /* 207 */	tc_wmb();
611	REG(vdac, bt_reg) = 0x0;  /* ORM */	tc_wmb();
612	REG(vdac, bt_reg) = 0x0;  /* OBM */	tc_wmb();
613	REG(vdac, bt_reg) = 0x0;  /* ILV */	tc_wmb();
614	REG(vdac, bt_reg) = 0x0;  /* TEST */	tc_wmb();
615
616	SELECT(vdac, BT459_REG_CCR);
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	REG(vdac, bt_reg) = 0x0;	tc_wmb();
629	REG(vdac, bt_reg) = 0x0;	tc_wmb();
630
631	/* build sane colormap */
632	SELECT(vdac, 0);
633	REG(vdac, bt_cmap) = 0;	 tc_wmb();
634	REG(vdac, bt_cmap) = 0;	 tc_wmb();
635	REG(vdac, bt_cmap) = 0;	 tc_wmb();
636	for (i = 1; i < CMAP_SIZE; i++) {
637		REG(vdac, bt_cmap) = 0xff;	tc_wmb();
638		REG(vdac, bt_cmap) = 0xff;	tc_wmb();
639		REG(vdac, bt_cmap) = 0xff;	tc_wmb();
640	}
641
642	/* clear out cursor image */
643	SELECT(vdac, BT459_REG_CRAM_BASE);
644	for (i = 0; i < 1024; i++)
645		REG(vdac, bt_reg) = 0xff;	tc_wmb();
646
647	/*
648	 * 2 bit/pixel cursor.  Assign MSB for cursor mask and LSB for
649	 * cursor image.  CCOLOR_2 for mask color, while CCOLOR_3 for
650	 * image color.  CCOLOR_1 will be never used.
651	 */
652	SELECT(vdac, BT459_REG_CCOLOR_1);
653	REG(vdac, bt_reg) = 0xff;	tc_wmb();
654	REG(vdac, bt_reg) = 0xff;	tc_wmb();
655	REG(vdac, bt_reg) = 0xff;	tc_wmb();
656
657	REG(vdac, bt_reg) = 0;	tc_wmb();
658	REG(vdac, bt_reg) = 0;	tc_wmb();
659	REG(vdac, bt_reg) = 0;	tc_wmb();
660
661	REG(vdac, bt_reg) = 0xff;	tc_wmb();
662	REG(vdac, bt_reg) = 0xff;	tc_wmb();
663	REG(vdac, bt_reg) = 0xff;	tc_wmb();
664}
665
666static int
667get_cmap(sc, p)
668	struct cfb_softc *sc;
669	struct wsdisplay_cmap *p;
670{
671	u_int index = p->index, count = p->count;
672
673	if (index >= CMAP_SIZE || (index + count) > CMAP_SIZE)
674		return (EINVAL);
675
676	if (!uvm_useracc(p->red, count, B_WRITE) ||
677	    !uvm_useracc(p->green, count, B_WRITE) ||
678	    !uvm_useracc(p->blue, count, B_WRITE))
679		return (EFAULT);
680
681	copyout(&sc->sc_cmap.r[index], p->red, count);
682	copyout(&sc->sc_cmap.g[index], p->green, count);
683	copyout(&sc->sc_cmap.b[index], p->blue, count);
684
685	return (0);
686}
687
688static int
689set_cmap(sc, p)
690	struct cfb_softc *sc;
691	struct wsdisplay_cmap *p;
692{
693	u_int index = p->index, count = p->count;
694
695	if (index >= CMAP_SIZE || (index + count) > CMAP_SIZE)
696		return (EINVAL);
697
698	if (!uvm_useracc(p->red, count, B_READ) ||
699	    !uvm_useracc(p->green, count, B_READ) ||
700	    !uvm_useracc(p->blue, count, B_READ))
701		return (EFAULT);
702
703	copyin(p->red, &sc->sc_cmap.r[index], count);
704	copyin(p->green, &sc->sc_cmap.g[index], count);
705	copyin(p->blue, &sc->sc_cmap.b[index], count);
706
707	sc->sc_changed |= DATA_CMAP_CHANGED;
708
709	return (0);
710}
711
712static int
713set_cursor(sc, p)
714	struct cfb_softc *sc;
715	struct wsdisplay_cursor *p;
716{
717#define	cc (&sc->sc_cursor)
718	int v, index, count, icount;
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		if (!uvm_useracc(p->cmap.red, count, B_READ) ||
727		    !uvm_useracc(p->cmap.green, count, B_READ) ||
728		    !uvm_useracc(p->cmap.blue, count, B_READ))
729			return (EFAULT);
730	}
731	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
732		if (p->size.x > CURSOR_MAX_SIZE || p->size.y > CURSOR_MAX_SIZE)
733			return (EINVAL);
734		icount = ((p->size.x < 33) ? 4 : 8) * p->size.y;
735		if (!uvm_useracc(p->image, icount, B_READ) ||
736		    !uvm_useracc(p->mask, icount, B_READ))
737			return (EFAULT);
738	}
739	if (v & (WSDISPLAY_CURSOR_DOPOS | WSDISPLAY_CURSOR_DOCUR)) {
740		if (v & WSDISPLAY_CURSOR_DOCUR)
741			cc->cc_hot = p->hot;
742		if (v & WSDISPLAY_CURSOR_DOPOS)
743			set_curpos(sc, &p->pos);
744		bt459_set_curpos(sc);
745	}
746
747	sc->sc_changed = 0;
748	if (v & WSDISPLAY_CURSOR_DOCUR) {
749		sc->sc_curenb = p->enable;
750		sc->sc_changed |= DATA_ENB_CHANGED;
751	}
752	if (v & WSDISPLAY_CURSOR_DOCMAP) {
753		copyin(p->cmap.red, &cc->cc_color[index], count);
754		copyin(p->cmap.green, &cc->cc_color[index + 2], count);
755		copyin(p->cmap.blue, &cc->cc_color[index + 4], count);
756		sc->sc_changed |= DATA_CURCMAP_CHANGED;
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		sc->sc_changed |= DATA_CURSHAPE_CHANGED;
764	}
765
766	return (0);
767#undef cc
768}
769
770static int
771get_cursor(sc, p)
772	struct cfb_softc *sc;
773	struct wsdisplay_cursor *p;
774{
775	return (ENOTTY); /* XXX */
776}
777
778static void
779set_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
798void
799bt459_set_curpos(sc)
800	struct cfb_softc *sc;
801{
802	caddr_t cfbbase = (caddr_t)sc->sc_dc->dc_vaddr;
803	struct bt459reg *vdac = (void *)(cfbbase + CX_BT459_OFFSET);
804	int x, y, s;
805
806	x = sc->sc_cursor.cc_pos.x - sc->sc_cursor.cc_hot.x;
807	y = sc->sc_cursor.cc_pos.y - sc->sc_cursor.cc_hot.y;
808
809	x += sc->sc_cursor.cc_magic.x;
810	y += sc->sc_cursor.cc_magic.y;
811
812	s = spltty();
813
814	SELECT(vdac, BT459_REG_CURSOR_X_LOW);
815	REG(vdac, bt_reg) = x;		tc_wmb();
816	REG(vdac, bt_reg) = x >> 8;	tc_wmb();
817	REG(vdac, bt_reg) = y;		tc_wmb();
818	REG(vdac, bt_reg) = y >> 8;	tc_wmb();
819
820	splx(s);
821}
822