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