xcfb.c revision 1.34
1/* $NetBSD: xcfb.c,v 1.34 2003/12/13 23:02:50 ad 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.34 2003/12/13 23:02:50 ad 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 __P((struct device *, struct cfdata *, void *));
100static void xcfbattach __P((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 __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	int error;
556
557	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
558		return (EINVAL);
559
560	error = copyout(&sc->sc_cmap.r[index], p->red, count);
561	if (error)
562		return error;
563	error = copyout(&sc->sc_cmap.g[index], p->green, count);
564	if (error)
565		return error;
566	error = copyout(&sc->sc_cmap.b[index], p->blue, count);
567	return error;
568}
569
570static int
571set_cmap(sc, p)
572	struct xcfb_softc *sc;
573	struct wsdisplay_cmap *p;
574{
575	struct hwcmap256 cmap;
576	u_int index = p->index, count = p->count;
577	int error;
578
579	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
580		return (EINVAL);
581
582	error = copyin(p->red, &cmap.r[index], count);
583	if (error)
584		return error;
585	error = copyin(p->green, &cmap.g[index], count);
586	if (error)
587		return error;
588	error = copyin(p->blue, &cmap.b[index], count);
589	if (error)
590		return error;
591	memcpy(&sc->sc_cmap.r[index], &cmap.r[index], count);
592	memcpy(&sc->sc_cmap.g[index], &cmap.g[index], count);
593	memcpy(&sc->sc_cmap.b[index], &cmap.b[index], count);
594	return (0);
595}
596
597static int
598set_cursor(sc, p)
599	struct xcfb_softc *sc;
600	struct wsdisplay_cursor *p;
601{
602#define	cc (&sc->sc_cursor)
603	u_int v, index = 0, count = 0, icount = 0;
604	uint8_t r[2], g[2], b[2], image[512], mask[512];
605	int error;
606
607	v = p->which;
608	if (v & WSDISPLAY_CURSOR_DOCMAP) {
609		index = p->cmap.index;
610		count = p->cmap.count;
611
612		if (index >= 2 || index + count > 2)
613			return (EINVAL);
614		error = copyin(p->cmap.red, &r[index], count);
615		if (error)
616			return error;
617		error = copyin(p->cmap.green, &g[index], count);
618		if (error)
619			return error;
620		error = copyin(p->cmap.blue, &b[index], count);
621		if (error)
622			return error;
623	}
624	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
625		if (p->size.x > CURSOR_MAX_SIZE || p->size.y > CURSOR_MAX_SIZE)
626			return (EINVAL);
627		icount = ((p->size.x < 33) ? 4 : 8) * p->size.y;
628		error = copyin(p->image, image, icount);
629		if (error)
630			return error;
631		error = copyin(p->mask, mask, icount);
632		if (error)
633			return error;
634	}
635
636	if (v & WSDISPLAY_CURSOR_DOCMAP) {
637		memcpy(&cc->cc_color[index], &r[index], count);
638		memcpy(&cc->cc_color[index + 2], &g[index], count);
639		memcpy(&cc->cc_color[index + 4], &b[index], count);
640		ims332_load_curcmap(sc);
641	}
642	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
643		cc->cc_size = p->size;
644		memset(cc->cc_image, 0, sizeof cc->cc_image);
645		memcpy(cc->cc_image, image, icount);
646		memset(cc->cc_mask, 0, sizeof cc->cc_mask);
647		memcpy(cc->cc_mask, mask, icount);
648		ims332_load_curshape(sc);
649	}
650	if (v & WSDISPLAY_CURSOR_DOCUR) {
651		cc->cc_hot = p->hot;
652		if (p->enable)
653			sc->sc_csr &= ~IMS332_CSR_A_DISABLE_CURSOR;
654		else
655			sc->sc_csr |= IMS332_CSR_A_DISABLE_CURSOR;
656		ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
657	}
658	if (v & WSDISPLAY_CURSOR_DOPOS) {
659		set_curpos(sc, &p->pos);
660		ims332_set_curpos(sc);
661	}
662
663	return (0);
664#undef cc
665}
666
667static int
668get_cursor(sc, p)
669	struct xcfb_softc *sc;
670	struct wsdisplay_cursor *p;
671{
672	return (EPASSTHROUGH); /* XXX */
673}
674
675static void
676set_curpos(sc, curpos)
677	struct xcfb_softc *sc;
678	struct wsdisplay_curpos *curpos;
679{
680	struct rasops_info *ri = sc->sc_ri;
681	int x = curpos->x, y = curpos->y;
682
683	if (y < 0)
684		y = 0;
685	else if (y > ri->ri_height)
686		y = ri->ri_height;
687	if (x < 0)
688		x = 0;
689	else if (x > ri->ri_width)
690		x = ri->ri_width;
691	sc->sc_cursor.cc_pos.x = x;
692	sc->sc_cursor.cc_pos.y = y;
693}
694
695static void
696ims332_loadcmap(cm)
697	struct hwcmap256 *cm;
698{
699	int i;
700	u_int32_t rgb;
701
702	for (i = 0; i < CMAP_SIZE; i++) {
703		rgb = cm->b[i] << 16 | cm->g[i] << 8 | cm->r[i];
704		ims332_write_reg(IMS332_REG_LUT_BASE + i, rgb);
705	}
706}
707
708static void
709ims332_set_curpos(sc)
710	struct xcfb_softc *sc;
711{
712	struct wsdisplay_curpos *curpos = &sc->sc_cursor.cc_pos;
713	u_int32_t pos;
714	int s;
715
716	s = spltty();
717	pos = (curpos->x & 0xfff) << 12 | (curpos->y & 0xfff);
718	ims332_write_reg(IMS332_REG_CURSOR_LOC, pos);
719	splx(s);
720}
721
722static void
723ims332_load_curcmap(sc)
724	struct xcfb_softc *sc;
725{
726	u_int8_t *cp = sc->sc_cursor.cc_color;
727	u_int32_t rgb;
728
729	/* cursor background */
730	rgb = cp[5] << 16 | cp[3] << 8 | cp[1];
731	ims332_write_reg(IMS332_REG_CURSOR_LUT_1, rgb);
732
733	/* cursor foreground */
734	rgb = cp[4] << 16 | cp[2] << 8 | cp[0];
735	ims332_write_reg(IMS332_REG_CURSOR_LUT_2, rgb);
736}
737
738static void
739ims332_load_curshape(sc)
740	struct xcfb_softc *sc;
741{
742	u_int i, img, msk, bits;
743	u_int8_t u, *ip, *mp;
744
745	ip = (u_int8_t *)sc->sc_cursor.cc_image;
746	mp = (u_int8_t *)sc->sc_cursor.cc_mask;
747
748	i = 0;
749	/* 64 pixel scan line is consisted with 8 halfword cursor ram */
750	while (i < sc->sc_cursor.cc_size.y * 8) {
751		/* pad right half 32 pixel when smaller than 33 */
752		if ((i & 0x4) && sc->sc_cursor.cc_size.x < 33)
753			bits = 0;
754		else {
755			img = *ip++;
756			msk = *mp++;
757			img &= msk;	/* cookie off image */
758			u = (msk & 0x0f) << 4 | (img & 0x0f);
759			bits = shuffle[u];
760			u = (msk & 0xf0) | (img & 0xf0) >> 4;
761			bits = (shuffle[u] << 8) | bits;
762		}
763		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, bits);
764		i += 1;
765	}
766	/* pad unoccupied scan lines */
767	while (i < CURSOR_MAX_SIZE * 8) {
768		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, 0);
769		i += 1;
770	}
771}
772
773static void
774ims332_write_reg(regno, val)
775	int regno;
776	u_int32_t val;
777{
778	caddr_t high8 = (caddr_t)(ioasic_base + IMS332_HIGH);
779	caddr_t low16 = (caddr_t)(ioasic_base + IMS332_WLOW) + (regno << 4);
780
781	*(volatile u_int16_t *)high8 = (val & 0xff0000) >> 8;
782	*(volatile u_int16_t *)low16 = val;
783}
784
785#if 0
786static u_int32_t
787ims332_read_reg(regno)
788	int regno;
789{
790	caddr_t high8 = (caddr_t)(ioasic_base + IMS332_HIGH);
791	caddr_t low16 = (caddr_t)(ioasic_base + IMS332_RLOW) + (regno << 4);
792	u_int v0, v1;
793
794	v1 = *(volatile u_int16_t *)high8;
795	v0 = *(volatile u_int16_t *)low16;
796	return (v1 & 0xff00) << 8 | v0;
797}
798#endif
799