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