xcfb.c revision 1.50
1/* $NetBSD: xcfb.c,v 1.50 2009/05/12 14:47:04 cegger Exp $ */
2
3/*-
4 * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Tohru Nishimura.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: xcfb.c,v 1.50 2009/05/12 14:47:04 cegger Exp $");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/device.h>
39#include <sys/malloc.h>
40#include <sys/buf.h>
41#include <sys/ioctl.h>
42
43#include <sys/bus.h>
44#include <sys/intr.h>
45
46#include <dev/wscons/wsconsio.h>
47#include <dev/wscons/wsdisplayvar.h>
48
49#include <dev/rasops/rasops.h>
50#include <dev/wsfont/wsfont.h>
51
52#include <dev/tc/tcvar.h>
53#include <dev/tc/ioasicreg.h>
54#include <dev/ic/ims332reg.h>
55#include <pmax/pmax/maxine.h>
56
57#include <uvm/uvm_extern.h>
58
59struct hwcmap256 {
60#define	CMAP_SIZE	256	/* 256 R/G/B entries */
61	u_int8_t r[CMAP_SIZE];
62	u_int8_t g[CMAP_SIZE];
63	u_int8_t b[CMAP_SIZE];
64};
65
66struct hwcursor64 {
67	struct wsdisplay_curpos cc_pos;
68	struct wsdisplay_curpos cc_hot;
69	struct wsdisplay_curpos cc_size;
70	struct wsdisplay_curpos cc_magic;	/* not used by PMAG-DV */
71#define	CURSOR_MAX_SIZE	64
72	u_int8_t cc_color[6];
73	u_int64_t cc_image[CURSOR_MAX_SIZE];
74	u_int64_t cc_mask[CURSOR_MAX_SIZE];
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	vaddr_t sc_vaddr;
86	size_t sc_size;
87	struct rasops_info *sc_ri;
88	struct hwcmap256 sc_cmap;	/* software copy of colormap */
89	struct hwcursor64 sc_cursor;	/* software copy of cursor */
90	int sc_blanked;
91	/* XXX MAXINE can take PMAG-DV vertical retrace interrupt XXX */
92	int nscreens;
93	/* cursor coordinate is located at upper-left corner */
94	int sc_csr;			/* software copy of IMS332 CSR A */
95};
96
97static int  xcfbmatch(device_t, cfdata_t, void *);
98static void xcfbattach(device_t, device_t, void *);
99
100CFATTACH_DECL_NEW(xcfb, sizeof(struct xcfb_softc),
101    xcfbmatch, xcfbattach, NULL, NULL);
102
103static tc_addr_t xcfb_consaddr;
104static struct rasops_info xcfb_console_ri;
105static void xcfb_common_init(struct rasops_info *);
106static void xcfbhwinit(void *);
107int xcfb_cnattach(void);
108
109struct wsscreen_descr xcfb_stdscreen = {
110	"std", 0, 0,
111	0, /* textops */
112	0, 0,
113	WSSCREEN_REVERSE
114};
115
116static const struct wsscreen_descr *_xcfb_scrlist[] = {
117	&xcfb_stdscreen,
118};
119
120static const struct wsscreen_list xcfb_screenlist = {
121	sizeof(_xcfb_scrlist) / sizeof(struct wsscreen_descr *), _xcfb_scrlist
122};
123
124static int	xcfbioctl(void *, void *, u_long, void *, int, struct lwp *);
125static paddr_t	xcfbmmap(void *, void *, off_t, int);
126
127static int	xcfb_alloc_screen(void *, const struct wsscreen_descr *,
128				       void **, int *, int *, long *);
129static void	xcfb_free_screen(void *, void *);
130static int	xcfb_show_screen(void *, void *, int,
131				      void (*) (void *, int, int), void *);
132
133static const struct wsdisplay_accessops xcfb_accessops = {
134	xcfbioctl,
135	xcfbmmap,
136	xcfb_alloc_screen,
137	xcfb_free_screen,
138	xcfb_show_screen,
139	0 /* load_font */
140};
141
142static int  xcfbintr(void *);
143static void xcfb_screenblank(struct xcfb_softc *);
144static void xcfb_cmap_init(struct xcfb_softc *);
145static int  set_cmap(struct xcfb_softc *, struct wsdisplay_cmap *);
146static int  get_cmap(struct xcfb_softc *, struct wsdisplay_cmap *);
147static int  set_cursor(struct xcfb_softc *, struct wsdisplay_cursor *);
148static int  get_cursor(struct xcfb_softc *, struct wsdisplay_cursor *);
149static void set_curpos(struct xcfb_softc *, struct wsdisplay_curpos *);
150static void ims332_loadcmap(struct hwcmap256 *);
151static void ims332_set_curpos(struct xcfb_softc *);
152static void ims332_load_curcmap(struct xcfb_softc *);
153static void ims332_load_curshape(struct xcfb_softc *);
154static void ims332_write_reg(int, u_int32_t);
155#if 0
156static u_int32_t ims332_read_reg(int);
157#endif
158
159extern long ioasic_base;	/* XXX */
160
161/*
162 * Compose 2 bit/pixel cursor image.
163 *   M M M M I I I I		M I M I M I M I
164 *	[ before ]		   [ after ]
165 *   3 2 1 0 3 2 1 0		3 3 2 2 1 1 0 0
166 *   7 6 5 4 7 6 5 4		7 7 6 6 5 5 4 4
167 */
168static const u_int8_t shuffle[256] = {
169	0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15,
170	0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55,
171	0x02, 0x03, 0x06, 0x07, 0x12, 0x13, 0x16, 0x17,
172	0x42, 0x43, 0x46, 0x47, 0x52, 0x53, 0x56, 0x57,
173	0x08, 0x09, 0x0c, 0x0d, 0x18, 0x19, 0x1c, 0x1d,
174	0x48, 0x49, 0x4c, 0x4d, 0x58, 0x59, 0x5c, 0x5d,
175	0x0a, 0x0b, 0x0e, 0x0f, 0x1a, 0x1b, 0x1e, 0x1f,
176	0x4a, 0x4b, 0x4e, 0x4f, 0x5a, 0x5b, 0x5e, 0x5f,
177	0x20, 0x21, 0x24, 0x25, 0x30, 0x31, 0x34, 0x35,
178	0x60, 0x61, 0x64, 0x65, 0x70, 0x71, 0x74, 0x75,
179	0x22, 0x23, 0x26, 0x27, 0x32, 0x33, 0x36, 0x37,
180	0x62, 0x63, 0x66, 0x67, 0x72, 0x73, 0x76, 0x77,
181	0x28, 0x29, 0x2c, 0x2d, 0x38, 0x39, 0x3c, 0x3d,
182	0x68, 0x69, 0x6c, 0x6d, 0x78, 0x79, 0x7c, 0x7d,
183	0x2a, 0x2b, 0x2e, 0x2f, 0x3a, 0x3b, 0x3e, 0x3f,
184	0x6a, 0x6b, 0x6e, 0x6f, 0x7a, 0x7b, 0x7e, 0x7f,
185	0x80, 0x81, 0x84, 0x85, 0x90, 0x91, 0x94, 0x95,
186	0xc0, 0xc1, 0xc4, 0xc5, 0xd0, 0xd1, 0xd4, 0xd5,
187	0x82, 0x83, 0x86, 0x87, 0x92, 0x93, 0x96, 0x97,
188	0xc2, 0xc3, 0xc6, 0xc7, 0xd2, 0xd3, 0xd6, 0xd7,
189	0x88, 0x89, 0x8c, 0x8d, 0x98, 0x99, 0x9c, 0x9d,
190	0xc8, 0xc9, 0xcc, 0xcd, 0xd8, 0xd9, 0xdc, 0xdd,
191	0x8a, 0x8b, 0x8e, 0x8f, 0x9a, 0x9b, 0x9e, 0x9f,
192	0xca, 0xcb, 0xce, 0xcf, 0xda, 0xdb, 0xde, 0xdf,
193	0xa0, 0xa1, 0xa4, 0xa5, 0xb0, 0xb1, 0xb4, 0xb5,
194	0xe0, 0xe1, 0xe4, 0xe5, 0xf0, 0xf1, 0xf4, 0xf5,
195	0xa2, 0xa3, 0xa6, 0xa7, 0xb2, 0xb3, 0xb6, 0xb7,
196	0xe2, 0xe3, 0xe6, 0xe7, 0xf2, 0xf3, 0xf6, 0xf7,
197	0xa8, 0xa9, 0xac, 0xad, 0xb8, 0xb9, 0xbc, 0xbd,
198	0xe8, 0xe9, 0xec, 0xed, 0xf8, 0xf9, 0xfc, 0xfd,
199	0xaa, 0xab, 0xae, 0xaf, 0xba, 0xbb, 0xbe, 0xbf,
200	0xea, 0xeb, 0xee, 0xef, 0xfa, 0xfb, 0xfe, 0xff,
201};
202
203static int
204xcfbmatch(device_t parent, cfdata_t match, void *aux)
205{
206	struct tc_attach_args *ta = aux;
207
208	if (strncmp("PMAG-DV ", ta->ta_modname, TC_ROM_LLEN) != 0)
209		return (0);
210
211	return (1);
212}
213
214static void
215xcfbattach(device_t parent, device_t self, void *aux)
216{
217	struct xcfb_softc *sc = device_private(self);
218	struct tc_attach_args *ta = aux;
219	struct rasops_info *ri;
220	struct wsemuldisplaydev_attach_args waa;
221	int console;
222
223	console = (ta->ta_addr == xcfb_consaddr);
224	if (console) {
225		sc->sc_ri = ri = &xcfb_console_ri;
226		sc->nscreens = 1;
227	}
228	else {
229		ri = malloc(sizeof(struct rasops_info), M_DEVBUF, M_NOWAIT);
230		if (ri == NULL) {
231			printf(": can't alloc memory\n");
232			return;
233		}
234		memset(ri, 0, sizeof(struct rasops_info));
235
236		ri->ri_hw = (void *)ioasic_base;
237		xcfb_common_init(ri);
238		sc->sc_ri = ri;
239	}
240	printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth);
241
242	xcfb_cmap_init(sc);
243
244	sc->sc_vaddr = ta->ta_addr;
245	sc->sc_blanked = 0;
246	sc->sc_csr = IMS332_BPP_8 | IMS332_CSR_A_VTG_ENABLE;
247
248        tc_intr_establish(parent, ta->ta_cookie, IPL_TTY, xcfbintr, sc);
249
250	waa.console = console;
251	waa.scrdata = &xcfb_screenlist;
252	waa.accessops = &xcfb_accessops;
253	waa.accesscookie = sc;
254
255	config_found(self, &waa, wsemuldisplaydevprint);
256}
257
258static void
259xcfb_cmap_init(struct xcfb_softc *sc)
260{
261	struct hwcmap256 *cm;
262	const u_int8_t *p;
263	int index;
264
265	cm = &sc->sc_cmap;
266	p = rasops_cmap;
267	for (index = 0; index < CMAP_SIZE; index++, p += 3) {
268		cm->r[index] = p[0];
269		cm->g[index] = p[1];
270		cm->b[index] = p[2];
271	}
272}
273
274static void
275xcfb_common_init(struct rasops_info *ri)
276{
277	int cookie;
278
279	/* initialize colormap and cursor hardware */
280	xcfbhwinit((void *)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 = (void *)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(void)
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(void *base)
336{
337	volatile u_int32_t *csr;
338	u_int32_t i;
339	const u_int8_t *p;
340
341	csr = (volatile u_int32_t *)((char *)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(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
394{
395	struct xcfb_softc *sc = v;
396	struct rasops_info *ri = sc->sc_ri;
397	int turnoff, error;
398
399	switch (cmd) {
400	case WSDISPLAYIO_GTYPE:
401		*(u_int *)data = WSDISPLAY_TYPE_XCFB;
402		return (0);
403
404	case WSDISPLAYIO_GINFO:
405#define	wsd_fbip ((struct wsdisplay_fbinfo *)data)
406		wsd_fbip->height = ri->ri_height;
407		wsd_fbip->width = ri->ri_width;
408		wsd_fbip->depth = ri->ri_depth;
409		wsd_fbip->cmsize = CMAP_SIZE;
410#undef fbt
411		return (0);
412
413	case WSDISPLAYIO_GETCMAP:
414		return get_cmap(sc, (struct wsdisplay_cmap *)data);
415
416	case WSDISPLAYIO_PUTCMAP:
417		error = set_cmap(sc, (struct wsdisplay_cmap *)data);
418		if (error == 0)
419			ims332_loadcmap(&sc->sc_cmap);
420		return (error);
421
422	case WSDISPLAYIO_SVIDEO:
423		turnoff = *(int *)data == WSDISPLAYIO_VIDEO_OFF;
424		if (sc->sc_blanked != turnoff) {
425			sc->sc_blanked = turnoff;
426			xcfb_screenblank(sc);
427		}
428		return (0);
429
430	case WSDISPLAYIO_GVIDEO:
431		*(u_int *)data = sc->sc_blanked ?
432		    WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON;
433		return (0);
434
435	case WSDISPLAYIO_GCURPOS:
436		*(struct wsdisplay_curpos *)data = sc->sc_cursor.cc_pos;
437		return (0);
438
439	case WSDISPLAYIO_SCURPOS:
440		set_curpos(sc, (struct wsdisplay_curpos *)data);
441		ims332_set_curpos(sc);
442		return (0);
443
444	case WSDISPLAYIO_GCURMAX:
445		((struct wsdisplay_curpos *)data)->x =
446		((struct wsdisplay_curpos *)data)->y = CURSOR_MAX_SIZE;
447		return (0);
448
449	case WSDISPLAYIO_GCURSOR:
450		return get_cursor(sc, (struct wsdisplay_cursor *)data);
451
452	case WSDISPLAYIO_SCURSOR:
453		return set_cursor(sc, (struct wsdisplay_cursor *)data);
454
455	case WSDISPLAYIO_SMODE:
456		if (*(int *)data == WSDISPLAYIO_MODE_EMUL) {
457			sc->sc_csr |= IMS332_CSR_A_DISABLE_CURSOR;
458			ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
459			xcfb_cmap_init(sc);
460			ims332_loadcmap(&sc->sc_cmap);
461			sc->sc_blanked = 0;
462			xcfb_screenblank(sc);
463		}
464		return (0);
465	}
466	return (EPASSTHROUGH);
467}
468
469static paddr_t
470xcfbmmap(void *v, void *vs, off_t offset, int prot)
471{
472
473	if (offset >= XCFB_FB_SIZE || offset < 0)
474		return (-1);
475	return mips_btop(MIPS_KSEG1_TO_PHYS(XCFB_FB_BASE + offset));
476}
477
478static int
479xcfb_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
480    int *curxp, int *curyp, long *attrp)
481{
482	struct xcfb_softc *sc = v;
483	struct rasops_info *ri = sc->sc_ri;
484	long defattr;
485
486	if (sc->nscreens > 0)
487		return (ENOMEM);
488
489	*cookiep = ri; 		/* one and only for now */
490	*curxp = 0;
491	*curyp = 0;
492	(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
493	*attrp = defattr;
494	sc->nscreens++;
495	return (0);
496}
497
498static void
499xcfb_free_screen(void *v, void *cookie)
500{
501	struct xcfb_softc *sc = v;
502
503	if (sc->sc_ri == &xcfb_console_ri)
504		panic("xcfb_free_screen: console");
505
506	sc->nscreens--;
507}
508
509static int
510xcfb_show_screen(void *v, void *cookie, int waitok,
511    void (*cb)(void *, int, int), void *cbarg)
512{
513
514	return (0);
515}
516
517static int
518xcfbintr(void *v)
519{
520	struct xcfb_softc *sc = v;
521	u_int32_t *intr, i;
522
523	intr = (u_int32_t *)((char *)sc->sc_ri->ri_hw + IOASIC_INTR);
524	i = *intr;
525	i &= ~XINE_INTR_VINT;
526	*intr = i;
527	return (1);
528}
529
530static void
531xcfb_screenblank(struct xcfb_softc *sc)
532{
533	if (sc->sc_blanked)
534		sc->sc_csr |= IMS332_CSR_A_FORCE_BLANK;
535	else
536		sc->sc_csr &= ~IMS332_CSR_A_FORCE_BLANK;
537	ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
538}
539
540static int
541get_cmap(struct xcfb_softc *sc, struct wsdisplay_cmap *p)
542{
543	u_int index = p->index, count = p->count;
544	int error;
545
546	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
547		return (EINVAL);
548
549	error = copyout(&sc->sc_cmap.r[index], p->red, count);
550	if (error)
551		return error;
552	error = copyout(&sc->sc_cmap.g[index], p->green, count);
553	if (error)
554		return error;
555	error = copyout(&sc->sc_cmap.b[index], p->blue, count);
556	return error;
557}
558
559static int
560set_cmap(struct xcfb_softc *sc, struct wsdisplay_cmap *p)
561{
562	struct hwcmap256 cmap;
563	u_int index = p->index, count = p->count;
564	int error;
565
566	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
567		return (EINVAL);
568
569	error = copyin(p->red, &cmap.r[index], count);
570	if (error)
571		return error;
572	error = copyin(p->green, &cmap.g[index], count);
573	if (error)
574		return error;
575	error = copyin(p->blue, &cmap.b[index], count);
576	if (error)
577		return error;
578	memcpy(&sc->sc_cmap.r[index], &cmap.r[index], count);
579	memcpy(&sc->sc_cmap.g[index], &cmap.g[index], count);
580	memcpy(&sc->sc_cmap.b[index], &cmap.b[index], count);
581	return (0);
582}
583
584static int
585set_cursor(struct xcfb_softc *sc, struct wsdisplay_cursor *p)
586{
587#define	cc (&sc->sc_cursor)
588	u_int v, index = 0, count = 0, icount = 0;
589	uint8_t r[2], g[2], b[2], image[512], mask[512];
590	int error;
591
592	v = p->which;
593	if (v & WSDISPLAY_CURSOR_DOCMAP) {
594		index = p->cmap.index;
595		count = p->cmap.count;
596
597		if (index >= 2 || index + count > 2)
598			return (EINVAL);
599		error = copyin(p->cmap.red, &r[index], count);
600		if (error)
601			return error;
602		error = copyin(p->cmap.green, &g[index], count);
603		if (error)
604			return error;
605		error = copyin(p->cmap.blue, &b[index], count);
606		if (error)
607			return error;
608	}
609	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
610		if (p->size.x > CURSOR_MAX_SIZE || p->size.y > CURSOR_MAX_SIZE)
611			return (EINVAL);
612		icount = ((p->size.x < 33) ? 4 : 8) * p->size.y;
613		error = copyin(p->image, image, icount);
614		if (error)
615			return error;
616		error = copyin(p->mask, mask, icount);
617		if (error)
618			return error;
619	}
620
621	if (v & WSDISPLAY_CURSOR_DOCMAP) {
622		memcpy(&cc->cc_color[index], &r[index], count);
623		memcpy(&cc->cc_color[index + 2], &g[index], count);
624		memcpy(&cc->cc_color[index + 4], &b[index], count);
625		ims332_load_curcmap(sc);
626	}
627	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
628		cc->cc_size = p->size;
629		memset(cc->cc_image, 0, sizeof cc->cc_image);
630		memcpy(cc->cc_image, image, icount);
631		memset(cc->cc_mask, 0, sizeof cc->cc_mask);
632		memcpy(cc->cc_mask, mask, icount);
633		ims332_load_curshape(sc);
634	}
635	if (v & WSDISPLAY_CURSOR_DOCUR) {
636		cc->cc_hot = p->hot;
637		if (p->enable)
638			sc->sc_csr &= ~IMS332_CSR_A_DISABLE_CURSOR;
639		else
640			sc->sc_csr |= IMS332_CSR_A_DISABLE_CURSOR;
641		ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
642	}
643	if (v & WSDISPLAY_CURSOR_DOPOS) {
644		set_curpos(sc, &p->pos);
645		ims332_set_curpos(sc);
646	}
647
648	return (0);
649#undef cc
650}
651
652static int
653get_cursor(struct xcfb_softc *sc, struct wsdisplay_cursor *p)
654{
655	return (EPASSTHROUGH); /* XXX */
656}
657
658static void
659set_curpos(struct xcfb_softc *sc, 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(struct hwcmap256 *cm)
678{
679	int i;
680	u_int32_t rgb;
681
682	for (i = 0; i < CMAP_SIZE; i++) {
683		rgb = cm->b[i] << 16 | cm->g[i] << 8 | cm->r[i];
684		ims332_write_reg(IMS332_REG_LUT_BASE + i, rgb);
685	}
686}
687
688static void
689ims332_set_curpos(struct xcfb_softc *sc)
690{
691	struct wsdisplay_curpos *curpos = &sc->sc_cursor.cc_pos;
692	u_int32_t pos;
693	int s;
694
695	s = spltty();
696	pos = (curpos->x & 0xfff) << 12 | (curpos->y & 0xfff);
697	ims332_write_reg(IMS332_REG_CURSOR_LOC, pos);
698	splx(s);
699}
700
701static void
702ims332_load_curcmap(struct xcfb_softc *sc)
703{
704	u_int8_t *cp = sc->sc_cursor.cc_color;
705	u_int32_t rgb;
706
707	/* cursor background */
708	rgb = cp[5] << 16 | cp[3] << 8 | cp[1];
709	ims332_write_reg(IMS332_REG_CURSOR_LUT_1, rgb);
710
711	/* cursor foreground */
712	rgb = cp[4] << 16 | cp[2] << 8 | cp[0];
713	ims332_write_reg(IMS332_REG_CURSOR_LUT_2, rgb);
714}
715
716static void
717ims332_load_curshape(struct xcfb_softc *sc)
718{
719	u_int i, img, msk, bits;
720	u_int8_t u, *ip, *mp;
721
722	ip = (u_int8_t *)sc->sc_cursor.cc_image;
723	mp = (u_int8_t *)sc->sc_cursor.cc_mask;
724
725	i = 0;
726	/* 64 pixel scan line is consisted with 8 halfword cursor ram */
727	while (i < sc->sc_cursor.cc_size.y * 8) {
728		/* pad right half 32 pixel when smaller than 33 */
729		if ((i & 0x4) && sc->sc_cursor.cc_size.x < 33)
730			bits = 0;
731		else {
732			img = *ip++;
733			msk = *mp++;
734			img &= msk;	/* cookie off image */
735			u = (msk & 0x0f) << 4 | (img & 0x0f);
736			bits = shuffle[u];
737			u = (msk & 0xf0) | (img & 0xf0) >> 4;
738			bits = (shuffle[u] << 8) | bits;
739		}
740		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, bits);
741		i += 1;
742	}
743	/* pad unoccupied scan lines */
744	while (i < CURSOR_MAX_SIZE * 8) {
745		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, 0);
746		i += 1;
747	}
748}
749
750static void
751ims332_write_reg(int regno, u_int32_t val)
752{
753	void *high8 = (void *)(ioasic_base + IMS332_HIGH);
754	void *low16 = (void *)(ioasic_base + IMS332_WLOW + (regno << 4));
755
756	*(volatile u_int16_t *)high8 = (val & 0xff0000) >> 8;
757	*(volatile u_int16_t *)low16 = val;
758}
759
760#if 0
761static u_int32_t
762ims332_read_reg(int regno)
763{
764	void *high8 = (void *)(ioasic_base + IMS332_HIGH);
765	void *low16 = (void *)(ioasic_base + IMS332_RLOW) + (regno << 4);
766	u_int v0, v1;
767
768	v1 = *(volatile u_int16_t *)high8;
769	v0 = *(volatile u_int16_t *)low16;
770	return (v1 & 0xff00) << 8 | v0;
771}
772#endif
773