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