xcfb.c revision 1.56
1/* $NetBSD: xcfb.c,v 1.56 2018/01/24 05:35:58 riastradh 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.56 2018/01/24 05:35:58 riastradh 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
57struct hwcmap256 {
58#define	CMAP_SIZE	256	/* 256 R/G/B entries */
59	uint8_t r[CMAP_SIZE];
60	uint8_t g[CMAP_SIZE];
61	uint8_t b[CMAP_SIZE];
62};
63
64struct hwcursor64 {
65	struct wsdisplay_curpos cc_pos;
66	struct wsdisplay_curpos cc_hot;
67	struct wsdisplay_curpos cc_size;
68	struct wsdisplay_curpos cc_magic;	/* not used by PMAG-DV */
69#define	CURSOR_MAX_SIZE	64
70	uint8_t cc_color[6];
71	uint64_t cc_image[CURSOR_MAX_SIZE];
72	uint64_t cc_mask[CURSOR_MAX_SIZE];
73};
74
75#define	XCFB_FB_BASE	(XINE_PHYS_CFB_START + 0x2000000)
76#define	XCFB_FB_SIZE	0x100000
77
78#define	IMS332_HIGH	(IOASIC_SLOT_5_START)
79#define	IMS332_RLOW	(IOASIC_SLOT_7_START)
80#define	IMS332_WLOW	(IOASIC_SLOT_7_START + 0x20000)
81
82struct xcfb_softc {
83	vaddr_t sc_vaddr;
84	size_t sc_size;
85	struct rasops_info *sc_ri;
86	struct hwcmap256 sc_cmap;	/* software copy of colormap */
87	struct hwcursor64 sc_cursor;	/* software copy of cursor */
88	int sc_blanked;
89	/* XXX MAXINE can take PMAG-DV vertical retrace interrupt XXX */
90	int nscreens;
91	/* cursor coordinate is located at upper-left corner */
92	int sc_csr;			/* software copy of IMS332 CSR A */
93};
94
95static int  xcfbmatch(device_t, cfdata_t, void *);
96static void xcfbattach(device_t, device_t, void *);
97
98CFATTACH_DECL_NEW(xcfb, sizeof(struct xcfb_softc),
99    xcfbmatch, xcfbattach, NULL, NULL);
100
101static tc_addr_t xcfb_consaddr;
102static struct rasops_info xcfb_console_ri;
103static void xcfb_common_init(struct rasops_info *);
104static void xcfbhwinit(void *);
105int xcfb_cnattach(void);
106
107struct wsscreen_descr xcfb_stdscreen = {
108	"std", 0, 0,
109	0, /* textops */
110	0, 0,
111	WSSCREEN_REVERSE
112};
113
114static const struct wsscreen_descr *_xcfb_scrlist[] = {
115	&xcfb_stdscreen,
116};
117
118static const struct wsscreen_list xcfb_screenlist = {
119	sizeof(_xcfb_scrlist) / sizeof(struct wsscreen_descr *), _xcfb_scrlist
120};
121
122static int	xcfbioctl(void *, void *, u_long, void *, int, struct lwp *);
123static paddr_t	xcfbmmap(void *, void *, off_t, int);
124
125static int	xcfb_alloc_screen(void *, const struct wsscreen_descr *,
126				       void **, int *, int *, long *);
127static void	xcfb_free_screen(void *, void *);
128static int	xcfb_show_screen(void *, void *, int,
129				      void (*) (void *, int, int), void *);
130
131static const struct wsdisplay_accessops xcfb_accessops = {
132	xcfbioctl,
133	xcfbmmap,
134	xcfb_alloc_screen,
135	xcfb_free_screen,
136	xcfb_show_screen,
137	0 /* load_font */
138};
139
140static int  xcfbintr(void *);
141static void xcfb_screenblank(struct xcfb_softc *);
142static void xcfb_cmap_init(struct xcfb_softc *);
143static int  set_cmap(struct xcfb_softc *, struct wsdisplay_cmap *);
144static int  get_cmap(struct xcfb_softc *, struct wsdisplay_cmap *);
145static int  set_cursor(struct xcfb_softc *, struct wsdisplay_cursor *);
146static int  get_cursor(struct xcfb_softc *, struct wsdisplay_cursor *);
147static void set_curpos(struct xcfb_softc *, struct wsdisplay_curpos *);
148static void ims332_loadcmap(struct hwcmap256 *);
149static void ims332_set_curpos(struct xcfb_softc *);
150static void ims332_load_curcmap(struct xcfb_softc *);
151static void ims332_load_curshape(struct xcfb_softc *);
152static void ims332_write_reg(int, uint32_t);
153#if 0
154static uint32_t ims332_read_reg(int);
155#endif
156
157extern long ioasic_base;	/* XXX */
158
159/*
160 * Compose 2 bit/pixel cursor image.
161 *   M M M M I I I I		M I M I M I M I
162 *	[ before ]		   [ after ]
163 *   3 2 1 0 3 2 1 0		3 3 2 2 1 1 0 0
164 *   7 6 5 4 7 6 5 4		7 7 6 6 5 5 4 4
165 */
166static const uint8_t shuffle[256] = {
167	0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15,
168	0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55,
169	0x02, 0x03, 0x06, 0x07, 0x12, 0x13, 0x16, 0x17,
170	0x42, 0x43, 0x46, 0x47, 0x52, 0x53, 0x56, 0x57,
171	0x08, 0x09, 0x0c, 0x0d, 0x18, 0x19, 0x1c, 0x1d,
172	0x48, 0x49, 0x4c, 0x4d, 0x58, 0x59, 0x5c, 0x5d,
173	0x0a, 0x0b, 0x0e, 0x0f, 0x1a, 0x1b, 0x1e, 0x1f,
174	0x4a, 0x4b, 0x4e, 0x4f, 0x5a, 0x5b, 0x5e, 0x5f,
175	0x20, 0x21, 0x24, 0x25, 0x30, 0x31, 0x34, 0x35,
176	0x60, 0x61, 0x64, 0x65, 0x70, 0x71, 0x74, 0x75,
177	0x22, 0x23, 0x26, 0x27, 0x32, 0x33, 0x36, 0x37,
178	0x62, 0x63, 0x66, 0x67, 0x72, 0x73, 0x76, 0x77,
179	0x28, 0x29, 0x2c, 0x2d, 0x38, 0x39, 0x3c, 0x3d,
180	0x68, 0x69, 0x6c, 0x6d, 0x78, 0x79, 0x7c, 0x7d,
181	0x2a, 0x2b, 0x2e, 0x2f, 0x3a, 0x3b, 0x3e, 0x3f,
182	0x6a, 0x6b, 0x6e, 0x6f, 0x7a, 0x7b, 0x7e, 0x7f,
183	0x80, 0x81, 0x84, 0x85, 0x90, 0x91, 0x94, 0x95,
184	0xc0, 0xc1, 0xc4, 0xc5, 0xd0, 0xd1, 0xd4, 0xd5,
185	0x82, 0x83, 0x86, 0x87, 0x92, 0x93, 0x96, 0x97,
186	0xc2, 0xc3, 0xc6, 0xc7, 0xd2, 0xd3, 0xd6, 0xd7,
187	0x88, 0x89, 0x8c, 0x8d, 0x98, 0x99, 0x9c, 0x9d,
188	0xc8, 0xc9, 0xcc, 0xcd, 0xd8, 0xd9, 0xdc, 0xdd,
189	0x8a, 0x8b, 0x8e, 0x8f, 0x9a, 0x9b, 0x9e, 0x9f,
190	0xca, 0xcb, 0xce, 0xcf, 0xda, 0xdb, 0xde, 0xdf,
191	0xa0, 0xa1, 0xa4, 0xa5, 0xb0, 0xb1, 0xb4, 0xb5,
192	0xe0, 0xe1, 0xe4, 0xe5, 0xf0, 0xf1, 0xf4, 0xf5,
193	0xa2, 0xa3, 0xa6, 0xa7, 0xb2, 0xb3, 0xb6, 0xb7,
194	0xe2, 0xe3, 0xe6, 0xe7, 0xf2, 0xf3, 0xf6, 0xf7,
195	0xa8, 0xa9, 0xac, 0xad, 0xb8, 0xb9, 0xbc, 0xbd,
196	0xe8, 0xe9, 0xec, 0xed, 0xf8, 0xf9, 0xfc, 0xfd,
197	0xaa, 0xab, 0xae, 0xaf, 0xba, 0xbb, 0xbe, 0xbf,
198	0xea, 0xeb, 0xee, 0xef, 0xfa, 0xfb, 0xfe, 0xff,
199};
200
201static int
202xcfbmatch(device_t parent, cfdata_t match, void *aux)
203{
204	struct tc_attach_args *ta = aux;
205
206	if (strncmp("PMAG-DV ", ta->ta_modname, TC_ROM_LLEN) != 0)
207		return (0);
208
209	return (1);
210}
211
212static void
213xcfbattach(device_t parent, device_t self, void *aux)
214{
215	struct xcfb_softc *sc = device_private(self);
216	struct tc_attach_args *ta = aux;
217	struct rasops_info *ri;
218	struct wsemuldisplaydev_attach_args waa;
219	int console;
220
221	console = (ta->ta_addr == xcfb_consaddr);
222	if (console) {
223		sc->sc_ri = ri = &xcfb_console_ri;
224		ri->ri_flg &= ~RI_NO_AUTO;
225		sc->nscreens = 1;
226	}
227	else {
228		ri = malloc(sizeof(struct rasops_info), M_DEVBUF, M_NOWAIT);
229		if (ri == NULL) {
230			printf(": can't alloc memory\n");
231			return;
232		}
233		memset(ri, 0, sizeof(struct rasops_info));
234
235		ri->ri_hw = (void *)ioasic_base;
236		xcfb_common_init(ri);
237		sc->sc_ri = ri;
238	}
239	printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth);
240
241	xcfb_cmap_init(sc);
242
243	sc->sc_vaddr = ta->ta_addr;
244	sc->sc_blanked = 0;
245	sc->sc_csr = IMS332_BPP_8 | IMS332_CSR_A_VTG_ENABLE;
246
247        tc_intr_establish(parent, ta->ta_cookie, IPL_TTY, xcfbintr, sc);
248
249	waa.console = console;
250	waa.scrdata = &xcfb_screenlist;
251	waa.accessops = &xcfb_accessops;
252	waa.accesscookie = sc;
253
254	config_found(self, &waa, wsemuldisplaydevprint);
255}
256
257static void
258xcfb_cmap_init(struct xcfb_softc *sc)
259{
260	struct hwcmap256 *cm;
261	const uint8_t *p;
262	int index;
263
264	cm = &sc->sc_cmap;
265	p = rasops_cmap;
266	for (index = 0; index < CMAP_SIZE; index++, p += 3) {
267		cm->r[index] = p[0];
268		cm->g[index] = p[1];
269		cm->b[index] = p[2];
270	}
271}
272
273static void
274xcfb_common_init(struct rasops_info *ri)
275{
276	int cookie;
277
278	/* initialize colormap and cursor hardware */
279	xcfbhwinit((void *)ri->ri_hw);
280
281	ri->ri_flg = RI_CENTER;
282	if (ri == &xcfb_console_ri)
283		ri->ri_flg |= RI_NO_AUTO;
284	ri->ri_depth = 8;
285	ri->ri_width = 1024;
286	ri->ri_height = 768;
287	ri->ri_stride = 1024;
288	ri->ri_bits = (void *)MIPS_PHYS_TO_KSEG1(XCFB_FB_BASE);
289
290	/* clear the screen */
291	memset(ri->ri_bits, 0, ri->ri_stride * ri->ri_height);
292
293	wsfont_init();
294	/* prefer 12 pixel wide font */
295	cookie = wsfont_find(NULL, 12, 0, 0, WSDISPLAY_FONTORDER_L2R,
296	    WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP);
297	if (cookie <= 0)
298		cookie = wsfont_find(NULL, 0, 0, 0, WSDISPLAY_FONTORDER_L2R,
299		    WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP);
300	if (cookie <= 0) {
301		printf("xcfb: font table is empty\n");
302		return;
303	}
304
305	if (wsfont_lock(cookie, &ri->ri_font)) {
306		printf("xcfb: couldn't lock font\n");
307		return;
308	}
309	ri->ri_wsfcookie = cookie;
310
311	rasops_init(ri, 34, 80);
312
313	/* XXX shouldn't be global */
314	xcfb_stdscreen.nrows = ri->ri_rows;
315	xcfb_stdscreen.ncols = ri->ri_cols;
316	xcfb_stdscreen.textops = &ri->ri_ops;
317	xcfb_stdscreen.capabilities = ri->ri_caps;
318}
319
320int
321xcfb_cnattach(void)
322{
323	struct rasops_info *ri;
324	long defattr;
325
326	ri = &xcfb_console_ri;
327	ri->ri_hw = (void *)ioasic_base;
328	xcfb_common_init(ri);
329	(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
330	wsdisplay_cnattach(&xcfb_stdscreen, ri, 0, 0, defattr);
331	xcfb_consaddr = MIPS_PHYS_TO_KSEG1(XINE_PHYS_CFB_START);
332	return (0);
333}
334
335static void
336xcfbhwinit(void *base)
337{
338	volatile uint32_t *csr;
339	uint32_t i;
340	const uint8_t *p;
341
342	csr = (volatile uint32_t *)((char *)base + IOASIC_CSR);
343	i = *csr;
344	i &= ~XINE_CSR_VDAC_ENABLE;
345	*csr = i;
346	DELAY(50);
347	i |= XINE_CSR_VDAC_ENABLE;
348	*csr = i;
349	DELAY(50);
350	ims332_write_reg(IMS332_REG_BOOT, 0x2c);
351	ims332_write_reg(IMS332_REG_CSR_A,
352		IMS332_BPP_8|IMS332_CSR_A_DISABLE_CURSOR);
353	ims332_write_reg(IMS332_REG_HALF_SYNCH, 0x10);
354	ims332_write_reg(IMS332_REG_BACK_PORCH, 0x21);
355	ims332_write_reg(IMS332_REG_DISPLAY, 0x100);
356	ims332_write_reg(IMS332_REG_SHORT_DIS, 0x5d);
357	ims332_write_reg(IMS332_REG_BROAD_PULSE, 0x9f);
358	ims332_write_reg(IMS332_REG_LINE_TIME, 0x146);
359	ims332_write_reg(IMS332_REG_V_SYNC, 0x0c);
360	ims332_write_reg(IMS332_REG_V_PRE_EQUALIZE, 0x02);
361	ims332_write_reg(IMS332_REG_V_POST_EQUALIZE, 0x02);
362	ims332_write_reg(IMS332_REG_V_BLANK, 0x2a);
363	ims332_write_reg(IMS332_REG_V_DISPLAY, 0x600);
364	ims332_write_reg(IMS332_REG_LINE_START, 0x10);
365	ims332_write_reg(IMS332_REG_MEM_INIT, 0x0a);
366	ims332_write_reg(IMS332_REG_COLOR_MASK, 0xffffff);
367	ims332_write_reg(IMS332_REG_CSR_A,
368		IMS332_BPP_8|IMS332_CSR_A_VTG_ENABLE);
369
370	/* build sane colormap */
371	p = rasops_cmap;
372	for (i = 0; i < CMAP_SIZE; i++, p += 3) {
373		uint32_t bgr;
374
375		bgr = p[2] << 16 | p[1] << 8 | p[0];
376		ims332_write_reg(IMS332_REG_LUT_BASE + i, bgr);
377	}
378
379	/* clear out cursor image */
380	for (i = 0; i < 512; i++)
381		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, 0);
382
383	/*
384	 * 2 bit/pixel cursor.  Assign MSB for cursor mask and LSB for
385	 * cursor image.  LUT_1 for mask color, while LUT_2 for
386	 * image color.  LUT_0 will be never used.
387	 */
388	ims332_write_reg(IMS332_REG_CURSOR_LUT_0, 0);
389	ims332_write_reg(IMS332_REG_CURSOR_LUT_1, 0xffffff);
390	ims332_write_reg(IMS332_REG_CURSOR_LUT_2, 0xffffff);
391}
392
393static int
394xcfbioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
395{
396	struct xcfb_softc *sc = v;
397	struct rasops_info *ri = sc->sc_ri;
398	int turnoff, error;
399
400	switch (cmd) {
401	case WSDISPLAYIO_GTYPE:
402		*(u_int *)data = WSDISPLAY_TYPE_XCFB;
403		return (0);
404
405	case WSDISPLAYIO_GINFO:
406#define	wsd_fbip ((struct wsdisplay_fbinfo *)data)
407		wsd_fbip->height = ri->ri_height;
408		wsd_fbip->width = ri->ri_width;
409		wsd_fbip->depth = ri->ri_depth;
410		wsd_fbip->cmsize = CMAP_SIZE;
411#undef fbt
412		return (0);
413
414	case WSDISPLAYIO_GETCMAP:
415		return get_cmap(sc, (struct wsdisplay_cmap *)data);
416
417	case WSDISPLAYIO_PUTCMAP:
418		error = set_cmap(sc, (struct wsdisplay_cmap *)data);
419		if (error == 0)
420			ims332_loadcmap(&sc->sc_cmap);
421		return (error);
422
423	case WSDISPLAYIO_SVIDEO:
424		turnoff = *(int *)data == WSDISPLAYIO_VIDEO_OFF;
425		if (sc->sc_blanked != turnoff) {
426			sc->sc_blanked = turnoff;
427			xcfb_screenblank(sc);
428		}
429		return (0);
430
431	case WSDISPLAYIO_GVIDEO:
432		*(u_int *)data = sc->sc_blanked ?
433		    WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON;
434		return (0);
435
436	case WSDISPLAYIO_GCURPOS:
437		*(struct wsdisplay_curpos *)data = sc->sc_cursor.cc_pos;
438		return (0);
439
440	case WSDISPLAYIO_SCURPOS:
441		set_curpos(sc, (struct wsdisplay_curpos *)data);
442		ims332_set_curpos(sc);
443		return (0);
444
445	case WSDISPLAYIO_GCURMAX:
446		((struct wsdisplay_curpos *)data)->x =
447		((struct wsdisplay_curpos *)data)->y = CURSOR_MAX_SIZE;
448		return (0);
449
450	case WSDISPLAYIO_GCURSOR:
451		return get_cursor(sc, (struct wsdisplay_cursor *)data);
452
453	case WSDISPLAYIO_SCURSOR:
454		return set_cursor(sc, (struct wsdisplay_cursor *)data);
455
456	case WSDISPLAYIO_SMODE:
457		if (*(int *)data == WSDISPLAYIO_MODE_EMUL) {
458			sc->sc_csr |= IMS332_CSR_A_DISABLE_CURSOR;
459			ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
460			xcfb_cmap_init(sc);
461			ims332_loadcmap(&sc->sc_cmap);
462			sc->sc_blanked = 0;
463			xcfb_screenblank(sc);
464		}
465		return (0);
466	}
467	return (EPASSTHROUGH);
468}
469
470static paddr_t
471xcfbmmap(void *v, void *vs, off_t offset, int prot)
472{
473
474	if (offset >= XCFB_FB_SIZE || offset < 0)
475		return (-1);
476	return mips_btop(MIPS_KSEG1_TO_PHYS(XCFB_FB_BASE + offset));
477}
478
479static int
480xcfb_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
481    int *curxp, int *curyp, 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(void *v, void *cookie)
501{
502	struct xcfb_softc *sc = v;
503
504	if (sc->sc_ri == &xcfb_console_ri)
505		panic("xcfb_free_screen: console");
506
507	sc->nscreens--;
508}
509
510static int
511xcfb_show_screen(void *v, void *cookie, int waitok,
512    void (*cb)(void *, int, int), void *cbarg)
513{
514
515	return (0);
516}
517
518static int
519xcfbintr(void *v)
520{
521	struct xcfb_softc *sc = v;
522	uint32_t *intr, i;
523
524	intr = (uint32_t *)((char *)sc->sc_ri->ri_hw + IOASIC_INTR);
525	i = *intr;
526	i &= ~XINE_INTR_VINT;
527	*intr = i;
528	return (1);
529}
530
531static void
532xcfb_screenblank(struct xcfb_softc *sc)
533{
534	if (sc->sc_blanked)
535		sc->sc_csr |= IMS332_CSR_A_FORCE_BLANK;
536	else
537		sc->sc_csr &= ~IMS332_CSR_A_FORCE_BLANK;
538	ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
539}
540
541static int
542get_cmap(struct xcfb_softc *sc, struct wsdisplay_cmap *p)
543{
544	u_int index = p->index, count = p->count;
545	int error;
546
547	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
548		return (EINVAL);
549
550	error = copyout(&sc->sc_cmap.r[index], p->red, count);
551	if (error)
552		return error;
553	error = copyout(&sc->sc_cmap.g[index], p->green, count);
554	if (error)
555		return error;
556	error = copyout(&sc->sc_cmap.b[index], p->blue, count);
557	return error;
558}
559
560static int
561set_cmap(struct xcfb_softc *sc, struct wsdisplay_cmap *p)
562{
563	struct hwcmap256 cmap;
564	u_int index = p->index, count = p->count;
565	int error;
566
567	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
568		return (EINVAL);
569
570	error = copyin(p->red, &cmap.r[index], count);
571	if (error)
572		return error;
573	error = copyin(p->green, &cmap.g[index], count);
574	if (error)
575		return error;
576	error = copyin(p->blue, &cmap.b[index], count);
577	if (error)
578		return error;
579	memcpy(&sc->sc_cmap.r[index], &cmap.r[index], count);
580	memcpy(&sc->sc_cmap.g[index], &cmap.g[index], count);
581	memcpy(&sc->sc_cmap.b[index], &cmap.b[index], count);
582	return (0);
583}
584
585static int
586set_cursor(struct xcfb_softc *sc, struct wsdisplay_cursor *p)
587{
588#define	cc (&sc->sc_cursor)
589	u_int v, index = 0, count = 0, icount = 0;
590	uint8_t r[2], g[2], b[2], image[512], mask[512];
591	int error;
592
593	v = p->which;
594	if (v & WSDISPLAY_CURSOR_DOCMAP) {
595		index = p->cmap.index;
596		count = p->cmap.count;
597
598		if (index >= 2 || count > 2 - index)
599			return (EINVAL);
600		error = copyin(p->cmap.red, &r[index], count);
601		if (error)
602			return error;
603		error = copyin(p->cmap.green, &g[index], count);
604		if (error)
605			return error;
606		error = copyin(p->cmap.blue, &b[index], count);
607		if (error)
608			return error;
609	}
610	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
611		if (p->size.x > CURSOR_MAX_SIZE || p->size.y > CURSOR_MAX_SIZE)
612			return (EINVAL);
613		icount = ((p->size.x < 33) ? 4 : 8) * p->size.y;
614		error = copyin(p->image, image, icount);
615		if (error)
616			return error;
617		error = copyin(p->mask, mask, icount);
618		if (error)
619			return error;
620	}
621
622	if (v & WSDISPLAY_CURSOR_DOCMAP) {
623		memcpy(&cc->cc_color[index], &r[index], count);
624		memcpy(&cc->cc_color[index + 2], &g[index], count);
625		memcpy(&cc->cc_color[index + 4], &b[index], count);
626		ims332_load_curcmap(sc);
627	}
628	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
629		cc->cc_size = p->size;
630		memset(cc->cc_image, 0, sizeof cc->cc_image);
631		memcpy(cc->cc_image, image, icount);
632		memset(cc->cc_mask, 0, sizeof cc->cc_mask);
633		memcpy(cc->cc_mask, mask, icount);
634		ims332_load_curshape(sc);
635	}
636	if (v & WSDISPLAY_CURSOR_DOCUR) {
637		cc->cc_hot = p->hot;
638		if (p->enable)
639			sc->sc_csr &= ~IMS332_CSR_A_DISABLE_CURSOR;
640		else
641			sc->sc_csr |= IMS332_CSR_A_DISABLE_CURSOR;
642		ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
643	}
644	if (v & WSDISPLAY_CURSOR_DOPOS) {
645		set_curpos(sc, &p->pos);
646		ims332_set_curpos(sc);
647	}
648
649	return (0);
650#undef cc
651}
652
653static int
654get_cursor(struct xcfb_softc *sc, struct wsdisplay_cursor *p)
655{
656	return (EPASSTHROUGH); /* XXX */
657}
658
659static void
660set_curpos(struct xcfb_softc *sc, struct wsdisplay_curpos *curpos)
661{
662	struct rasops_info *ri = sc->sc_ri;
663	int x = curpos->x, y = curpos->y;
664
665	if (y < 0)
666		y = 0;
667	else if (y > ri->ri_height)
668		y = ri->ri_height;
669	if (x < 0)
670		x = 0;
671	else if (x > ri->ri_width)
672		x = ri->ri_width;
673	sc->sc_cursor.cc_pos.x = x;
674	sc->sc_cursor.cc_pos.y = y;
675}
676
677static void
678ims332_loadcmap(struct hwcmap256 *cm)
679{
680	int i;
681	uint32_t rgb;
682
683	for (i = 0; i < CMAP_SIZE; i++) {
684		rgb = cm->b[i] << 16 | cm->g[i] << 8 | cm->r[i];
685		ims332_write_reg(IMS332_REG_LUT_BASE + i, rgb);
686	}
687}
688
689static void
690ims332_set_curpos(struct xcfb_softc *sc)
691{
692	struct wsdisplay_curpos *curpos = &sc->sc_cursor.cc_pos;
693	uint32_t pos;
694	int s;
695
696	s = spltty();
697	pos = (curpos->x & 0xfff) << 12 | (curpos->y & 0xfff);
698	ims332_write_reg(IMS332_REG_CURSOR_LOC, pos);
699	splx(s);
700}
701
702static void
703ims332_load_curcmap(struct xcfb_softc *sc)
704{
705	uint8_t *cp = sc->sc_cursor.cc_color;
706	uint32_t rgb;
707
708	/* cursor background */
709	rgb = cp[5] << 16 | cp[3] << 8 | cp[1];
710	ims332_write_reg(IMS332_REG_CURSOR_LUT_1, rgb);
711
712	/* cursor foreground */
713	rgb = cp[4] << 16 | cp[2] << 8 | cp[0];
714	ims332_write_reg(IMS332_REG_CURSOR_LUT_2, rgb);
715}
716
717static void
718ims332_load_curshape(struct xcfb_softc *sc)
719{
720	u_int i, img, msk, bits;
721	uint8_t u, *ip, *mp;
722
723	ip = (uint8_t *)sc->sc_cursor.cc_image;
724	mp = (uint8_t *)sc->sc_cursor.cc_mask;
725
726	i = 0;
727	/* 64 pixel scan line is consisted with 8 halfword cursor ram */
728	while (i < sc->sc_cursor.cc_size.y * 8) {
729		/* pad right half 32 pixel when smaller than 33 */
730		if ((i & 0x4) && sc->sc_cursor.cc_size.x < 33)
731			bits = 0;
732		else {
733			img = *ip++;
734			msk = *mp++;
735			img &= msk;	/* cookie off image */
736			u = (msk & 0x0f) << 4 | (img & 0x0f);
737			bits = shuffle[u];
738			u = (msk & 0xf0) | (img & 0xf0) >> 4;
739			bits = (shuffle[u] << 8) | bits;
740		}
741		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, bits);
742		i += 1;
743	}
744	/* pad unoccupied scan lines */
745	while (i < CURSOR_MAX_SIZE * 8) {
746		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, 0);
747		i += 1;
748	}
749}
750
751static void
752ims332_write_reg(int regno, uint32_t val)
753{
754	void *high8 = (void *)(ioasic_base + IMS332_HIGH);
755	void *low16 = (void *)(ioasic_base + IMS332_WLOW + (regno << 4));
756
757	*(volatile uint16_t *)high8 = (val & 0xff0000) >> 8;
758	*(volatile uint16_t *)low16 = val;
759}
760
761#if 0
762static uint32_t
763ims332_read_reg(int regno)
764{
765	void *high8 = (void *)(ioasic_base + IMS332_HIGH);
766	void *low16 = (void *)(ioasic_base + IMS332_RLOW) + (regno << 4);
767	u_int v0, v1;
768
769	v1 = *(volatile uint16_t *)high8;
770	v0 = *(volatile uint16_t *)low16;
771	return (v1 & 0xff00) << 8 | v0;
772}
773#endif
774