xcfb.c revision 1.53
1/* $NetBSD: xcfb.c,v 1.53 2010/05/15 08:53:27 tsutsui 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.53 2010/05/15 08:53:27 tsutsui 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	uint8_t r[CMAP_SIZE];
62	uint8_t g[CMAP_SIZE];
63	uint8_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	uint8_t cc_color[6];
73	uint64_t cc_image[CURSOR_MAX_SIZE];
74	uint64_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, uint32_t);
155#if 0
156static uint32_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 uint8_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		ri->ri_flg &= ~RI_NO_AUTO;
227		sc->nscreens = 1;
228	}
229	else {
230		ri = malloc(sizeof(struct rasops_info), M_DEVBUF, M_NOWAIT);
231		if (ri == NULL) {
232			printf(": can't alloc memory\n");
233			return;
234		}
235		memset(ri, 0, sizeof(struct rasops_info));
236
237		ri->ri_hw = (void *)ioasic_base;
238		xcfb_common_init(ri);
239		sc->sc_ri = ri;
240	}
241	printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth);
242
243	xcfb_cmap_init(sc);
244
245	sc->sc_vaddr = ta->ta_addr;
246	sc->sc_blanked = 0;
247	sc->sc_csr = IMS332_BPP_8 | IMS332_CSR_A_VTG_ENABLE;
248
249        tc_intr_establish(parent, ta->ta_cookie, IPL_TTY, xcfbintr, sc);
250
251	waa.console = console;
252	waa.scrdata = &xcfb_screenlist;
253	waa.accessops = &xcfb_accessops;
254	waa.accesscookie = sc;
255
256	config_found(self, &waa, wsemuldisplaydevprint);
257}
258
259static void
260xcfb_cmap_init(struct xcfb_softc *sc)
261{
262	struct hwcmap256 *cm;
263	const uint8_t *p;
264	int index;
265
266	cm = &sc->sc_cmap;
267	p = rasops_cmap;
268	for (index = 0; index < CMAP_SIZE; index++, p += 3) {
269		cm->r[index] = p[0];
270		cm->g[index] = p[1];
271		cm->b[index] = p[2];
272	}
273}
274
275static void
276xcfb_common_init(struct rasops_info *ri)
277{
278	int cookie;
279
280	/* initialize colormap and cursor hardware */
281	xcfbhwinit((void *)ri->ri_hw);
282
283	ri->ri_flg = RI_CENTER;
284	if (ri == &xcfb_console_ri)
285		ri->ri_flg |= RI_NO_AUTO;
286	ri->ri_depth = 8;
287	ri->ri_width = 1024;
288	ri->ri_height = 768;
289	ri->ri_stride = 1024;
290	ri->ri_bits = (void *)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(void *base)
339{
340	volatile uint32_t *csr;
341	uint32_t i;
342	const uint8_t *p;
343
344	csr = (volatile uint32_t *)((char *)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		uint32_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, void *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	uint32_t *intr, i;
525
526	intr = (uint32_t *)((char *)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	uint32_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	uint32_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	uint8_t *cp = sc->sc_cursor.cc_color;
708	uint32_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	uint8_t u, *ip, *mp;
724
725	ip = (uint8_t *)sc->sc_cursor.cc_image;
726	mp = (uint8_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, uint32_t val)
755{
756	void *high8 = (void *)(ioasic_base + IMS332_HIGH);
757	void *low16 = (void *)(ioasic_base + IMS332_WLOW + (regno << 4));
758
759	*(volatile uint16_t *)high8 = (val & 0xff0000) >> 8;
760	*(volatile uint16_t *)low16 = val;
761}
762
763#if 0
764static uint32_t
765ims332_read_reg(int regno)
766{
767	void *high8 = (void *)(ioasic_base + IMS332_HIGH);
768	void *low16 = (void *)(ioasic_base + IMS332_RLOW) + (regno << 4);
769	u_int v0, v1;
770
771	v1 = *(volatile uint16_t *)high8;
772	v0 = *(volatile uint16_t *)low16;
773	return (v1 & 0xff00) << 8 | v0;
774}
775#endif
776