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