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