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