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