xcfb.c revision 1.58
1/* $NetBSD: xcfb.c,v 1.58 2021/04/24 23:36:59 thorpej 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.58 2021/04/24 23:36:59 thorpej 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_WAITOK | M_ZERO);
229		ri->ri_hw = (void *)ioasic_base;
230		xcfb_common_init(ri);
231		sc->sc_ri = ri;
232	}
233	printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth);
234
235	xcfb_cmap_init(sc);
236
237	sc->sc_vaddr = ta->ta_addr;
238	sc->sc_blanked = 0;
239	sc->sc_csr = IMS332_BPP_8 | IMS332_CSR_A_VTG_ENABLE;
240
241        tc_intr_establish(parent, ta->ta_cookie, IPL_TTY, xcfbintr, sc);
242
243	waa.console = console;
244	waa.scrdata = &xcfb_screenlist;
245	waa.accessops = &xcfb_accessops;
246	waa.accesscookie = sc;
247
248	config_found(self, &waa, wsemuldisplaydevprint, CFARG_EOL);
249}
250
251static void
252xcfb_cmap_init(struct xcfb_softc *sc)
253{
254	struct hwcmap256 *cm;
255	const uint8_t *p;
256	int index;
257
258	cm = &sc->sc_cmap;
259	p = rasops_cmap;
260	for (index = 0; index < CMAP_SIZE; index++, p += 3) {
261		cm->r[index] = p[0];
262		cm->g[index] = p[1];
263		cm->b[index] = p[2];
264	}
265}
266
267static void
268xcfb_common_init(struct rasops_info *ri)
269{
270	int cookie;
271
272	/* initialize colormap and cursor hardware */
273	xcfbhwinit((void *)ri->ri_hw);
274
275	ri->ri_flg = RI_CENTER;
276	if (ri == &xcfb_console_ri)
277		ri->ri_flg |= RI_NO_AUTO;
278	ri->ri_depth = 8;
279	ri->ri_width = 1024;
280	ri->ri_height = 768;
281	ri->ri_stride = 1024;
282	ri->ri_bits = (void *)MIPS_PHYS_TO_KSEG1(XCFB_FB_BASE);
283
284	/* clear the screen */
285	memset(ri->ri_bits, 0, ri->ri_stride * ri->ri_height);
286
287	wsfont_init();
288	/* prefer 12 pixel wide font */
289	cookie = wsfont_find(NULL, 12, 0, 0, WSDISPLAY_FONTORDER_L2R,
290	    WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP);
291	if (cookie <= 0)
292		cookie = wsfont_find(NULL, 0, 0, 0, WSDISPLAY_FONTORDER_L2R,
293		    WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP);
294	if (cookie <= 0) {
295		printf("xcfb: font table is empty\n");
296		return;
297	}
298
299	if (wsfont_lock(cookie, &ri->ri_font)) {
300		printf("xcfb: couldn't lock font\n");
301		return;
302	}
303	ri->ri_wsfcookie = cookie;
304
305	rasops_init(ri, 34, 80);
306
307	/* XXX shouldn't be global */
308	xcfb_stdscreen.nrows = ri->ri_rows;
309	xcfb_stdscreen.ncols = ri->ri_cols;
310	xcfb_stdscreen.textops = &ri->ri_ops;
311	xcfb_stdscreen.capabilities = ri->ri_caps;
312}
313
314int
315xcfb_cnattach(void)
316{
317	struct rasops_info *ri;
318	long defattr;
319
320	ri = &xcfb_console_ri;
321	ri->ri_hw = (void *)ioasic_base;
322	xcfb_common_init(ri);
323	(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
324	wsdisplay_cnattach(&xcfb_stdscreen, ri, 0, 0, defattr);
325	xcfb_consaddr = MIPS_PHYS_TO_KSEG1(XINE_PHYS_CFB_START);
326	return (0);
327}
328
329static void
330xcfbhwinit(void *base)
331{
332	volatile uint32_t *csr;
333	uint32_t i;
334	const uint8_t *p;
335
336	csr = (volatile uint32_t *)((char *)base + IOASIC_CSR);
337	i = *csr;
338	i &= ~XINE_CSR_VDAC_ENABLE;
339	*csr = i;
340	DELAY(50);
341	i |= XINE_CSR_VDAC_ENABLE;
342	*csr = i;
343	DELAY(50);
344	ims332_write_reg(IMS332_REG_BOOT, 0x2c);
345	ims332_write_reg(IMS332_REG_CSR_A,
346		IMS332_BPP_8|IMS332_CSR_A_DISABLE_CURSOR);
347	ims332_write_reg(IMS332_REG_HALF_SYNCH, 0x10);
348	ims332_write_reg(IMS332_REG_BACK_PORCH, 0x21);
349	ims332_write_reg(IMS332_REG_DISPLAY, 0x100);
350	ims332_write_reg(IMS332_REG_SHORT_DIS, 0x5d);
351	ims332_write_reg(IMS332_REG_BROAD_PULSE, 0x9f);
352	ims332_write_reg(IMS332_REG_LINE_TIME, 0x146);
353	ims332_write_reg(IMS332_REG_V_SYNC, 0x0c);
354	ims332_write_reg(IMS332_REG_V_PRE_EQUALIZE, 0x02);
355	ims332_write_reg(IMS332_REG_V_POST_EQUALIZE, 0x02);
356	ims332_write_reg(IMS332_REG_V_BLANK, 0x2a);
357	ims332_write_reg(IMS332_REG_V_DISPLAY, 0x600);
358	ims332_write_reg(IMS332_REG_LINE_START, 0x10);
359	ims332_write_reg(IMS332_REG_MEM_INIT, 0x0a);
360	ims332_write_reg(IMS332_REG_COLOR_MASK, 0xffffff);
361	ims332_write_reg(IMS332_REG_CSR_A,
362		IMS332_BPP_8|IMS332_CSR_A_VTG_ENABLE);
363
364	/* build sane colormap */
365	p = rasops_cmap;
366	for (i = 0; i < CMAP_SIZE; i++, p += 3) {
367		uint32_t bgr;
368
369		bgr = p[2] << 16 | p[1] << 8 | p[0];
370		ims332_write_reg(IMS332_REG_LUT_BASE + i, bgr);
371	}
372
373	/* clear out cursor image */
374	for (i = 0; i < 512; i++)
375		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, 0);
376
377	/*
378	 * 2 bit/pixel cursor.  Assign MSB for cursor mask and LSB for
379	 * cursor image.  LUT_1 for mask color, while LUT_2 for
380	 * image color.  LUT_0 will be never used.
381	 */
382	ims332_write_reg(IMS332_REG_CURSOR_LUT_0, 0);
383	ims332_write_reg(IMS332_REG_CURSOR_LUT_1, 0xffffff);
384	ims332_write_reg(IMS332_REG_CURSOR_LUT_2, 0xffffff);
385}
386
387static int
388xcfbioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
389{
390	struct xcfb_softc *sc = v;
391	struct rasops_info *ri = sc->sc_ri;
392	int turnoff, error;
393
394	switch (cmd) {
395	case WSDISPLAYIO_GTYPE:
396		*(u_int *)data = WSDISPLAY_TYPE_XCFB;
397		return (0);
398
399	case WSDISPLAYIO_GINFO:
400#define	wsd_fbip ((struct wsdisplay_fbinfo *)data)
401		wsd_fbip->height = ri->ri_height;
402		wsd_fbip->width = ri->ri_width;
403		wsd_fbip->depth = ri->ri_depth;
404		wsd_fbip->cmsize = CMAP_SIZE;
405#undef fbt
406		return (0);
407
408	case WSDISPLAYIO_GETCMAP:
409		return get_cmap(sc, (struct wsdisplay_cmap *)data);
410
411	case WSDISPLAYIO_PUTCMAP:
412		error = set_cmap(sc, (struct wsdisplay_cmap *)data);
413		if (error == 0)
414			ims332_loadcmap(&sc->sc_cmap);
415		return (error);
416
417	case WSDISPLAYIO_SVIDEO:
418		turnoff = *(int *)data == WSDISPLAYIO_VIDEO_OFF;
419		if (sc->sc_blanked != turnoff) {
420			sc->sc_blanked = turnoff;
421			xcfb_screenblank(sc);
422		}
423		return (0);
424
425	case WSDISPLAYIO_GVIDEO:
426		*(u_int *)data = sc->sc_blanked ?
427		    WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON;
428		return (0);
429
430	case WSDISPLAYIO_GCURPOS:
431		*(struct wsdisplay_curpos *)data = sc->sc_cursor.cc_pos;
432		return (0);
433
434	case WSDISPLAYIO_SCURPOS:
435		set_curpos(sc, (struct wsdisplay_curpos *)data);
436		ims332_set_curpos(sc);
437		return (0);
438
439	case WSDISPLAYIO_GCURMAX:
440		((struct wsdisplay_curpos *)data)->x =
441		((struct wsdisplay_curpos *)data)->y = CURSOR_MAX_SIZE;
442		return (0);
443
444	case WSDISPLAYIO_GCURSOR:
445		return get_cursor(sc, (struct wsdisplay_cursor *)data);
446
447	case WSDISPLAYIO_SCURSOR:
448		return set_cursor(sc, (struct wsdisplay_cursor *)data);
449
450	case WSDISPLAYIO_SMODE:
451		if (*(int *)data == WSDISPLAYIO_MODE_EMUL) {
452			sc->sc_csr |= IMS332_CSR_A_DISABLE_CURSOR;
453			ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
454			xcfb_cmap_init(sc);
455			ims332_loadcmap(&sc->sc_cmap);
456			sc->sc_blanked = 0;
457			xcfb_screenblank(sc);
458		}
459		return (0);
460	}
461	return (EPASSTHROUGH);
462}
463
464static paddr_t
465xcfbmmap(void *v, void *vs, off_t offset, int prot)
466{
467
468	if (offset >= XCFB_FB_SIZE || offset < 0)
469		return (-1);
470	return mips_btop(MIPS_KSEG1_TO_PHYS(XCFB_FB_BASE + offset));
471}
472
473static int
474xcfb_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
475    int *curxp, int *curyp, long *attrp)
476{
477	struct xcfb_softc *sc = v;
478	struct rasops_info *ri = sc->sc_ri;
479	long defattr;
480
481	if (sc->nscreens > 0)
482		return (ENOMEM);
483
484	*cookiep = ri; 		/* one and only for now */
485	*curxp = 0;
486	*curyp = 0;
487	(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
488	*attrp = defattr;
489	sc->nscreens++;
490	return (0);
491}
492
493static void
494xcfb_free_screen(void *v, void *cookie)
495{
496	struct xcfb_softc *sc = v;
497
498	if (sc->sc_ri == &xcfb_console_ri)
499		panic("xcfb_free_screen: console");
500
501	sc->nscreens--;
502}
503
504static int
505xcfb_show_screen(void *v, void *cookie, int waitok,
506    void (*cb)(void *, int, int), void *cbarg)
507{
508
509	return (0);
510}
511
512static int
513xcfbintr(void *v)
514{
515	struct xcfb_softc *sc = v;
516	uint32_t *intr, i;
517
518	intr = (uint32_t *)((char *)sc->sc_ri->ri_hw + IOASIC_INTR);
519	i = *intr;
520	i &= ~XINE_INTR_VINT;
521	*intr = i;
522	return (1);
523}
524
525static void
526xcfb_screenblank(struct xcfb_softc *sc)
527{
528	if (sc->sc_blanked)
529		sc->sc_csr |= IMS332_CSR_A_FORCE_BLANK;
530	else
531		sc->sc_csr &= ~IMS332_CSR_A_FORCE_BLANK;
532	ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
533}
534
535static int
536get_cmap(struct xcfb_softc *sc, struct wsdisplay_cmap *p)
537{
538	u_int index = p->index, count = p->count;
539	int error;
540
541	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
542		return (EINVAL);
543
544	error = copyout(&sc->sc_cmap.r[index], p->red, count);
545	if (error)
546		return error;
547	error = copyout(&sc->sc_cmap.g[index], p->green, count);
548	if (error)
549		return error;
550	error = copyout(&sc->sc_cmap.b[index], p->blue, count);
551	return error;
552}
553
554static int
555set_cmap(struct xcfb_softc *sc, struct wsdisplay_cmap *p)
556{
557	struct hwcmap256 cmap;
558	u_int index = p->index, count = p->count;
559	int error;
560
561	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
562		return (EINVAL);
563
564	error = copyin(p->red, &cmap.r[index], count);
565	if (error)
566		return error;
567	error = copyin(p->green, &cmap.g[index], count);
568	if (error)
569		return error;
570	error = copyin(p->blue, &cmap.b[index], count);
571	if (error)
572		return error;
573	memcpy(&sc->sc_cmap.r[index], &cmap.r[index], count);
574	memcpy(&sc->sc_cmap.g[index], &cmap.g[index], count);
575	memcpy(&sc->sc_cmap.b[index], &cmap.b[index], count);
576	return (0);
577}
578
579static int
580set_cursor(struct xcfb_softc *sc, struct wsdisplay_cursor *p)
581{
582#define	cc (&sc->sc_cursor)
583	u_int v, index = 0, count = 0, icount = 0;
584	uint8_t r[2], g[2], b[2], image[512], mask[512];
585	int error;
586
587	v = p->which;
588	if (v & WSDISPLAY_CURSOR_DOCMAP) {
589		index = p->cmap.index;
590		count = p->cmap.count;
591
592		if (index >= 2 || count > 2 - index)
593			return (EINVAL);
594		error = copyin(p->cmap.red, &r[index], count);
595		if (error)
596			return error;
597		error = copyin(p->cmap.green, &g[index], count);
598		if (error)
599			return error;
600		error = copyin(p->cmap.blue, &b[index], count);
601		if (error)
602			return error;
603	}
604	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
605		if (p->size.x > CURSOR_MAX_SIZE || p->size.y > CURSOR_MAX_SIZE)
606			return (EINVAL);
607		icount = ((p->size.x < 33) ? 4 : 8) * p->size.y;
608		error = copyin(p->image, image, icount);
609		if (error)
610			return error;
611		error = copyin(p->mask, mask, icount);
612		if (error)
613			return error;
614	}
615
616	if (v & WSDISPLAY_CURSOR_DOCMAP) {
617		memcpy(&cc->cc_color[index], &r[index], count);
618		memcpy(&cc->cc_color[index + 2], &g[index], count);
619		memcpy(&cc->cc_color[index + 4], &b[index], count);
620		ims332_load_curcmap(sc);
621	}
622	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
623		cc->cc_size = p->size;
624		memset(cc->cc_image, 0, sizeof cc->cc_image);
625		memcpy(cc->cc_image, image, icount);
626		memset(cc->cc_mask, 0, sizeof cc->cc_mask);
627		memcpy(cc->cc_mask, mask, icount);
628		ims332_load_curshape(sc);
629	}
630	if (v & WSDISPLAY_CURSOR_DOCUR) {
631		cc->cc_hot = p->hot;
632		if (p->enable)
633			sc->sc_csr &= ~IMS332_CSR_A_DISABLE_CURSOR;
634		else
635			sc->sc_csr |= IMS332_CSR_A_DISABLE_CURSOR;
636		ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
637	}
638	if (v & WSDISPLAY_CURSOR_DOPOS) {
639		set_curpos(sc, &p->pos);
640		ims332_set_curpos(sc);
641	}
642
643	return (0);
644#undef cc
645}
646
647static int
648get_cursor(struct xcfb_softc *sc, struct wsdisplay_cursor *p)
649{
650	return (EPASSTHROUGH); /* XXX */
651}
652
653static void
654set_curpos(struct xcfb_softc *sc, struct wsdisplay_curpos *curpos)
655{
656	struct rasops_info *ri = sc->sc_ri;
657	int x = curpos->x, y = curpos->y;
658
659	if (y < 0)
660		y = 0;
661	else if (y > ri->ri_height)
662		y = ri->ri_height;
663	if (x < 0)
664		x = 0;
665	else if (x > ri->ri_width)
666		x = ri->ri_width;
667	sc->sc_cursor.cc_pos.x = x;
668	sc->sc_cursor.cc_pos.y = y;
669}
670
671static void
672ims332_loadcmap(struct hwcmap256 *cm)
673{
674	int i;
675	uint32_t rgb;
676
677	for (i = 0; i < CMAP_SIZE; i++) {
678		rgb = cm->b[i] << 16 | cm->g[i] << 8 | cm->r[i];
679		ims332_write_reg(IMS332_REG_LUT_BASE + i, rgb);
680	}
681}
682
683static void
684ims332_set_curpos(struct xcfb_softc *sc)
685{
686	struct wsdisplay_curpos *curpos = &sc->sc_cursor.cc_pos;
687	uint32_t pos;
688	int s;
689
690	s = spltty();
691	pos = (curpos->x & 0xfff) << 12 | (curpos->y & 0xfff);
692	ims332_write_reg(IMS332_REG_CURSOR_LOC, pos);
693	splx(s);
694}
695
696static void
697ims332_load_curcmap(struct xcfb_softc *sc)
698{
699	uint8_t *cp = sc->sc_cursor.cc_color;
700	uint32_t rgb;
701
702	/* cursor background */
703	rgb = cp[5] << 16 | cp[3] << 8 | cp[1];
704	ims332_write_reg(IMS332_REG_CURSOR_LUT_1, rgb);
705
706	/* cursor foreground */
707	rgb = cp[4] << 16 | cp[2] << 8 | cp[0];
708	ims332_write_reg(IMS332_REG_CURSOR_LUT_2, rgb);
709}
710
711static void
712ims332_load_curshape(struct xcfb_softc *sc)
713{
714	u_int i, img, msk, bits;
715	uint8_t u, *ip, *mp;
716
717	ip = (uint8_t *)sc->sc_cursor.cc_image;
718	mp = (uint8_t *)sc->sc_cursor.cc_mask;
719
720	i = 0;
721	/* 64 pixel scan line is consisted with 8 halfword cursor ram */
722	while (i < sc->sc_cursor.cc_size.y * 8) {
723		/* pad right half 32 pixel when smaller than 33 */
724		if ((i & 0x4) && sc->sc_cursor.cc_size.x < 33)
725			bits = 0;
726		else {
727			img = *ip++;
728			msk = *mp++;
729			img &= msk;	/* cookie off image */
730			u = (msk & 0x0f) << 4 | (img & 0x0f);
731			bits = shuffle[u];
732			u = (msk & 0xf0) | (img & 0xf0) >> 4;
733			bits = (shuffle[u] << 8) | bits;
734		}
735		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, bits);
736		i += 1;
737	}
738	/* pad unoccupied scan lines */
739	while (i < CURSOR_MAX_SIZE * 8) {
740		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, 0);
741		i += 1;
742	}
743}
744
745static void
746ims332_write_reg(int regno, uint32_t val)
747{
748	void *high8 = (void *)(ioasic_base + IMS332_HIGH);
749	void *low16 = (void *)(ioasic_base + IMS332_WLOW + (regno << 4));
750
751	*(volatile uint16_t *)high8 = (val & 0xff0000) >> 8;
752	*(volatile uint16_t *)low16 = val;
753}
754
755#if 0
756static uint32_t
757ims332_read_reg(int regno)
758{
759	void *high8 = (void *)(ioasic_base + IMS332_HIGH);
760	void *low16 = (void *)(ioasic_base + IMS332_RLOW) + (regno << 4);
761	u_int v0, v1;
762
763	v1 = *(volatile uint16_t *)high8;
764	v0 = *(volatile uint16_t *)low16;
765	return (v1 & 0xff00) << 8 | v0;
766}
767#endif
768