xcfb.c revision 1.46
119370Spst/* $NetBSD: xcfb.c,v 1.46 2008/05/26 10:31:22 nisimura Exp $ */
298944Sobrien
398944Sobrien/*-
419370Spst * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc.
598944Sobrien * All rights reserved.
619370Spst *
798944Sobrien * This code is derived from software contributed to The NetBSD Foundation
898944Sobrien * by Tohru Nishimura.
998944Sobrien *
1098944Sobrien * Redistribution and use in source and binary forms, with or without
1119370Spst * modification, are permitted provided that the following conditions
1298944Sobrien * are met:
1398944Sobrien * 1. Redistributions of source code must retain the above copyright
1498944Sobrien *    notice, this list of conditions and the following disclaimer.
1598944Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1619370Spst *    notice, this list of conditions and the following disclaimer in the
1798944Sobrien *    documentation and/or other materials provided with the distribution.
1898944Sobrien *
1998944Sobrien * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2098944Sobrien * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2119370Spst * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2219370Spst * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2319370Spst * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2419370Spst * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25130803Smarcel * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26130803Smarcel * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2798944Sobrien * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2898944Sobrien * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2919370Spst * POSSIBILITY OF SUCH DAMAGE.
3098944Sobrien */
3198944Sobrien
3219370Spst#include <sys/cdefs.h>
3398944Sobrien__KERNEL_RCSID(0, "$NetBSD: xcfb.c,v 1.46 2008/05/26 10:31:22 nisimura Exp $");
3498944Sobrien
3519370Spst#include <sys/param.h>
3698944Sobrien#include <sys/systm.h>
3798944Sobrien#include <sys/kernel.h>
3898944Sobrien#include <sys/device.h>
3998944Sobrien#include <sys/malloc.h>
4098944Sobrien#include <sys/buf.h>
4119370Spst#include <sys/ioctl.h>
4298944Sobrien
4319370Spst#include <sys/bus.h>
4498944Sobrien#include <sys/intr.h>
4519370Spst
4698944Sobrien#include <dev/wscons/wsconsio.h>
4719370Spst#include <dev/wscons/wsdisplayvar.h>
4898944Sobrien
4919370Spst#include <dev/rasops/rasops.h>
5098944Sobrien#include <dev/wsfont/wsfont.h>
5119370Spst
5298944Sobrien#include <dev/tc/tcvar.h>
5319370Spst#include <dev/tc/ioasicreg.h>
5498944Sobrien#include <dev/ic/ims332reg.h>
5519370Spst#include <pmax/pmax/maxine.h>
5698944Sobrien
5798944Sobrien#include <uvm/uvm_extern.h>
5898944Sobrien
5998944Sobrienstruct hwcmap256 {
6098944Sobrien#define	CMAP_SIZE	256	/* 256 R/G/B entries */
6119370Spst	u_int8_t r[CMAP_SIZE];
6298944Sobrien	u_int8_t g[CMAP_SIZE];
6398944Sobrien	u_int8_t b[CMAP_SIZE];
6498944Sobrien};
6598944Sobrien
6698944Sobrienstruct hwcursor64 {
6798944Sobrien	struct wsdisplay_curpos cc_pos;
6898944Sobrien	struct wsdisplay_curpos cc_hot;
6998944Sobrien	struct wsdisplay_curpos cc_size;
7098944Sobrien	struct wsdisplay_curpos cc_magic;	/* not used by PMAG-DV */
7119370Spst#define	CURSOR_MAX_SIZE	64
7298944Sobrien	u_int8_t cc_color[6];
7319370Spst	u_int64_t cc_image[CURSOR_MAX_SIZE];
7498944Sobrien	u_int64_t cc_mask[CURSOR_MAX_SIZE];
7598944Sobrien};
7698944Sobrien
7798944Sobrien#define	XCFB_FB_BASE	(XINE_PHYS_CFB_START + 0x2000000)
7898944Sobrien#define	XCFB_FB_SIZE	0x100000
7998944Sobrien
8098944Sobrien#define	IMS332_HIGH	(IOASIC_SLOT_5_START)
8198944Sobrien#define	IMS332_RLOW	(IOASIC_SLOT_7_START)
8298944Sobrien#define	IMS332_WLOW	(IOASIC_SLOT_7_START + 0x20000)
8346283Sdfr
8446283Sdfrstruct xcfb_softc {
8598944Sobrien	struct device sc_dev;
8619370Spst	vaddr_t sc_vaddr;
8798944Sobrien	size_t sc_size;
8898944Sobrien	struct rasops_info *sc_ri;
8919370Spst	struct hwcmap256 sc_cmap;	/* software copy of colormap */
9098944Sobrien	struct hwcursor64 sc_cursor;	/* software copy of cursor */
9119370Spst	int sc_blanked;
9298944Sobrien	/* XXX MAXINE can take PMAG-DV vertical retrace interrupt XXX */
9398944Sobrien	int nscreens;
9419370Spst	/* cursor coordinate is located at upper-left corner */
9598944Sobrien	int sc_csr;			/* software copy of IMS332 CSR A */
9698944Sobrien};
9719370Spst
9819370Spststatic int  xcfbmatch(struct device *, struct cfdata *, void *);
9998944Sobrienstatic void xcfbattach(struct device *, struct device *, void *);
10019370Spst
10119370SpstCFATTACH_DECL(xcfb, sizeof(struct xcfb_softc),
10219370Spst    xcfbmatch, xcfbattach, NULL, NULL);
10398944Sobrien
10419370Spststatic tc_addr_t xcfb_consaddr;
10519370Spststatic struct rasops_info xcfb_console_ri;
10619370Spststatic void xcfb_common_init(struct rasops_info *);
10719370Spststatic void xcfbhwinit(void *);
10898944Sobrienint xcfb_cnattach(void);
10998944Sobrien
11019370Spststruct wsscreen_descr xcfb_stdscreen = {
11119370Spst	"std", 0, 0,
11298944Sobrien	0, /* textops */
11398944Sobrien	0, 0,
11419370Spst	WSSCREEN_REVERSE
11598944Sobrien};
11619370Spst
11798944Sobrienstatic const struct wsscreen_descr *_xcfb_scrlist[] = {
11898944Sobrien	&xcfb_stdscreen,
11998944Sobrien};
12098944Sobrien
12198944Sobrienstatic const struct wsscreen_list xcfb_screenlist = {
12298944Sobrien	sizeof(_xcfb_scrlist) / sizeof(struct wsscreen_descr *), _xcfb_scrlist
12319370Spst};
12419370Spst
12598944Sobrienstatic int	xcfbioctl(void *, void *, u_long, void *, int, struct lwp *);
12698944Sobrienstatic paddr_t	xcfbmmap(void *, void *, off_t, int);
12798944Sobrien
12819370Spststatic int	xcfb_alloc_screen(void *, const struct wsscreen_descr *,
12998944Sobrien				       void **, int *, int *, long *);
13019370Spststatic void	xcfb_free_screen(void *, void *);
13198944Sobrienstatic int	xcfb_show_screen(void *, void *, int,
13298944Sobrien				      void (*) (void *, int, int), void *);
13319370Spst
13498944Sobrienstatic const struct wsdisplay_accessops xcfb_accessops = {
13519370Spst	xcfbioctl,
13698944Sobrien	xcfbmmap,
13798944Sobrien	xcfb_alloc_screen,
13819370Spst	xcfb_free_screen,
13998944Sobrien	xcfb_show_screen,
14098944Sobrien	0 /* load_font */
14198944Sobrien};
14219370Spst
14398944Sobrienstatic int  xcfbintr(void *);
14419370Spststatic void xcfb_screenblank(struct xcfb_softc *);
14598944Sobrienstatic void xcfb_cmap_init(struct xcfb_softc *);
14619370Spststatic int  set_cmap(struct xcfb_softc *, struct wsdisplay_cmap *);
14798944Sobrienstatic int  get_cmap(struct xcfb_softc *, struct wsdisplay_cmap *);
14819370Spststatic int  set_cursor(struct xcfb_softc *, struct wsdisplay_cursor *);
14998944Sobrienstatic int  get_cursor(struct xcfb_softc *, struct wsdisplay_cursor *);
15019370Spststatic void set_curpos(struct xcfb_softc *, struct wsdisplay_curpos *);
15198944Sobrienstatic void ims332_loadcmap(struct hwcmap256 *);
15219370Spststatic void ims332_set_curpos(struct xcfb_softc *);
15398944Sobrienstatic void ims332_load_curcmap(struct xcfb_softc *);
15419370Spststatic void ims332_load_curshape(struct xcfb_softc *);
15598944Sobrienstatic void ims332_write_reg(int, u_int32_t);
15698944Sobrien#if 0
15798944Sobrienstatic u_int32_t ims332_read_reg(int);
15819370Spst#endif
15998944Sobrien
16098944Sobrienextern long ioasic_base;	/* XXX */
16119370Spst
16298944Sobrien/*
16398944Sobrien * Compose 2 bit/pixel cursor image.
16498944Sobrien *   M M M M I I I I		M I M I M I M I
16598944Sobrien *	[ before ]		   [ after ]
16619370Spst *   3 2 1 0 3 2 1 0		3 3 2 2 1 1 0 0
16798944Sobrien *   7 6 5 4 7 6 5 4		7 7 6 6 5 5 4 4
16819370Spst */
16998944Sobrienstatic const u_int8_t shuffle[256] = {
17019370Spst	0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15,
17198944Sobrien	0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55,
17298944Sobrien	0x02, 0x03, 0x06, 0x07, 0x12, 0x13, 0x16, 0x17,
17319370Spst	0x42, 0x43, 0x46, 0x47, 0x52, 0x53, 0x56, 0x57,
17498944Sobrien	0x08, 0x09, 0x0c, 0x0d, 0x18, 0x19, 0x1c, 0x1d,
17519370Spst	0x48, 0x49, 0x4c, 0x4d, 0x58, 0x59, 0x5c, 0x5d,
17698944Sobrien	0x0a, 0x0b, 0x0e, 0x0f, 0x1a, 0x1b, 0x1e, 0x1f,
17798944Sobrien	0x4a, 0x4b, 0x4e, 0x4f, 0x5a, 0x5b, 0x5e, 0x5f,
17898944Sobrien	0x20, 0x21, 0x24, 0x25, 0x30, 0x31, 0x34, 0x35,
17998944Sobrien	0x60, 0x61, 0x64, 0x65, 0x70, 0x71, 0x74, 0x75,
18098944Sobrien	0x22, 0x23, 0x26, 0x27, 0x32, 0x33, 0x36, 0x37,
18198944Sobrien	0x62, 0x63, 0x66, 0x67, 0x72, 0x73, 0x76, 0x77,
18298944Sobrien	0x28, 0x29, 0x2c, 0x2d, 0x38, 0x39, 0x3c, 0x3d,
18398944Sobrien	0x68, 0x69, 0x6c, 0x6d, 0x78, 0x79, 0x7c, 0x7d,
18498944Sobrien	0x2a, 0x2b, 0x2e, 0x2f, 0x3a, 0x3b, 0x3e, 0x3f,
18598944Sobrien	0x6a, 0x6b, 0x6e, 0x6f, 0x7a, 0x7b, 0x7e, 0x7f,
18698944Sobrien	0x80, 0x81, 0x84, 0x85, 0x90, 0x91, 0x94, 0x95,
18798944Sobrien	0xc0, 0xc1, 0xc4, 0xc5, 0xd0, 0xd1, 0xd4, 0xd5,
18898944Sobrien	0x82, 0x83, 0x86, 0x87, 0x92, 0x93, 0x96, 0x97,
18998944Sobrien	0xc2, 0xc3, 0xc6, 0xc7, 0xd2, 0xd3, 0xd6, 0xd7,
19098944Sobrien	0x88, 0x89, 0x8c, 0x8d, 0x98, 0x99, 0x9c, 0x9d,
19198944Sobrien	0xc8, 0xc9, 0xcc, 0xcd, 0xd8, 0xd9, 0xdc, 0xdd,
19298944Sobrien	0x8a, 0x8b, 0x8e, 0x8f, 0x9a, 0x9b, 0x9e, 0x9f,
19398944Sobrien	0xca, 0xcb, 0xce, 0xcf, 0xda, 0xdb, 0xde, 0xdf,
19498944Sobrien	0xa0, 0xa1, 0xa4, 0xa5, 0xb0, 0xb1, 0xb4, 0xb5,
19598944Sobrien	0xe0, 0xe1, 0xe4, 0xe5, 0xf0, 0xf1, 0xf4, 0xf5,
19698944Sobrien	0xa2, 0xa3, 0xa6, 0xa7, 0xb2, 0xb3, 0xb6, 0xb7,
19798944Sobrien	0xe2, 0xe3, 0xe6, 0xe7, 0xf2, 0xf3, 0xf6, 0xf7,
19898944Sobrien	0xa8, 0xa9, 0xac, 0xad, 0xb8, 0xb9, 0xbc, 0xbd,
19998944Sobrien	0xe8, 0xe9, 0xec, 0xed, 0xf8, 0xf9, 0xfc, 0xfd,
20098944Sobrien	0xaa, 0xab, 0xae, 0xaf, 0xba, 0xbb, 0xbe, 0xbf,
20198944Sobrien	0xea, 0xeb, 0xee, 0xef, 0xfa, 0xfb, 0xfe, 0xff,
20298944Sobrien};
20398944Sobrien
20498944Sobrienstatic int
20598944Sobrienxcfbmatch(struct device *parent, struct cfdata *match, void *aux)
20698944Sobrien{
20798944Sobrien	struct tc_attach_args *ta = aux;
20898944Sobrien
20998944Sobrien	if (strncmp("PMAG-DV ", ta->ta_modname, TC_ROM_LLEN) != 0)
21098944Sobrien		return (0);
21198944Sobrien
21298944Sobrien	return (1);
21398944Sobrien}
21498944Sobrien
21598944Sobrienstatic void
21698944Sobrienxcfbattach(struct device *parent, struct device *self, void *aux)
21798944Sobrien{
21898944Sobrien	struct xcfb_softc *sc = device_private(self);
21998944Sobrien	struct tc_attach_args *ta = aux;
22098944Sobrien	struct rasops_info *ri;
22198944Sobrien	struct wsemuldisplaydev_attach_args waa;
22298944Sobrien	int console;
22398944Sobrien
22498944Sobrien	console = (ta->ta_addr == xcfb_consaddr);
22598944Sobrien	if (console) {
22698944Sobrien		sc->sc_ri = ri = &xcfb_console_ri;
22798944Sobrien		sc->nscreens = 1;
22898944Sobrien	}
22998944Sobrien	else {
23098944Sobrien		MALLOC(ri, struct rasops_info *, sizeof(struct rasops_info),
23198944Sobrien			M_DEVBUF, M_NOWAIT);
23298944Sobrien		if (ri == NULL) {
23398944Sobrien			printf(": can't alloc memory\n");
23498944Sobrien			return;
23598944Sobrien		}
23698944Sobrien		memset(ri, 0, sizeof(struct rasops_info));
23798944Sobrien
23898944Sobrien		ri->ri_hw = (void *)ioasic_base;
23919370Spst		xcfb_common_init(ri);
24019370Spst		sc->sc_ri = ri;
24198944Sobrien	}
24219370Spst	printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth);
24319370Spst
244	xcfb_cmap_init(sc);
245
246	sc->sc_vaddr = ta->ta_addr;
247	sc->sc_blanked = 0;
248	sc->sc_csr = IMS332_BPP_8 | IMS332_CSR_A_VTG_ENABLE;
249
250        tc_intr_establish(parent, ta->ta_cookie, IPL_TTY, xcfbintr, sc);
251
252	waa.console = console;
253	waa.scrdata = &xcfb_screenlist;
254	waa.accessops = &xcfb_accessops;
255	waa.accesscookie = sc;
256
257	config_found(self, &waa, wsemuldisplaydevprint);
258}
259
260static void
261xcfb_cmap_init(struct xcfb_softc *sc)
262{
263	struct hwcmap256 *cm;
264	const u_int8_t *p;
265	int index;
266
267	cm = &sc->sc_cmap;
268	p = rasops_cmap;
269	for (index = 0; index < CMAP_SIZE; index++, p += 3) {
270		cm->r[index] = p[0];
271		cm->g[index] = p[1];
272		cm->b[index] = p[2];
273	}
274}
275
276static void
277xcfb_common_init(struct rasops_info *ri)
278{
279	int cookie;
280
281	/* initialize colormap and cursor hardware */
282	xcfbhwinit((void *)ri->ri_hw);
283
284	ri->ri_flg = RI_CENTER;
285	ri->ri_depth = 8;
286	ri->ri_width = 1024;
287	ri->ri_height = 768;
288	ri->ri_stride = 1024;
289	ri->ri_bits = (void *)MIPS_PHYS_TO_KSEG1(XCFB_FB_BASE);
290
291	/* clear the screen */
292	memset(ri->ri_bits, 0, ri->ri_stride * ri->ri_height);
293
294	wsfont_init();
295	/* prefer 12 pixel wide font */
296	cookie = wsfont_find(NULL, 12, 0, 0, WSDISPLAY_FONTORDER_L2R,
297	    WSDISPLAY_FONTORDER_L2R);
298	if (cookie <= 0)
299		cookie = wsfont_find(NULL, 0, 0, 0, WSDISPLAY_FONTORDER_L2R,
300		    WSDISPLAY_FONTORDER_L2R);
301	if (cookie <= 0) {
302		printf("xcfb: font table is empty\n");
303		return;
304	}
305
306	if (wsfont_lock(cookie, &ri->ri_font)) {
307		printf("xcfb: couldn't lock font\n");
308		return;
309	}
310	ri->ri_wsfcookie = cookie;
311
312	rasops_init(ri, 34, 80);
313
314	/* XXX shouldn't be global */
315	xcfb_stdscreen.nrows = ri->ri_rows;
316	xcfb_stdscreen.ncols = ri->ri_cols;
317	xcfb_stdscreen.textops = &ri->ri_ops;
318	xcfb_stdscreen.capabilities = ri->ri_caps;
319}
320
321int
322xcfb_cnattach(void)
323{
324	struct rasops_info *ri;
325	long defattr;
326
327	ri = &xcfb_console_ri;
328	ri->ri_hw = (void *)ioasic_base;
329	xcfb_common_init(ri);
330	(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
331	wsdisplay_cnattach(&xcfb_stdscreen, ri, 0, 0, defattr);
332	xcfb_consaddr = MIPS_PHYS_TO_KSEG1(XINE_PHYS_CFB_START);
333	return (0);
334}
335
336static void
337xcfbhwinit(void *base)
338{
339	volatile u_int32_t *csr;
340	u_int32_t i;
341	const u_int8_t *p;
342
343	csr = (volatile u_int32_t *)((char *)base + IOASIC_CSR);
344	i = *csr;
345	i &= ~XINE_CSR_VDAC_ENABLE;
346	*csr = i;
347	DELAY(50);
348	i |= XINE_CSR_VDAC_ENABLE;
349	*csr = i;
350	DELAY(50);
351	ims332_write_reg(IMS332_REG_BOOT, 0x2c);
352	ims332_write_reg(IMS332_REG_CSR_A,
353		IMS332_BPP_8|IMS332_CSR_A_DISABLE_CURSOR);
354	ims332_write_reg(IMS332_REG_HALF_SYNCH, 0x10);
355	ims332_write_reg(IMS332_REG_BACK_PORCH, 0x21);
356	ims332_write_reg(IMS332_REG_DISPLAY, 0x100);
357	ims332_write_reg(IMS332_REG_SHORT_DIS, 0x5d);
358	ims332_write_reg(IMS332_REG_BROAD_PULSE, 0x9f);
359	ims332_write_reg(IMS332_REG_LINE_TIME, 0x146);
360	ims332_write_reg(IMS332_REG_V_SYNC, 0x0c);
361	ims332_write_reg(IMS332_REG_V_PRE_EQUALIZE, 0x02);
362	ims332_write_reg(IMS332_REG_V_POST_EQUALIZE, 0x02);
363	ims332_write_reg(IMS332_REG_V_BLANK, 0x2a);
364	ims332_write_reg(IMS332_REG_V_DISPLAY, 0x600);
365	ims332_write_reg(IMS332_REG_LINE_START, 0x10);
366	ims332_write_reg(IMS332_REG_MEM_INIT, 0x0a);
367	ims332_write_reg(IMS332_REG_COLOR_MASK, 0xffffff);
368	ims332_write_reg(IMS332_REG_CSR_A,
369		IMS332_BPP_8|IMS332_CSR_A_VTG_ENABLE);
370
371	/* build sane colormap */
372	p = rasops_cmap;
373	for (i = 0; i < CMAP_SIZE; i++, p += 3) {
374		u_int32_t bgr;
375
376		bgr = p[2] << 16 | p[1] << 8 | p[0];
377		ims332_write_reg(IMS332_REG_LUT_BASE + i, bgr);
378	}
379
380	/* clear out cursor image */
381	for (i = 0; i < 512; i++)
382		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, 0);
383
384	/*
385	 * 2 bit/pixel cursor.  Assign MSB for cursor mask and LSB for
386	 * cursor image.  LUT_1 for mask color, while LUT_2 for
387	 * image color.  LUT_0 will be never used.
388	 */
389	ims332_write_reg(IMS332_REG_CURSOR_LUT_0, 0);
390	ims332_write_reg(IMS332_REG_CURSOR_LUT_1, 0xffffff);
391	ims332_write_reg(IMS332_REG_CURSOR_LUT_2, 0xffffff);
392}
393
394static int
395xcfbioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
396{
397	struct xcfb_softc *sc = v;
398	struct rasops_info *ri = sc->sc_ri;
399	int turnoff, error;
400
401	switch (cmd) {
402	case WSDISPLAYIO_GTYPE:
403		*(u_int *)data = WSDISPLAY_TYPE_XCFB;
404		return (0);
405
406	case WSDISPLAYIO_GINFO:
407#define	wsd_fbip ((struct wsdisplay_fbinfo *)data)
408		wsd_fbip->height = ri->ri_height;
409		wsd_fbip->width = ri->ri_width;
410		wsd_fbip->depth = ri->ri_depth;
411		wsd_fbip->cmsize = CMAP_SIZE;
412#undef fbt
413		return (0);
414
415	case WSDISPLAYIO_GETCMAP:
416		return get_cmap(sc, (struct wsdisplay_cmap *)data);
417
418	case WSDISPLAYIO_PUTCMAP:
419		error = set_cmap(sc, (struct wsdisplay_cmap *)data);
420		if (error == 0)
421			ims332_loadcmap(&sc->sc_cmap);
422		return (error);
423
424	case WSDISPLAYIO_SVIDEO:
425		turnoff = *(int *)data == WSDISPLAYIO_VIDEO_OFF;
426		if (sc->sc_blanked != turnoff) {
427			sc->sc_blanked = turnoff;
428			xcfb_screenblank(sc);
429		}
430		return (0);
431
432	case WSDISPLAYIO_GVIDEO:
433		*(u_int *)data = sc->sc_blanked ?
434		    WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON;
435		return (0);
436
437	case WSDISPLAYIO_GCURPOS:
438		*(struct wsdisplay_curpos *)data = sc->sc_cursor.cc_pos;
439		return (0);
440
441	case WSDISPLAYIO_SCURPOS:
442		set_curpos(sc, (struct wsdisplay_curpos *)data);
443		ims332_set_curpos(sc);
444		return (0);
445
446	case WSDISPLAYIO_GCURMAX:
447		((struct wsdisplay_curpos *)data)->x =
448		((struct wsdisplay_curpos *)data)->y = CURSOR_MAX_SIZE;
449		return (0);
450
451	case WSDISPLAYIO_GCURSOR:
452		return get_cursor(sc, (struct wsdisplay_cursor *)data);
453
454	case WSDISPLAYIO_SCURSOR:
455		return set_cursor(sc, (struct wsdisplay_cursor *)data);
456
457	case WSDISPLAYIO_SMODE:
458		if (*(int *)data == WSDISPLAYIO_MODE_EMUL) {
459			sc->sc_csr |= IMS332_CSR_A_DISABLE_CURSOR;
460			ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
461			xcfb_cmap_init(sc);
462			ims332_loadcmap(&sc->sc_cmap);
463			sc->sc_blanked = 0;
464			xcfb_screenblank(sc);
465		}
466		return (0);
467	}
468	return (EPASSTHROUGH);
469}
470
471static paddr_t
472xcfbmmap(void *v, void *vs, off_t offset, int prot)
473{
474
475	if (offset >= XCFB_FB_SIZE || offset < 0)
476		return (-1);
477	return mips_btop(MIPS_KSEG1_TO_PHYS(XCFB_FB_BASE + offset));
478}
479
480static int
481xcfb_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
482    int *curxp, int *curyp, long *attrp)
483{
484	struct xcfb_softc *sc = v;
485	struct rasops_info *ri = sc->sc_ri;
486	long defattr;
487
488	if (sc->nscreens > 0)
489		return (ENOMEM);
490
491	*cookiep = ri; 		/* one and only for now */
492	*curxp = 0;
493	*curyp = 0;
494	(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
495	*attrp = defattr;
496	sc->nscreens++;
497	return (0);
498}
499
500static void
501xcfb_free_screen(void *v, void *cookie)
502{
503	struct xcfb_softc *sc = v;
504
505	if (sc->sc_ri == &xcfb_console_ri)
506		panic("xcfb_free_screen: console");
507
508	sc->nscreens--;
509}
510
511static int
512xcfb_show_screen(void *v, void *cookie, int waitok,
513    void (*cb)(void *, int, int), void *cbarg)
514{
515
516	return (0);
517}
518
519static int
520xcfbintr(void *v)
521{
522	struct xcfb_softc *sc = v;
523	u_int32_t *intr, i;
524
525	intr = (u_int32_t *)((char *)sc->sc_ri->ri_hw + IOASIC_INTR);
526	i = *intr;
527	i &= ~XINE_INTR_VINT;
528	*intr = i;
529	return (1);
530}
531
532static void
533xcfb_screenblank(struct xcfb_softc *sc)
534{
535	if (sc->sc_blanked)
536		sc->sc_csr |= IMS332_CSR_A_FORCE_BLANK;
537	else
538		sc->sc_csr &= ~IMS332_CSR_A_FORCE_BLANK;
539	ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
540}
541
542static int
543get_cmap(struct xcfb_softc *sc, struct wsdisplay_cmap *p)
544{
545	u_int index = p->index, count = p->count;
546	int error;
547
548	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
549		return (EINVAL);
550
551	error = copyout(&sc->sc_cmap.r[index], p->red, count);
552	if (error)
553		return error;
554	error = copyout(&sc->sc_cmap.g[index], p->green, count);
555	if (error)
556		return error;
557	error = copyout(&sc->sc_cmap.b[index], p->blue, count);
558	return error;
559}
560
561static int
562set_cmap(struct xcfb_softc *sc, struct wsdisplay_cmap *p)
563{
564	struct hwcmap256 cmap;
565	u_int index = p->index, count = p->count;
566	int error;
567
568	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
569		return (EINVAL);
570
571	error = copyin(p->red, &cmap.r[index], count);
572	if (error)
573		return error;
574	error = copyin(p->green, &cmap.g[index], count);
575	if (error)
576		return error;
577	error = copyin(p->blue, &cmap.b[index], count);
578	if (error)
579		return error;
580	memcpy(&sc->sc_cmap.r[index], &cmap.r[index], count);
581	memcpy(&sc->sc_cmap.g[index], &cmap.g[index], count);
582	memcpy(&sc->sc_cmap.b[index], &cmap.b[index], count);
583	return (0);
584}
585
586static int
587set_cursor(struct xcfb_softc *sc, struct wsdisplay_cursor *p)
588{
589#define	cc (&sc->sc_cursor)
590	u_int v, index = 0, count = 0, icount = 0;
591	uint8_t r[2], g[2], b[2], image[512], mask[512];
592	int error;
593
594	v = p->which;
595	if (v & WSDISPLAY_CURSOR_DOCMAP) {
596		index = p->cmap.index;
597		count = p->cmap.count;
598
599		if (index >= 2 || index + count > 2)
600			return (EINVAL);
601		error = copyin(p->cmap.red, &r[index], count);
602		if (error)
603			return error;
604		error = copyin(p->cmap.green, &g[index], count);
605		if (error)
606			return error;
607		error = copyin(p->cmap.blue, &b[index], count);
608		if (error)
609			return error;
610	}
611	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
612		if (p->size.x > CURSOR_MAX_SIZE || p->size.y > CURSOR_MAX_SIZE)
613			return (EINVAL);
614		icount = ((p->size.x < 33) ? 4 : 8) * p->size.y;
615		error = copyin(p->image, image, icount);
616		if (error)
617			return error;
618		error = copyin(p->mask, mask, icount);
619		if (error)
620			return error;
621	}
622
623	if (v & WSDISPLAY_CURSOR_DOCMAP) {
624		memcpy(&cc->cc_color[index], &r[index], count);
625		memcpy(&cc->cc_color[index + 2], &g[index], count);
626		memcpy(&cc->cc_color[index + 4], &b[index], count);
627		ims332_load_curcmap(sc);
628	}
629	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
630		cc->cc_size = p->size;
631		memset(cc->cc_image, 0, sizeof cc->cc_image);
632		memcpy(cc->cc_image, image, icount);
633		memset(cc->cc_mask, 0, sizeof cc->cc_mask);
634		memcpy(cc->cc_mask, mask, icount);
635		ims332_load_curshape(sc);
636	}
637	if (v & WSDISPLAY_CURSOR_DOCUR) {
638		cc->cc_hot = p->hot;
639		if (p->enable)
640			sc->sc_csr &= ~IMS332_CSR_A_DISABLE_CURSOR;
641		else
642			sc->sc_csr |= IMS332_CSR_A_DISABLE_CURSOR;
643		ims332_write_reg(IMS332_REG_CSR_A, sc->sc_csr);
644	}
645	if (v & WSDISPLAY_CURSOR_DOPOS) {
646		set_curpos(sc, &p->pos);
647		ims332_set_curpos(sc);
648	}
649
650	return (0);
651#undef cc
652}
653
654static int
655get_cursor(struct xcfb_softc *sc, struct wsdisplay_cursor *p)
656{
657	return (EPASSTHROUGH); /* XXX */
658}
659
660static void
661set_curpos(struct xcfb_softc *sc, struct wsdisplay_curpos *curpos)
662{
663	struct rasops_info *ri = sc->sc_ri;
664	int x = curpos->x, y = curpos->y;
665
666	if (y < 0)
667		y = 0;
668	else if (y > ri->ri_height)
669		y = ri->ri_height;
670	if (x < 0)
671		x = 0;
672	else if (x > ri->ri_width)
673		x = ri->ri_width;
674	sc->sc_cursor.cc_pos.x = x;
675	sc->sc_cursor.cc_pos.y = y;
676}
677
678static void
679ims332_loadcmap(struct hwcmap256 *cm)
680{
681	int i;
682	u_int32_t rgb;
683
684	for (i = 0; i < CMAP_SIZE; i++) {
685		rgb = cm->b[i] << 16 | cm->g[i] << 8 | cm->r[i];
686		ims332_write_reg(IMS332_REG_LUT_BASE + i, rgb);
687	}
688}
689
690static void
691ims332_set_curpos(struct xcfb_softc *sc)
692{
693	struct wsdisplay_curpos *curpos = &sc->sc_cursor.cc_pos;
694	u_int32_t pos;
695	int s;
696
697	s = spltty();
698	pos = (curpos->x & 0xfff) << 12 | (curpos->y & 0xfff);
699	ims332_write_reg(IMS332_REG_CURSOR_LOC, pos);
700	splx(s);
701}
702
703static void
704ims332_load_curcmap(struct xcfb_softc *sc)
705{
706	u_int8_t *cp = sc->sc_cursor.cc_color;
707	u_int32_t rgb;
708
709	/* cursor background */
710	rgb = cp[5] << 16 | cp[3] << 8 | cp[1];
711	ims332_write_reg(IMS332_REG_CURSOR_LUT_1, rgb);
712
713	/* cursor foreground */
714	rgb = cp[4] << 16 | cp[2] << 8 | cp[0];
715	ims332_write_reg(IMS332_REG_CURSOR_LUT_2, rgb);
716}
717
718static void
719ims332_load_curshape(struct xcfb_softc *sc)
720{
721	u_int i, img, msk, bits;
722	u_int8_t u, *ip, *mp;
723
724	ip = (u_int8_t *)sc->sc_cursor.cc_image;
725	mp = (u_int8_t *)sc->sc_cursor.cc_mask;
726
727	i = 0;
728	/* 64 pixel scan line is consisted with 8 halfword cursor ram */
729	while (i < sc->sc_cursor.cc_size.y * 8) {
730		/* pad right half 32 pixel when smaller than 33 */
731		if ((i & 0x4) && sc->sc_cursor.cc_size.x < 33)
732			bits = 0;
733		else {
734			img = *ip++;
735			msk = *mp++;
736			img &= msk;	/* cookie off image */
737			u = (msk & 0x0f) << 4 | (img & 0x0f);
738			bits = shuffle[u];
739			u = (msk & 0xf0) | (img & 0xf0) >> 4;
740			bits = (shuffle[u] << 8) | bits;
741		}
742		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, bits);
743		i += 1;
744	}
745	/* pad unoccupied scan lines */
746	while (i < CURSOR_MAX_SIZE * 8) {
747		ims332_write_reg(IMS332_REG_CURSOR_RAM + i, 0);
748		i += 1;
749	}
750}
751
752static void
753ims332_write_reg(int regno, u_int32_t val)
754{
755	void *high8 = (void *)(ioasic_base + IMS332_HIGH);
756	void *low16 = (void *)(ioasic_base + IMS332_WLOW + (regno << 4));
757
758	*(volatile u_int16_t *)high8 = (val & 0xff0000) >> 8;
759	*(volatile u_int16_t *)low16 = val;
760}
761
762#if 0
763static u_int32_t
764ims332_read_reg(int regno)
765{
766	void *high8 = (void *)(ioasic_base + IMS332_HIGH);
767	void *low16 = (void *)(ioasic_base + IMS332_RLOW) + (regno << 4);
768	u_int v0, v1;
769
770	v1 = *(volatile u_int16_t *)high8;
771	v0 = *(volatile u_int16_t *)low16;
772	return (v1 & 0xff00) << 8 | v0;
773}
774#endif
775