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