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