xcfb.c revision 1.42
1/* $NetBSD: xcfb.c,v 1.42 2006/04/12 19:38:24 jmmv 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: xcfb.c,v 1.42 2006/04/12 19:38:24 jmmv 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/tc/ioasicreg.h>
55#include <dev/ic/ims332reg.h>
56#include <pmax/pmax/maxine.h>
57
58#include <uvm/uvm_extern.h>
59
60struct hwcmap256 {
61#define	CMAP_SIZE	256	/* 256 R/G/B entries */
62	u_int8_t r[CMAP_SIZE];
63	u_int8_t g[CMAP_SIZE];
64	u_int8_t b[CMAP_SIZE];
65};
66
67struct hwcursor64 {
68	struct wsdisplay_curpos cc_pos;
69	struct wsdisplay_curpos cc_hot;
70	struct wsdisplay_curpos cc_size;
71	struct wsdisplay_curpos cc_magic;	/* not used by PMAG-DV */
72#define	CURSOR_MAX_SIZE	64
73	u_int8_t cc_color[6];
74	u_int64_t cc_image[CURSOR_MAX_SIZE];
75	u_int64_t cc_mask[CURSOR_MAX_SIZE];
76};
77
78#define	XCFB_FB_BASE	(XINE_PHYS_CFB_START + 0x2000000)
79#define	XCFB_FB_SIZE	0x100000
80
81#define	IMS332_HIGH	(IOASIC_SLOT_5_START)
82#define	IMS332_RLOW	(IOASIC_SLOT_7_START)
83#define	IMS332_WLOW	(IOASIC_SLOT_7_START + 0x20000)
84
85struct xcfb_softc {
86	struct device sc_dev;
87	vaddr_t sc_vaddr;
88	size_t sc_size;
89	struct rasops_info *sc_ri;
90	struct hwcmap256 sc_cmap;	/* software copy of colormap */
91	struct hwcursor64 sc_cursor;	/* software copy of cursor */
92	int sc_blanked;
93	/* XXX MAXINE can take PMAG-DV vertical retrace interrupt XXX */
94	int nscreens;
95	/* cursor coordinate is located at upper-left corner */
96	int sc_csr;			/* software copy of IMS332 CSR A */
97};
98
99static int  xcfbmatch(struct device *, struct cfdata *, void *);
100static void xcfbattach(struct device *, struct device *, void *);
101
102CFATTACH_DECL(xcfb, sizeof(struct xcfb_softc),
103    xcfbmatch, xcfbattach, NULL, NULL);
104
105static tc_addr_t xcfb_consaddr;
106static struct rasops_info xcfb_console_ri;
107static void xcfb_common_init(struct rasops_info *);
108static void xcfbhwinit(caddr_t);
109int xcfb_cnattach(void);
110
111struct wsscreen_descr xcfb_stdscreen = {
112	"std", 0, 0,
113	0, /* textops */
114	0, 0,
115	WSSCREEN_REVERSE
116};
117
118static const struct wsscreen_descr *_xcfb_scrlist[] = {
119	&xcfb_stdscreen,
120};
121
122static const struct wsscreen_list xcfb_screenlist = {
123	sizeof(_xcfb_scrlist) / sizeof(struct wsscreen_descr *), _xcfb_scrlist
124};
125
126static int	xcfbioctl(void *, void *, u_long, caddr_t, int, struct lwp *);
127static paddr_t	xcfbmmap(void *, void *, off_t, int);
128
129static int	xcfb_alloc_screen(void *, const struct wsscreen_descr *,
130				       void **, int *, int *, long *);
131static void	xcfb_free_screen(void *, void *);
132static int	xcfb_show_screen(void *, void *, int,
133				      void (*) (void *, int, int), void *);
134
135static const struct wsdisplay_accessops xcfb_accessops = {
136	xcfbioctl,
137	xcfbmmap,
138	xcfb_alloc_screen,
139	xcfb_free_screen,
140	xcfb_show_screen,
141	0 /* load_font */
142};
143
144static int  xcfbintr(void *);
145static void xcfb_screenblank(struct xcfb_softc *);
146static void xcfb_cmap_init(struct xcfb_softc *);
147static int  set_cmap(struct xcfb_softc *, struct wsdisplay_cmap *);
148static int  get_cmap(struct xcfb_softc *, struct wsdisplay_cmap *);
149static int  set_cursor(struct xcfb_softc *, struct wsdisplay_cursor *);
150static int  get_cursor(struct xcfb_softc *, struct wsdisplay_cursor *);
151static void set_curpos(struct xcfb_softc *, struct wsdisplay_curpos *);
152static void ims332_loadcmap(struct hwcmap256 *);
153static void ims332_set_curpos(struct xcfb_softc *);
154static void ims332_load_curcmap(struct xcfb_softc *);
155static void ims332_load_curshape(struct xcfb_softc *);
156static void ims332_write_reg(int, u_int32_t);
157#if 0
158static u_int32_t ims332_read_reg(int);
159#endif
160
161extern long ioasic_base;	/* XXX */
162
163/*
164 * Compose 2 bit/pixel cursor image.
165 *   M M M M I I I I		M I M I M I M I
166 *	[ before ]		   [ after ]
167 *   3 2 1 0 3 2 1 0		3 3 2 2 1 1 0 0
168 *   7 6 5 4 7 6 5 4		7 7 6 6 5 5 4 4
169 */
170static const u_int8_t shuffle[256] = {
171	0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15,
172	0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55,
173	0x02, 0x03, 0x06, 0x07, 0x12, 0x13, 0x16, 0x17,
174	0x42, 0x43, 0x46, 0x47, 0x52, 0x53, 0x56, 0x57,
175	0x08, 0x09, 0x0c, 0x0d, 0x18, 0x19, 0x1c, 0x1d,
176	0x48, 0x49, 0x4c, 0x4d, 0x58, 0x59, 0x5c, 0x5d,
177	0x0a, 0x0b, 0x0e, 0x0f, 0x1a, 0x1b, 0x1e, 0x1f,
178	0x4a, 0x4b, 0x4e, 0x4f, 0x5a, 0x5b, 0x5e, 0x5f,
179	0x20, 0x21, 0x24, 0x25, 0x30, 0x31, 0x34, 0x35,
180	0x60, 0x61, 0x64, 0x65, 0x70, 0x71, 0x74, 0x75,
181	0x22, 0x23, 0x26, 0x27, 0x32, 0x33, 0x36, 0x37,
182	0x62, 0x63, 0x66, 0x67, 0x72, 0x73, 0x76, 0x77,
183	0x28, 0x29, 0x2c, 0x2d, 0x38, 0x39, 0x3c, 0x3d,
184	0x68, 0x69, 0x6c, 0x6d, 0x78, 0x79, 0x7c, 0x7d,
185	0x2a, 0x2b, 0x2e, 0x2f, 0x3a, 0x3b, 0x3e, 0x3f,
186	0x6a, 0x6b, 0x6e, 0x6f, 0x7a, 0x7b, 0x7e, 0x7f,
187	0x80, 0x81, 0x84, 0x85, 0x90, 0x91, 0x94, 0x95,
188	0xc0, 0xc1, 0xc4, 0xc5, 0xd0, 0xd1, 0xd4, 0xd5,
189	0x82, 0x83, 0x86, 0x87, 0x92, 0x93, 0x96, 0x97,
190	0xc2, 0xc3, 0xc6, 0xc7, 0xd2, 0xd3, 0xd6, 0xd7,
191	0x88, 0x89, 0x8c, 0x8d, 0x98, 0x99, 0x9c, 0x9d,
192	0xc8, 0xc9, 0xcc, 0xcd, 0xd8, 0xd9, 0xdc, 0xdd,
193	0x8a, 0x8b, 0x8e, 0x8f, 0x9a, 0x9b, 0x9e, 0x9f,
194	0xca, 0xcb, 0xce, 0xcf, 0xda, 0xdb, 0xde, 0xdf,
195	0xa0, 0xa1, 0xa4, 0xa5, 0xb0, 0xb1, 0xb4, 0xb5,
196	0xe0, 0xe1, 0xe4, 0xe5, 0xf0, 0xf1, 0xf4, 0xf5,
197	0xa2, 0xa3, 0xa6, 0xa7, 0xb2, 0xb3, 0xb6, 0xb7,
198	0xe2, 0xe3, 0xe6, 0xe7, 0xf2, 0xf3, 0xf6, 0xf7,
199	0xa8, 0xa9, 0xac, 0xad, 0xb8, 0xb9, 0xbc, 0xbd,
200	0xe8, 0xe9, 0xec, 0xed, 0xf8, 0xf9, 0xfc, 0xfd,
201	0xaa, 0xab, 0xae, 0xaf, 0xba, 0xbb, 0xbe, 0xbf,
202	0xea, 0xeb, 0xee, 0xef, 0xfa, 0xfb, 0xfe, 0xff,
203};
204
205static int
206xcfbmatch(struct device *parent, struct cfdata *match, void *aux)
207{
208	struct tc_attach_args *ta = aux;
209
210	if (strncmp("PMAG-DV ", ta->ta_modname, TC_ROM_LLEN) != 0)
211		return (0);
212
213	return (1);
214}
215
216static void
217xcfbattach(struct device *parent, struct device *self, void *aux)
218{
219	struct xcfb_softc *sc = device_private(self);
220	struct tc_attach_args *ta = aux;
221	struct rasops_info *ri;
222	struct wsemuldisplaydev_attach_args waa;
223	int console;
224
225	console = (ta->ta_addr == xcfb_consaddr);
226	if (console) {
227		sc->sc_ri = ri = &xcfb_console_ri;
228		sc->nscreens = 1;
229	}
230	else {
231		MALLOC(ri, struct rasops_info *, sizeof(struct rasops_info),
232			M_DEVBUF, M_NOWAIT);
233		if (ri == NULL) {
234			printf(": can't alloc memory\n");
235			return;
236		}
237		memset(ri, 0, sizeof(struct rasops_info));
238
239		ri->ri_hw = (void *)ioasic_base;
240		xcfb_common_init(ri);
241		sc->sc_ri = ri;
242	}
243	printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth);
244
245	xcfb_cmap_init(sc);
246
247	sc->sc_vaddr = ta->ta_addr;
248	sc->sc_blanked = 0;
249	sc->sc_csr = IMS332_BPP_8 | IMS332_CSR_A_VTG_ENABLE;
250
251        tc_intr_establish(parent, ta->ta_cookie, IPL_TTY, xcfbintr, sc);
252
253	waa.console = console;
254	waa.scrdata = &xcfb_screenlist;
255	waa.accessops = &xcfb_accessops;
256	waa.accesscookie = sc;
257
258	config_found(self, &waa, wsemuldisplaydevprint);
259}
260
261static void
262xcfb_cmap_init(struct xcfb_softc *sc)
263{
264	struct hwcmap256 *cm;
265	const u_int8_t *p;
266	int index;
267
268	cm = &sc->sc_cmap;
269	p = rasops_cmap;
270	for (index = 0; index < CMAP_SIZE; index++, p += 3) {
271		cm->r[index] = p[0];
272		cm->g[index] = p[1];
273		cm->b[index] = p[2];
274	}
275}
276
277static void
278xcfb_common_init(struct rasops_info *ri)
279{
280	int cookie;
281
282	/* initialize colormap and cursor hardware */
283	xcfbhwinit((caddr_t)ri->ri_hw);
284
285	ri->ri_flg = RI_CENTER;
286	ri->ri_depth = 8;
287	ri->ri_width = 1024;
288	ri->ri_height = 768;
289	ri->ri_stride = 1024;
290	ri->ri_bits = (caddr_t)MIPS_PHYS_TO_KSEG1(XCFB_FB_BASE);
291
292	/* clear the screen */
293	memset(ri->ri_bits, 0, ri->ri_stride * ri->ri_height);
294
295	wsfont_init();
296	/* prefer 12 pixel wide font */
297	cookie = wsfont_find(NULL, 12, 0, 0, WSDISPLAY_FONTORDER_L2R,
298	    WSDISPLAY_FONTORDER_L2R);
299	if (cookie <= 0)
300		cookie = wsfont_find(NULL, 0, 0, 0, WSDISPLAY_FONTORDER_L2R,
301		    WSDISPLAY_FONTORDER_L2R);
302	if (cookie <= 0) {
303		printf("xcfb: font table is empty\n");
304		return;
305	}
306
307	if (wsfont_lock(cookie, &ri->ri_font)) {
308		printf("xcfb: couldn't lock font\n");
309		return;
310	}
311	ri->ri_wsfcookie = cookie;
312
313	rasops_init(ri, 34, 80);
314
315	/* XXX shouldn't be global */
316	xcfb_stdscreen.nrows = ri->ri_rows;
317	xcfb_stdscreen.ncols = ri->ri_cols;
318	xcfb_stdscreen.textops = &ri->ri_ops;
319	xcfb_stdscreen.capabilities = ri->ri_caps;
320}
321
322int
323xcfb_cnattach(void)
324{
325	struct rasops_info *ri;
326	long defattr;
327
328	ri = &xcfb_console_ri;
329	ri->ri_hw = (void *)ioasic_base;
330	xcfb_common_init(ri);
331	(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
332	wsdisplay_cnattach(&xcfb_stdscreen, ri, 0, 0, defattr);
333	xcfb_consaddr = MIPS_PHYS_TO_KSEG1(XINE_PHYS_CFB_START);
334	return (0);
335}
336
337static void
338xcfbhwinit(caddr_t base)
339{
340	volatile u_int32_t *csr;
341	u_int32_t i;
342	const u_int8_t *p;
343
344	csr = (volatile u_int32_t *)(base + IOASIC_CSR);
345	i = *csr;
346	i &= ~XINE_CSR_VDAC_ENABLE;
347	*csr = i;
348	DELAY(50);
349	i |= XINE_CSR_VDAC_ENABLE;
350	*csr = i;
351	DELAY(50);
352	ims332_write_reg(IMS332_REG_BOOT, 0x2c);
353	ims332_write_reg(IMS332_REG_CSR_A,
354		IMS332_BPP_8|IMS332_CSR_A_DISABLE_CURSOR);
355	ims332_write_reg(IMS332_REG_HALF_SYNCH, 0x10);
356	ims332_write_reg(IMS332_REG_BACK_PORCH, 0x21);
357	ims332_write_reg(IMS332_REG_DISPLAY, 0x100);
358	ims332_write_reg(IMS332_REG_SHORT_DIS, 0x5d);
359	ims332_write_reg(IMS332_REG_BROAD_PULSE, 0x9f);
360	ims332_write_reg(IMS332_REG_LINE_TIME, 0x146);
361	ims332_write_reg(IMS332_REG_V_SYNC, 0x0c);
362	ims332_write_reg(IMS332_REG_V_PRE_EQUALIZE, 0x02);
363	ims332_write_reg(IMS332_REG_V_POST_EQUALIZE, 0x02);
364	ims332_write_reg(IMS332_REG_V_BLANK, 0x2a);
365	ims332_write_reg(IMS332_REG_V_DISPLAY, 0x600);
366	ims332_write_reg(IMS332_REG_LINE_START, 0x10);
367	ims332_write_reg(IMS332_REG_MEM_INIT, 0x0a);
368	ims332_write_reg(IMS332_REG_COLOR_MASK, 0xffffff);
369	ims332_write_reg(IMS332_REG_CSR_A,
370		IMS332_BPP_8|IMS332_CSR_A_VTG_ENABLE);
371
372	/* build sane colormap */
373	p = rasops_cmap;
374	for (i = 0; i < CMAP_SIZE; i++, p += 3) {
375		u_int32_t bgr;
376
377		bgr = p[2] << 16 | p[1] << 8 | p[0];
378		ims332_write_reg(IMS332_REG_LUT_BASE + i, bgr);
379	}
380
381	/* clear out cursor image */
382	for (i = 0; i < 512; i++)
383		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, 0);
384
385	/*
386	 * 2 bit/pixel cursor.  Assign MSB for cursor mask and LSB for
387	 * cursor image.  LUT_1 for mask color, while LUT_2 for
388	 * image color.  LUT_0 will be never used.
389	 */
390	ims332_write_reg(IMS332_REG_CURSOR_LUT_0, 0);
391	ims332_write_reg(IMS332_REG_CURSOR_LUT_1, 0xffffff);
392	ims332_write_reg(IMS332_REG_CURSOR_LUT_2, 0xffffff);
393}
394
395static int
396xcfbioctl(void *v, void *vs, u_long cmd, caddr_t data, int flag, struct lwp *l)
397{
398	struct xcfb_softc *sc = v;
399	struct rasops_info *ri = sc->sc_ri;
400	int turnoff, error;
401
402	switch (cmd) {
403	case WSDISPLAYIO_GTYPE:
404		*(u_int *)data = WSDISPLAY_TYPE_XCFB;
405		return (0);
406
407	case WSDISPLAYIO_GINFO:
408#define	wsd_fbip ((struct wsdisplay_fbinfo *)data)
409		wsd_fbip->height = ri->ri_height;
410		wsd_fbip->width = ri->ri_width;
411		wsd_fbip->depth = ri->ri_depth;
412		wsd_fbip->cmsize = CMAP_SIZE;
413#undef fbt
414		return (0);
415
416	case WSDISPLAYIO_GETCMAP:
417		return get_cmap(sc, (struct wsdisplay_cmap *)data);
418
419	case WSDISPLAYIO_PUTCMAP:
420		error = set_cmap(sc, (struct wsdisplay_cmap *)data);
421		if (error == 0)
422			ims332_loadcmap(&sc->sc_cmap);
423		return (error);
424
425	case WSDISPLAYIO_SVIDEO:
426		turnoff = *(int *)data == WSDISPLAYIO_VIDEO_OFF;
427		if (sc->sc_blanked != turnoff) {
428			sc->sc_blanked = turnoff;
429			xcfb_screenblank(sc);
430		}
431		return (0);
432
433	case WSDISPLAYIO_GVIDEO:
434		*(u_int *)data = sc->sc_blanked ?
435		    WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON;
436		return (0);
437
438	case WSDISPLAYIO_GCURPOS:
439		*(struct wsdisplay_curpos *)data = sc->sc_cursor.cc_pos;
440		return (0);
441
442	case WSDISPLAYIO_SCURPOS:
443		set_curpos(sc, (struct wsdisplay_curpos *)data);
444		ims332_set_curpos(sc);
445		return (0);
446
447	case WSDISPLAYIO_GCURMAX:
448		((struct wsdisplay_curpos *)data)->x =
449		((struct wsdisplay_curpos *)data)->y = CURSOR_MAX_SIZE;
450		return (0);
451
452	case WSDISPLAYIO_GCURSOR:
453		return get_cursor(sc, (struct wsdisplay_cursor *)data);
454
455	case WSDISPLAYIO_SCURSOR:
456		return set_cursor(sc, (struct wsdisplay_cursor *)data);
457
458	case WSDISPLAYIO_SMODE:
459		if (*(int *)data == WSDISPLAYIO_MODE_EMUL) {
460			sc->sc_csr |= IMS332_CSR_A_DISABLE_CURSOR;
461			ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
462			xcfb_cmap_init(sc);
463			ims332_loadcmap(&sc->sc_cmap);
464			sc->sc_blanked = 0;
465			xcfb_screenblank(sc);
466		}
467		return (0);
468	}
469	return (EPASSTHROUGH);
470}
471
472static paddr_t
473xcfbmmap(void *v, void *vs, off_t offset, int prot)
474{
475
476	if (offset >= XCFB_FB_SIZE || offset < 0)
477		return (-1);
478	return mips_btop(MIPS_KSEG1_TO_PHYS(XCFB_FB_BASE + offset));
479}
480
481static int
482xcfb_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
483    int *curxp, int *curyp, long *attrp)
484{
485	struct xcfb_softc *sc = v;
486	struct rasops_info *ri = sc->sc_ri;
487	long defattr;
488
489	if (sc->nscreens > 0)
490		return (ENOMEM);
491
492	*cookiep = ri; 		/* one and only for now */
493	*curxp = 0;
494	*curyp = 0;
495	(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
496	*attrp = defattr;
497	sc->nscreens++;
498	return (0);
499}
500
501static void
502xcfb_free_screen(void *v, void *cookie)
503{
504	struct xcfb_softc *sc = v;
505
506	if (sc->sc_ri == &xcfb_console_ri)
507		panic("xcfb_free_screen: console");
508
509	sc->nscreens--;
510}
511
512static int
513xcfb_show_screen(void *v, void *cookie, int waitok,
514    void (*cb)(void *, int, int), void *cbarg)
515{
516
517	return (0);
518}
519
520static int
521xcfbintr(void *v)
522{
523	struct xcfb_softc *sc = v;
524	u_int32_t *intr, i;
525
526	intr = (u_int32_t *)((caddr_t)sc->sc_ri->ri_hw + IOASIC_INTR);
527	i = *intr;
528	i &= ~XINE_INTR_VINT;
529	*intr = i;
530	return (1);
531}
532
533static void
534xcfb_screenblank(struct xcfb_softc *sc)
535{
536	if (sc->sc_blanked)
537		sc->sc_csr |= IMS332_CSR_A_FORCE_BLANK;
538	else
539		sc->sc_csr &= ~IMS332_CSR_A_FORCE_BLANK;
540	ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
541}
542
543static int
544get_cmap(struct xcfb_softc *sc, struct wsdisplay_cmap *p)
545{
546	u_int index = p->index, count = p->count;
547	int error;
548
549	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
550		return (EINVAL);
551
552	error = copyout(&sc->sc_cmap.r[index], p->red, count);
553	if (error)
554		return error;
555	error = copyout(&sc->sc_cmap.g[index], p->green, count);
556	if (error)
557		return error;
558	error = copyout(&sc->sc_cmap.b[index], p->blue, count);
559	return error;
560}
561
562static int
563set_cmap(struct xcfb_softc *sc, struct wsdisplay_cmap *p)
564{
565	struct hwcmap256 cmap;
566	u_int index = p->index, count = p->count;
567	int error;
568
569	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
570		return (EINVAL);
571
572	error = copyin(p->red, &cmap.r[index], count);
573	if (error)
574		return error;
575	error = copyin(p->green, &cmap.g[index], count);
576	if (error)
577		return error;
578	error = copyin(p->blue, &cmap.b[index], count);
579	if (error)
580		return error;
581	memcpy(&sc->sc_cmap.r[index], &cmap.r[index], count);
582	memcpy(&sc->sc_cmap.g[index], &cmap.g[index], count);
583	memcpy(&sc->sc_cmap.b[index], &cmap.b[index], count);
584	return (0);
585}
586
587static int
588set_cursor(struct xcfb_softc *sc, struct wsdisplay_cursor *p)
589{
590#define	cc (&sc->sc_cursor)
591	u_int v, index = 0, count = 0, icount = 0;
592	uint8_t r[2], g[2], b[2], image[512], mask[512];
593	int error;
594
595	v = p->which;
596	if (v & WSDISPLAY_CURSOR_DOCMAP) {
597		index = p->cmap.index;
598		count = p->cmap.count;
599
600		if (index >= 2 || index + count > 2)
601			return (EINVAL);
602		error = copyin(p->cmap.red, &r[index], count);
603		if (error)
604			return error;
605		error = copyin(p->cmap.green, &g[index], count);
606		if (error)
607			return error;
608		error = copyin(p->cmap.blue, &b[index], count);
609		if (error)
610			return error;
611	}
612	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
613		if (p->size.x > CURSOR_MAX_SIZE || p->size.y > CURSOR_MAX_SIZE)
614			return (EINVAL);
615		icount = ((p->size.x < 33) ? 4 : 8) * p->size.y;
616		error = copyin(p->image, image, icount);
617		if (error)
618			return error;
619		error = copyin(p->mask, mask, icount);
620		if (error)
621			return error;
622	}
623
624	if (v & WSDISPLAY_CURSOR_DOCMAP) {
625		memcpy(&cc->cc_color[index], &r[index], count);
626		memcpy(&cc->cc_color[index + 2], &g[index], count);
627		memcpy(&cc->cc_color[index + 4], &b[index], count);
628		ims332_load_curcmap(sc);
629	}
630	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
631		cc->cc_size = p->size;
632		memset(cc->cc_image, 0, sizeof cc->cc_image);
633		memcpy(cc->cc_image, image, icount);
634		memset(cc->cc_mask, 0, sizeof cc->cc_mask);
635		memcpy(cc->cc_mask, mask, icount);
636		ims332_load_curshape(sc);
637	}
638	if (v & WSDISPLAY_CURSOR_DOCUR) {
639		cc->cc_hot = p->hot;
640		if (p->enable)
641			sc->sc_csr &= ~IMS332_CSR_A_DISABLE_CURSOR;
642		else
643			sc->sc_csr |= IMS332_CSR_A_DISABLE_CURSOR;
644		ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
645	}
646	if (v & WSDISPLAY_CURSOR_DOPOS) {
647		set_curpos(sc, &p->pos);
648		ims332_set_curpos(sc);
649	}
650
651	return (0);
652#undef cc
653}
654
655static int
656get_cursor(struct xcfb_softc *sc, struct wsdisplay_cursor *p)
657{
658	return (EPASSTHROUGH); /* XXX */
659}
660
661static void
662set_curpos(struct xcfb_softc *sc, struct wsdisplay_curpos *curpos)
663{
664	struct rasops_info *ri = sc->sc_ri;
665	int x = curpos->x, y = curpos->y;
666
667	if (y < 0)
668		y = 0;
669	else if (y > ri->ri_height)
670		y = ri->ri_height;
671	if (x < 0)
672		x = 0;
673	else if (x > ri->ri_width)
674		x = ri->ri_width;
675	sc->sc_cursor.cc_pos.x = x;
676	sc->sc_cursor.cc_pos.y = y;
677}
678
679static void
680ims332_loadcmap(struct hwcmap256 *cm)
681{
682	int i;
683	u_int32_t rgb;
684
685	for (i = 0; i < CMAP_SIZE; i++) {
686		rgb = cm->b[i] << 16 | cm->g[i] << 8 | cm->r[i];
687		ims332_write_reg(IMS332_REG_LUT_BASE + i, rgb);
688	}
689}
690
691static void
692ims332_set_curpos(struct xcfb_softc *sc)
693{
694	struct wsdisplay_curpos *curpos = &sc->sc_cursor.cc_pos;
695	u_int32_t pos;
696	int s;
697
698	s = spltty();
699	pos = (curpos->x & 0xfff) << 12 | (curpos->y & 0xfff);
700	ims332_write_reg(IMS332_REG_CURSOR_LOC, pos);
701	splx(s);
702}
703
704static void
705ims332_load_curcmap(struct xcfb_softc *sc)
706{
707	u_int8_t *cp = sc->sc_cursor.cc_color;
708	u_int32_t rgb;
709
710	/* cursor background */
711	rgb = cp[5] << 16 | cp[3] << 8 | cp[1];
712	ims332_write_reg(IMS332_REG_CURSOR_LUT_1, rgb);
713
714	/* cursor foreground */
715	rgb = cp[4] << 16 | cp[2] << 8 | cp[0];
716	ims332_write_reg(IMS332_REG_CURSOR_LUT_2, rgb);
717}
718
719static void
720ims332_load_curshape(struct xcfb_softc *sc)
721{
722	u_int i, img, msk, bits;
723	u_int8_t u, *ip, *mp;
724
725	ip = (u_int8_t *)sc->sc_cursor.cc_image;
726	mp = (u_int8_t *)sc->sc_cursor.cc_mask;
727
728	i = 0;
729	/* 64 pixel scan line is consisted with 8 halfword cursor ram */
730	while (i < sc->sc_cursor.cc_size.y * 8) {
731		/* pad right half 32 pixel when smaller than 33 */
732		if ((i & 0x4) && sc->sc_cursor.cc_size.x < 33)
733			bits = 0;
734		else {
735			img = *ip++;
736			msk = *mp++;
737			img &= msk;	/* cookie off image */
738			u = (msk & 0x0f) << 4 | (img & 0x0f);
739			bits = shuffle[u];
740			u = (msk & 0xf0) | (img & 0xf0) >> 4;
741			bits = (shuffle[u] << 8) | bits;
742		}
743		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, bits);
744		i += 1;
745	}
746	/* pad unoccupied scan lines */
747	while (i < CURSOR_MAX_SIZE * 8) {
748		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, 0);
749		i += 1;
750	}
751}
752
753static void
754ims332_write_reg(int regno, u_int32_t val)
755{
756	caddr_t high8 = (caddr_t)(ioasic_base + IMS332_HIGH);
757	caddr_t low16 = (caddr_t)(ioasic_base + IMS332_WLOW) + (regno << 4);
758
759	*(volatile u_int16_t *)high8 = (val & 0xff0000) >> 8;
760	*(volatile u_int16_t *)low16 = val;
761}
762
763#if 0
764static u_int32_t
765ims332_read_reg(int regno)
766{
767	caddr_t high8 = (caddr_t)(ioasic_base + IMS332_HIGH);
768	caddr_t low16 = (caddr_t)(ioasic_base + IMS332_RLOW) + (regno << 4);
769	u_int v0, v1;
770
771	v1 = *(volatile u_int16_t *)high8;
772	v0 = *(volatile u_int16_t *)low16;
773	return (v1 & 0xff00) << 8 | v0;
774}
775#endif
776