cfb.c revision 1.9
1/* $NetBSD: cfb.c,v 1.9 1999/03/24 05:51:21 mrg 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.9 1999/03/24 05:51: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#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#include <machine/autoconf.h>
54
55#include <dev/tc/tcvar.h>
56#include <dev/ic/bt459reg.h>
57
58#include <uvm/uvm_extern.h>
59
60/* XXX BUS'IFYING XXX */
61
62#if defined(pmax)
63#define	machine_btop(x) mips_btop(x)
64#define	MACHINE_KSEG0_TO_PHYS(x) MIPS_KSEG1_TO_PHYS(x)
65#endif
66
67#if defined(__alpha__) || defined(alpha)
68/*
69 * Digital UNIX never supports PMAG-BA
70 */
71#define machine_btop(x) alpha_btop(x)
72#define MACHINE_KSEG0_TO_PHYS(x) ALPHA_K0SEG_TO_PHYS(x)
73#endif
74
75/*
76 * N.B., Bt459 registers are 8bit width.  Some of TC framebuffers have
77 * obscure register layout such as 2nd and 3rd Bt459 registers are
78 * adjacent each other in a word, i.e.,
79 *	struct bt459triplet {
80 * 		struct {
81 *			u_int8_t u0;
82 *			u_int8_t u1;
83 *			u_int8_t u2;
84 *			unsigned :8;
85 *		} bt_lo;
86 *		...
87 * Although CX has single Bt459, 32bit R/W can be done w/o any trouble.
88 */
89struct bt459reg {
90        u_int32_t       bt_lo;
91        u_int32_t       bt_hi;
92        u_int32_t       bt_reg;
93        u_int32_t       bt_cmap;
94};
95
96/* XXX XXX XXX */
97
98struct fb_devconfig {
99	vaddr_t dc_vaddr;		/* memory space virtual base address */
100	paddr_t dc_paddr;		/* memory space physical base address */
101	vsize_t	dc_size;		/* size of slot memory */
102	int	dc_wid;			/* width of frame buffer */
103	int	dc_ht;			/* height of frame buffer */
104	int	dc_depth;		/* depth, bits per pixel */
105	int	dc_rowbytes;		/* bytes in a FB scan line */
106	vaddr_t dc_videobase;		/* base of flat frame buffer */
107	struct raster	dc_raster;	/* raster description */
108	struct rcons	dc_rcons;	/* raster blitter control info */
109	int	    dc_blanked;		/* currently has video disabled */
110};
111
112struct hwcmap {
113#define	CMAP_SIZE	256	/* 256 R/G/B entries */
114	u_int8_t r[CMAP_SIZE];
115	u_int8_t g[CMAP_SIZE];
116	u_int8_t b[CMAP_SIZE];
117};
118
119struct hwcursor {
120	struct wsdisplay_curpos cc_pos;
121	struct wsdisplay_curpos cc_hot;
122	struct wsdisplay_curpos cc_size;
123#define	CURSOR_MAX_SIZE	64
124	u_int8_t cc_color[6];
125	u_int64_t cc_image[64 + 64];
126};
127
128struct cfb_softc {
129	struct device sc_dev;
130	struct fb_devconfig *sc_dc;	/* device configuration */
131	struct hwcmap sc_cmap;		/* software copy of colormap */
132	struct hwcursor sc_cursor;	/* software copy of cursor */
133	int sc_curenb;			/* cursor sprite enabled */
134	int sc_changed;			/* need update of colormap */
135#define	DATA_ENB_CHANGED	0x01	/* cursor enable changed */
136#define	DATA_CURCMAP_CHANGED	0x02	/* cursor colormap changed */
137#define	DATA_CURSHAPE_CHANGED	0x04	/* cursor size, image, mask changed */
138#define	DATA_CMAP_CHANGED	0x08	/* colormap changed */
139#define	DATA_ALL_CHANGED	0x0f
140	int nscreens;
141	short magic_x, magic_y;		/* CX cursor location offset */
142#define	CX_MAGIC_X 220
143#define	CX_MAGIC_Y 35
144};
145
146#define	CX_FB_OFFSET	0x000000
147#define	CX_FB_SIZE	0x100000
148#define	CX_BT459_OFFSET	0x200000
149#define	CX_OFFSET_IREQ	0x300000	/* Interrupt req. control */
150
151int  cfbmatch __P((struct device *, struct cfdata *, void *));
152void cfbattach __P((struct device *, struct device *, void *));
153
154struct cfattach cfb_ca = {
155	sizeof(struct cfb_softc), cfbmatch, cfbattach,
156};
157
158void cfb_getdevconfig __P((tc_addr_t, struct fb_devconfig *));
159struct fb_devconfig cfb_console_dc;
160tc_addr_t cfb_consaddr;
161
162struct wsdisplay_emulops cfb_emulops = {
163	rcons_cursor,			/* could use hardware cursor; punt */
164	rcons_mapchar,
165	rcons_putchar,
166	rcons_copycols,
167	rcons_erasecols,
168	rcons_copyrows,
169	rcons_eraserows,
170	rcons_alloc_attr
171};
172
173struct wsscreen_descr cfb_stdscreen = {
174	"std", 0, 0,
175	&cfb_emulops,
176	0, 0,
177	0
178};
179
180const struct wsscreen_descr *_cfb_scrlist[] = {
181	&cfb_stdscreen,
182};
183
184struct wsscreen_list cfb_screenlist = {
185	sizeof(_cfb_scrlist) / sizeof(struct wsscreen_descr *), _cfb_scrlist
186};
187
188int	cfbioctl __P((void *, u_long, caddr_t, int, struct proc *));
189int	cfbmmap __P((void *, off_t, int));
190
191int	cfb_alloc_screen __P((void *, const struct wsscreen_descr *,
192				      void **, int *, int *, long *));
193void	cfb_free_screen __P((void *, void *));
194void	cfb_show_screen __P((void *, void *));
195
196struct wsdisplay_accessops cfb_accessops = {
197	cfbioctl,
198	cfbmmap,
199	cfb_alloc_screen,
200	cfb_free_screen,
201	cfb_show_screen,
202	0 /* load_font */
203};
204
205int  cfb_cnattach __P((tc_addr_t));
206int  cfbintr __P((void *));
207void cfbinit __P((struct fb_devconfig *));
208
209static int  get_cmap __P((struct cfb_softc *, struct wsdisplay_cmap *));
210static int  set_cmap __P((struct cfb_softc *, struct wsdisplay_cmap *));
211static int  set_cursor __P((struct cfb_softc *, struct wsdisplay_cursor *));
212static int  get_cursor __P((struct cfb_softc *, struct wsdisplay_cursor *));
213static void set_curpos __P((struct cfb_softc *, struct wsdisplay_curpos *));
214static void bt459_set_curpos __P((struct cfb_softc *));
215
216/* XXX XXX XXX */
217#define	BT459_SELECT(vdac, regno) do {		\
218	vdac->bt_lo = (regno) & 0x00ff;		\
219	vdac->bt_hi = ((regno)& 0x0f00) >> 8;	\
220	tc_wmb();				\
221   } while (0)
222/* XXX XXX XXX */
223
224/*
225 * Compose 2 bit/pixel cursor image.  Bit order will be reversed.
226 *   M M M M I I I I		M I M I M I M I
227 *	[ before ]		   [ after ]
228 *   3 2 1 0 3 2 1 0		0 0 1 1 2 2 3 3
229 *   7 6 5 4 7 6 5 4		4 4 5 5 6 6 7 7
230 */
231const static u_int8_t shuffle[256] = {
232	0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54,
233	0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55,
234	0x80, 0xc0, 0x90, 0xd0, 0x84, 0xc4, 0x94, 0xd4,
235	0x81, 0xc1, 0x91, 0xd1, 0x85, 0xc5, 0x95, 0xd5,
236	0x20, 0x60, 0x30, 0x70, 0x24, 0x64, 0x34, 0x74,
237	0x21, 0x61, 0x31, 0x71, 0x25, 0x65, 0x35, 0x75,
238	0xa0, 0xe0, 0xb0, 0xf0, 0xa4, 0xe4, 0xb4, 0xf4,
239	0xa1, 0xe1, 0xb1, 0xf1, 0xa5, 0xe5, 0xb5, 0xf5,
240	0x08, 0x48, 0x18, 0x58, 0x0c, 0x4c, 0x1c, 0x5c,
241	0x09, 0x49, 0x19, 0x59, 0x0d, 0x4d, 0x1d, 0x5d,
242	0x88, 0xc8, 0x98, 0xd8, 0x8c, 0xcc, 0x9c, 0xdc,
243	0x89, 0xc9, 0x99, 0xd9, 0x8d, 0xcd, 0x9d, 0xdd,
244	0x28, 0x68, 0x38, 0x78, 0x2c, 0x6c, 0x3c, 0x7c,
245	0x29, 0x69, 0x39, 0x79, 0x2d, 0x6d, 0x3d, 0x7d,
246	0xa8, 0xe8, 0xb8, 0xf8, 0xac, 0xec, 0xbc, 0xfc,
247	0xa9, 0xe9, 0xb9, 0xf9, 0xad, 0xed, 0xbd, 0xfd,
248	0x02, 0x42, 0x12, 0x52, 0x06, 0x46, 0x16, 0x56,
249	0x03, 0x43, 0x13, 0x53, 0x07, 0x47, 0x17, 0x57,
250	0x82, 0xc2, 0x92, 0xd2, 0x86, 0xc6, 0x96, 0xd6,
251	0x83, 0xc3, 0x93, 0xd3, 0x87, 0xc7, 0x97, 0xd7,
252	0x22, 0x62, 0x32, 0x72, 0x26, 0x66, 0x36, 0x76,
253	0x23, 0x63, 0x33, 0x73, 0x27, 0x67, 0x37, 0x77,
254	0xa2, 0xe2, 0xb2, 0xf2, 0xa6, 0xe6, 0xb6, 0xf6,
255	0xa3, 0xe3, 0xb3, 0xf3, 0xa7, 0xe7, 0xb7, 0xf7,
256	0x0a, 0x4a, 0x1a, 0x5a, 0x0e, 0x4e, 0x1e, 0x5e,
257	0x0b, 0x4b, 0x1b, 0x5b, 0x0f, 0x4f, 0x1f, 0x5f,
258	0x8a, 0xca, 0x9a, 0xda, 0x8e, 0xce, 0x9e, 0xde,
259	0x8b, 0xcb, 0x9b, 0xdb, 0x8f, 0xcf, 0x9f, 0xdf,
260	0x2a, 0x6a, 0x3a, 0x7a, 0x2e, 0x6e, 0x3e, 0x7e,
261	0x2b, 0x6b, 0x3b, 0x7b, 0x2f, 0x6f, 0x3f, 0x7f,
262	0xaa, 0xea, 0xba, 0xfa, 0xae, 0xee, 0xbe, 0xfe,
263	0xab, 0xeb, 0xbb, 0xfb, 0xaf, 0xef, 0xbf, 0xff,
264};
265
266int
267cfbmatch(parent, match, aux)
268	struct device *parent;
269	struct cfdata *match;
270	void *aux;
271{
272	struct tc_attach_args *ta = aux;
273
274	if (strncmp("PMAG-BA ", ta->ta_modname, TC_ROM_LLEN) != 0)
275		return (0);
276
277	return (1);
278}
279
280void
281cfb_getdevconfig(dense_addr, dc)
282	tc_addr_t dense_addr;
283	struct fb_devconfig *dc;
284{
285	struct raster *rap;
286	struct rcons *rcp;
287	int i;
288
289	dc->dc_vaddr = dense_addr;
290	dc->dc_paddr = MACHINE_KSEG0_TO_PHYS(dc->dc_vaddr + CX_FB_OFFSET);
291
292	dc->dc_wid = 1024;
293	dc->dc_ht = 864;
294	dc->dc_depth = 8;
295	dc->dc_rowbytes = 1024;
296	dc->dc_videobase = dc->dc_vaddr + CX_FB_OFFSET;
297	dc->dc_blanked = 0;
298
299	/* initialize colormap and cursor resource */
300	cfbinit(dc);
301
302	/* clear the screen */
303	for (i = 0; i < dc->dc_ht * dc->dc_rowbytes; i += sizeof(u_int32_t))
304		*(u_int32_t *)(dc->dc_videobase + i) = 0x0;
305
306	/* initialize the raster */
307	rap = &dc->dc_raster;
308	rap->width = dc->dc_wid;
309	rap->height = dc->dc_ht;
310	rap->depth = dc->dc_depth;
311	rap->linelongs = dc->dc_rowbytes / sizeof(u_int32_t);
312	rap->pixels = (u_int32_t *)dc->dc_videobase;
313
314	/* initialize the raster console blitter */
315	rcp = &dc->dc_rcons;
316	rcp->rc_sp = rap;
317	rcp->rc_crow = rcp->rc_ccol = -1;
318	rcp->rc_crowp = &rcp->rc_crow;
319	rcp->rc_ccolp = &rcp->rc_ccol;
320	rcons_init(rcp, 34, 80);
321
322	cfb_stdscreen.nrows = dc->dc_rcons.rc_maxrow;
323	cfb_stdscreen.ncols = dc->dc_rcons.rc_maxcol;
324}
325
326void
327cfbattach(parent, self, aux)
328	struct device *parent, *self;
329	void *aux;
330{
331	struct cfb_softc *sc = (struct cfb_softc *)self;
332	struct tc_attach_args *ta = aux;
333	struct wsemuldisplaydev_attach_args waa;
334	struct hwcmap *cm;
335	int console, i;
336
337	console = (ta->ta_addr == cfb_consaddr);
338	if (console) {
339		sc->sc_dc = &cfb_console_dc;
340		sc->nscreens = 1;
341	}
342	else {
343		sc->sc_dc = (struct fb_devconfig *)
344		    malloc(sizeof(struct fb_devconfig), M_DEVBUF, M_WAITOK);
345		cfb_getdevconfig(ta->ta_addr, sc->sc_dc);
346	}
347	printf(": %d x %d, %dbpp\n", sc->sc_dc->dc_wid, sc->sc_dc->dc_ht,
348	    sc->sc_dc->dc_depth);
349
350	cm = &sc->sc_cmap;
351	cm->r[0] = cm->g[0] = cm->b[0] = 0;
352	for (i = 1; i < CMAP_SIZE; i++) {
353		cm->r[i] = cm->g[i] = cm->b[i] = 0xff;
354	}
355	sc->magic_x = CX_MAGIC_X;
356	sc->magic_y = CX_MAGIC_Y;
357
358	/* Establish an interrupt handler, and clear any pending interrupts */
359        tc_intr_establish(parent, ta->ta_cookie, TC_IPL_TTY, cfbintr, sc);
360	*(u_int8_t *)(sc->sc_dc->dc_vaddr + CX_OFFSET_IREQ) = 0;
361
362	waa.console = console;
363	waa.scrdata = &cfb_screenlist;
364	waa.accessops = &cfb_accessops;
365	waa.accesscookie = sc;
366
367	config_found(self, &waa, wsemuldisplaydevprint);
368}
369
370int
371cfbioctl(v, cmd, data, flag, p)
372	void *v;
373	u_long cmd;
374	caddr_t data;
375	int flag;
376	struct proc *p;
377{
378	struct cfb_softc *sc = v;
379	struct fb_devconfig *dc = sc->sc_dc;
380	int turnoff;
381
382	switch (cmd) {
383	case WSDISPLAYIO_GTYPE:
384		*(u_int *)data = WSDISPLAY_TYPE_CFB;
385		return (0);
386
387	case WSDISPLAYIO_GINFO:
388#define	wsd_fbip ((struct wsdisplay_fbinfo *)data)
389		wsd_fbip->height = sc->sc_dc->dc_ht;
390		wsd_fbip->width = sc->sc_dc->dc_wid;
391		wsd_fbip->depth = sc->sc_dc->dc_depth;
392		wsd_fbip->cmsize = CMAP_SIZE;
393#undef fbt
394		return (0);
395
396	case WSDISPLAYIO_GETCMAP:
397		return get_cmap(sc, (struct wsdisplay_cmap *)data);
398
399	case WSDISPLAYIO_PUTCMAP:
400		return set_cmap(sc, (struct wsdisplay_cmap *)data);
401
402	case WSDISPLAYIO_SVIDEO:
403		turnoff = *(int *)data == WSDISPLAYIO_VIDEO_OFF;
404		if ((dc->dc_blanked == 0) ^ turnoff) {
405			dc->dc_blanked = turnoff;
406			/* XXX later XXX */
407		}
408		return (0);
409
410	case WSDISPLAYIO_GVIDEO:
411		*(u_int *)data = dc->dc_blanked ?
412		    WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON;
413		return (0);
414
415	case WSDISPLAYIO_GCURPOS:
416		*(struct wsdisplay_curpos *)data = sc->sc_cursor.cc_pos;
417		return (0);
418
419	case WSDISPLAYIO_SCURPOS:
420		set_curpos(sc, (struct wsdisplay_curpos *)data);
421		bt459_set_curpos(sc);
422		return (0);
423
424	case WSDISPLAYIO_GCURMAX:
425		((struct wsdisplay_curpos *)data)->x =
426		((struct wsdisplay_curpos *)data)->y = CURSOR_MAX_SIZE;
427		return (0);
428
429	case WSDISPLAYIO_GCURSOR:
430		return get_cursor(sc, (struct wsdisplay_cursor *)data);
431
432	case WSDISPLAYIO_SCURSOR:
433		return set_cursor(sc, (struct wsdisplay_cursor *)data);
434	}
435	return ENOTTY;
436}
437
438int
439cfbmmap(v, offset, prot)
440	void *v;
441	off_t offset;
442	int prot;
443{
444	struct cfb_softc *sc = v;
445
446	if (offset >= CX_FB_SIZE || offset < 0)
447		return (-1);
448	return machine_btop(sc->sc_dc->dc_paddr + offset);
449}
450
451int
452cfb_alloc_screen(v, type, cookiep, curxp, curyp, attrp)
453	void *v;
454	const struct wsscreen_descr *type;
455	void **cookiep;
456	int *curxp, *curyp;
457	long *attrp;
458{
459	struct cfb_softc *sc = v;
460	long defattr;
461
462	if (sc->nscreens > 0)
463		return (ENOMEM);
464
465	*cookiep = &sc->sc_dc->dc_rcons; /* one and only for now */
466	*curxp = 0;
467	*curyp = 0;
468	rcons_alloc_attr(&sc->sc_dc->dc_rcons, 0, 0, 0, &defattr);
469	*attrp = defattr;
470	sc->nscreens++;
471	return (0);
472}
473
474void
475cfb_free_screen(v, cookie)
476	void *v;
477	void *cookie;
478{
479	struct cfb_softc *sc = v;
480
481	if (sc->sc_dc == &cfb_console_dc)
482		panic("cfb_free_screen: console");
483
484	sc->nscreens--;
485}
486
487void
488cfb_show_screen(v, cookie)
489	void *v;
490	void *cookie;
491{
492}
493
494int
495cfb_cnattach(addr)
496        tc_addr_t addr;
497{
498        struct fb_devconfig *dcp = &cfb_console_dc;
499        long defattr;
500
501        cfb_getdevconfig(addr, dcp);
502
503        rcons_alloc_attr(&dcp->dc_rcons, 0, 0, 0, &defattr);
504
505        wsdisplay_cnattach(&cfb_stdscreen, &dcp->dc_rcons,
506                           0, 0, defattr);
507        cfb_consaddr = addr;
508        return(0);
509}
510
511
512int
513cfbintr(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		BT459_SELECT(vdac, BT459_REG_CCR);
530		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		BT459_SELECT(vdac, BT459_REG_CCOLOR_2);
536		vdac->bt_reg = cp[1];	tc_wmb();
537		vdac->bt_reg = cp[3];	tc_wmb();
538		vdac->bt_reg = cp[5];	tc_wmb();
539
540		vdac->bt_reg = cp[0];	tc_wmb();
541		vdac->bt_reg = cp[2];	tc_wmb();
542		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		BT459_SELECT(vdac, BT459_REG_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				vdac->bt_reg = 0; tc_wmb();
559				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				vdac->bt_reg = shuffle[u];	tc_wmb();
567				u = (msk & 0xf0) | (img & 0xf0) >> 4;
568				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			vdac->bt_reg = 0; tc_wmb();
575			vdac->bt_reg = 0; tc_wmb();
576			bcnt += 2;
577		}
578	}
579	if (v & DATA_CMAP_CHANGED) {
580		struct hwcmap *cm = &sc->sc_cmap;
581		int index;
582
583		BT459_SELECT(vdac, 0);
584		for (index = 0; index < CMAP_SIZE; index++) {
585			vdac->bt_cmap = cm->r[index];	tc_wmb();
586			vdac->bt_cmap = cm->g[index];	tc_wmb();
587			vdac->bt_cmap = cm->b[index];	tc_wmb();
588		}
589	}
590	return (1);
591}
592
593void
594cfbinit(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	BT459_SELECT(vdac, BT459_REG_COMMAND_0);
602	vdac->bt_reg = 0x40; /* CMD0 */	tc_wmb();
603	vdac->bt_reg = 0x0;  /* CMD1 */	tc_wmb();
604	vdac->bt_reg = 0xc0; /* CMD2 */	tc_wmb();
605	vdac->bt_reg = 0xff; /* PRM */	tc_wmb();
606	vdac->bt_reg = 0;    /* 205 */	tc_wmb();
607	vdac->bt_reg = 0x0;  /* PBM */	tc_wmb();
608	vdac->bt_reg = 0;    /* 207 */	tc_wmb();
609	vdac->bt_reg = 0x0;  /* ORM */	tc_wmb();
610	vdac->bt_reg = 0x0;  /* OBM */	tc_wmb();
611	vdac->bt_reg = 0x0;  /* ILV */	tc_wmb();
612	vdac->bt_reg = 0x0;  /* TEST */	tc_wmb();
613
614	BT459_SELECT(vdac, BT459_REG_CCR);
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	vdac->bt_reg = 0x0;	tc_wmb();
628
629	/* build sane colormap */
630	BT459_SELECT(vdac, 0);
631	vdac->bt_cmap = 0;	tc_wmb();
632	vdac->bt_cmap = 0;	tc_wmb();
633	vdac->bt_cmap = 0;	tc_wmb();
634	for (i = 1; i < CMAP_SIZE; i++) {
635		vdac->bt_cmap = 0xff;	tc_wmb();
636		vdac->bt_cmap = 0xff;	tc_wmb();
637		vdac->bt_cmap = 0xff;	tc_wmb();
638	}
639
640	/* clear out cursor image */
641	BT459_SELECT(vdac, BT459_REG_CRAM_BASE);
642	for (i = 0; i < 1024; i++)
643		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	BT459_SELECT(vdac, BT459_REG_CCOLOR_1);
651	vdac->bt_reg = 0xff;	tc_wmb();
652	vdac->bt_reg = 0xff;	tc_wmb();
653	vdac->bt_reg = 0xff;	tc_wmb();
654
655	vdac->bt_reg = 0;	tc_wmb();
656	vdac->bt_reg = 0;	tc_wmb();
657	vdac->bt_reg = 0;	tc_wmb();
658
659	vdac->bt_reg = 0xff;	tc_wmb();
660	vdac->bt_reg = 0xff;	tc_wmb();
661	vdac->bt_reg = 0xff;	tc_wmb();
662}
663
664static int
665get_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
686static int
687set_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
710static int
711set_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
768static int
769get_cursor(sc, p)
770	struct cfb_softc *sc;
771	struct wsdisplay_cursor *p;
772{
773	return (ENOTTY); /* XXX */
774}
775
776static void
777set_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
796void
797bt459_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	x += sc->magic_x; y += sc->magic_y; /* magic offset of CX coordinate */
807
808	s = spltty();
809
810	BT459_SELECT(vdac, BT459_REG_CURSOR_X_LOW);
811	vdac->bt_reg = x;	tc_wmb();
812	vdac->bt_reg = x >> 8;	tc_wmb();
813	vdac->bt_reg = y;	tc_wmb();
814	vdac->bt_reg = y >> 8;	tc_wmb();
815
816	splx(s);
817}
818