1/*	$NetBSD: gpx.c,v 1.3 2024/02/03 16:21:25 tsutsui Exp $ */
2/*	$OpenBSD: gpx.c,v 1.25 2014/12/23 21:39:12 miod Exp $	*/
3/*
4 * Copyright (c) 2006 Miodrag Vallat.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice, this permission notice, and the disclaimer below
9 * appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19/*-
20 * Copyright (c) 1988 Regents of the University of California.
21 * All rights reserved.
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
25 * are met:
26 * 1. Redistributions of source code must retain the above copyright
27 *    notice, this list of conditions and the following disclaimer.
28 * 2. Redistributions in binary form must reproduce the above copyright
29 *    notice, this list of conditions and the following disclaimer in the
30 *    documentation and/or other materials provided with the distribution.
31 * 3. Neither the name of the University nor the names of its contributors
32 *    may be used to endorse or promote products derived from this software
33 *    without specific prior written permission.
34 *
35 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
36 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
39 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
41 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45 * SUCH DAMAGE.
46 *
47 *	@(#)qd.c	7.1 (Berkeley) 6/28/91
48 */
49
50/************************************************************************
51*									*
52*			Copyright (c) 1985-1988 by			*
53*		Digital Equipment Corporation, Maynard, MA		*
54*			All rights reserved.				*
55*									*
56*   This software is furnished under a license and may be used and	*
57*   copied  only  in accordance with the terms of such license and	*
58*   with the  inclusion  of  the  above  copyright  notice.   This	*
59*   software  or  any  other copies thereof may not be provided or	*
60*   otherwise made available to any other person.  No title to and	*
61*   ownership of the software is hereby transferred.			*
62*									*
63*   The information in this software is subject to change  without	*
64*   notice  and should not be construed as a commitment by Digital	*
65*   Equipment Corporation.						*
66*									*
67*   Digital assumes no responsibility for the use  or  reliability	*
68*   of its software on equipment which is not supplied by Digital.	*
69*									*
70*************************************************************************/
71
72/*
73 * Driver for the GPX color option on VAXstation 3100, based on the
74 * MicroVAX II qdss driver.
75 *
76 * The frame buffer memory itself is not directly accessible (unlike
77 * the on-board monochrome smg frame buffer), and writes through the
78 * Dragon chip can only happen in multiples of 16 pixels, horizontally.
79 *
80 * Because of this limitation, the font image is copied to offscreen
81 * memory (which there is plenty of), and screen to screen blt operations
82 * are done for everything.
83 */
84
85#include "dzkbd.h"
86#include "wsdisplay.h"
87
88#include <sys/param.h>
89#include <sys/device.h>
90#include <sys/systm.h>
91#include <sys/kmem.h>
92#include <sys/conf.h>
93
94#include <machine/sid.h>
95#include <machine/cpu.h>
96#include <machine/ka420.h>
97#include <machine/scb.h>
98#include <machine/vsbus.h>
99#include <machine/qdreg.h>
100
101#include <dev/cons.h>
102
103#include <dev/dec/dzreg.h>
104#include <dev/dec/dzvar.h>
105#include <dev/dec/dzkbdvar.h>
106
107#include <dev/wscons/wsconsio.h>
108#include <dev/wscons/wscons_callbacks.h>
109#include <dev/wscons/wsdisplayvar.h>
110#include <dev/rasops/rasops.h>
111#include <dev/wsfont/wsfont.h>
112
113#if 0
114#include <dev/ic/bt458reg.h>
115#include <dev/ic/dc503reg.h>
116#endif
117
118#define	GPXADDR		0x3c000000	/* base address on VAXstation 3100 */
119
120#define	GPX_ADDER_OFFSET	0x0000
121#define	GPX_VDAC_OFFSET		0x0300
122#define	GPX_CURSOR_OFFSET	0x0400	/* DC503 */
123#define	GPX_READBACK_OFFSET	0x0500
124
125#define	GPX_WIDTH	1024
126#define	GPX_VISHEIGHT	864
127#define	GPX_HEIGHT	2048
128
129/* XXX these should be in <dev/ic/bt458reg.h> */
130/*
131 * Brooktree Bt451, Bt457, Bt458 register definitions
132 */
133#define BT_OV0	0x00		/* overlay 0 */
134#define BT_OV1	0x01		/* overlay 1 */
135#define BT_OV2	0x02		/* overlay 2 */
136#define BT_OV3	0x03		/* overlay 3 */
137#define BT_RMR	0x04		/* read mask */
138#define BT_BMR	0x05		/* blink mask */
139#define BT_CR	0x06		/* control */
140#define BT_CTR	0x07		/* control/test */
141
142#define BTCR_MPLX_5		0x80	/* multiplex select, 5:1 */
143#define BTCR_MPLX_4		0x00	/* multiplex select, 4:1 */
144#define BTCR_RAMENA		0x40	/* use color palette RAM */
145#define BTCR_BLINK_M		0x30	/* blink mask */
146#define BTCR_BLINK_1648		0x00	/*  16 on, 48 off */
147#define BTCR_BLINK_1616		0x10	/*  16 on, 16 off */
148#define BTCR_BLINK_3232		0x20	/*  32 on, 32 off */
149#define BTCR_BLINK_6464		0x30	/*  64 on, 64 off */
150#define BTCR_BLINKENA_OV1	0x08	/* OV1 blink enable */
151#define BTCR_BLINKENA_OV0	0x04	/* OV0 blink enable */
152#define BTCR_DISPENA_OV1	0x02	/* OV1 display enable */
153#define BTCR_DISPENA_OV0	0x01	/* OV0 display enable */
154
155#define BTCTR_R_ENA		0x01	/* red channel enable */
156#define BTCTR_G_ENA		0x02	/* green channel enable */
157#define BTCTR_B_ENA		0x04	/* blue channel enable */
158#define BTCTR_NIB_M		0x08	/* nibble mask: */
159#define BTCTR_NIB_LOW		0x08	/*  low */
160#define BTCTR_NIB_HIGH		0x00	/*  high */
161
162/* 4 plane option RAMDAC */
163struct	ramdac4 {
164	uint16_t	colormap[16];
165	uint8_t		unknown[0x20];
166	uint16_t	cursormap[4];
167	uint8_t		unknown2[0x18];
168	uint16_t	control;
169#define	RAMDAC4_INIT	0x0047
170#define	RAMDAC4_ENABLE	0x0002
171};
172
173/* 8 plane option RAMDAC - Bt458 or compatible */
174struct	ramdac8 {
175	uint16_t	address;
176	uint16_t	cmapdata;
177	uint16_t	control;
178	uint16_t	omapdata;
179};
180
181struct	gpx_screen {
182	struct rasops_info ss_ri;
183	int		ss_console;
184	u_int		ss_depth;
185	u_int		ss_gpr;		/* font glyphs per row */
186	struct adder	*ss_adder;
187	void		*ss_vdac;
188	uint8_t		ss_cmap[256 * 3];
189#if 0
190	struct dc503reg	*ss_cursor;
191	uint16_t	ss_curcmd;
192#endif
193};
194
195struct	gpx_softc {
196	device_t sc_dev;
197	struct gpx_screen *sc_scr;
198	int	sc_nscreens;
199};
200
201static int gpx_match(device_t, cfdata_t, void *);
202static void gpx_attach(device_t, device_t, void *);
203
204static int gpx_ioctl(void *, void *, u_long, void *, int, struct lwp *);
205static paddr_t gpx_mmap(void *, void *, off_t, int);
206static int gpx_alloc_screen(void *, const struct wsscreen_descr *,
207    void **, int *, int *, long *);
208static void gpx_free_screen(void *, void *);
209static int gpx_show_screen(void *, void *, int,
210    void (*) (void *, int, int), void *);
211
212static void gpx_putchar(void *, int, int, u_int, long);
213static void gpx_copycols(void *, int, int, int, int);
214static void gpx_erasecols(void *, int, int, int, long);
215static void gpx_copyrows(void *, int, int, int);
216static void gpx_eraserows(void *, int, int, long);
217static void gpx_do_cursor(struct rasops_info *);
218
219static int gpx_wait(struct gpx_screen *, int);
220static int gpx_viper_write(struct gpx_screen *, u_int, uint16_t);
221static void gpx_reset_viper(struct gpx_screen *);
222static void gpx_clear_screen(struct gpx_screen *);
223static int gpx_setup_screen(struct gpx_screen *);
224static void gpx_upload_font(struct gpx_screen *);
225static void gpx_copyrect(struct gpx_screen *, int, int, int, int, int, int);
226static void gpx_fillrect(struct gpx_screen *, int, int, int, int, long, u_int);
227static int gpx_getcmap(struct gpx_screen *, struct wsdisplay_cmap *);
228static int gpx_putcmap(struct gpx_screen *, struct wsdisplay_cmap *);
229static void gpx_loadcmap(struct gpx_screen *, int, int);
230static void gpx_resetcmap(struct gpx_screen *);
231
232/* for console */
233static struct gpx_screen gpx_consscr;
234
235CFATTACH_DECL_NEW(gpx, sizeof(struct gpx_softc),
236    gpx_match, gpx_attach, NULL, NULL);
237
238static struct wsscreen_descr gpx_stdscreen = {
239	"std",
240};
241
242static const struct wsscreen_descr *_gpx_scrlist[] = {
243	&gpx_stdscreen,
244};
245
246static const struct wsscreen_list gpx_screenlist = {
247	sizeof(_gpx_scrlist) / sizeof(struct wsscreen_descr *),
248	_gpx_scrlist,
249};
250
251static const struct wsdisplay_accessops gpx_accessops = {
252	.ioctl = gpx_ioctl,
253	.mmap = gpx_mmap,
254	.alloc_screen = gpx_alloc_screen,
255	.free_screen = gpx_free_screen,
256	.show_screen = gpx_show_screen,
257	.load_font = NULL
258};
259
260/*
261 * Autoconf glue
262 */
263
264static int
265gpx_match(device_t parent, cfdata_t match, void *aux)
266{
267	struct vsbus_attach_args *va = aux;
268	volatile struct adder *adder;
269	vaddr_t tmp;
270	u_int depth;
271	u_short status;
272
273	switch (vax_boardtype) {
274	default:
275		return 0;
276
277	case VAX_BTYP_410:
278	case VAX_BTYP_420:
279	case VAX_BTYP_43:
280		if (va->va_paddr != GPXADDR)
281			return 0;
282
283		/* not present on microvaxes */
284		if ((vax_confdata & KA420_CFG_MULTU) != 0)
285			return 0;
286
287		if ((vax_confdata & KA420_CFG_VIDOPT) == 0)
288			return 0;
289		break;
290	}
291
292	/* Check for hardware */
293	adder = (volatile struct adder *)
294	    vax_map_physmem(va->va_paddr + GPX_ADDER_OFFSET, 1);
295	if (adder == NULL)
296		return 0;
297	adder->status = 0;
298	status = adder->status;
299	vax_unmap_physmem((vaddr_t)adder, 1);
300	if (status == offsetof(struct adder, status))
301		return 0;
302
303	/* Check for a recognized color depth */
304	tmp = vax_map_physmem(va->va_paddr + GPX_READBACK_OFFSET, 1);
305	if (tmp == 0L)
306		return 0;
307	depth = (*(volatile uint16_t *)tmp) & 0x00f0;
308	vax_unmap_physmem(tmp, 1);
309	if (depth != 0x00f0 && depth != 0x0080)
310		return 0;
311
312	/* when already running as console, always fake things */
313	if ((vax_confdata & KA420_CFG_L3CON) == 0
314#if NWSDISPLAY > 0
315	    && cn_tab->cn_putc == wsdisplay_cnputc
316#endif
317	) {
318		struct vsbus_softc *sc = device_private(parent);
319		sc->sc_mask = 0x08;
320		scb_fake(0x44, 0x15);
321	} else {
322		adder = (struct adder *)vax_map_physmem(va->va_paddr +
323		    GPX_ADDER_OFFSET, 1);
324		if (adder == NULL)
325			return 0;
326		adder->interrupt_enable = FRAME_SYNC;
327		DELAY(100000);	/* enough to get a retrace interrupt */
328		adder->interrupt_enable = 0;
329		vax_unmap_physmem((vaddr_t)adder, 1);
330	}
331	return 20;
332}
333
334static void
335gpx_attach(device_t parent, device_t self, void *aux)
336{
337	struct gpx_softc *sc = device_private(self);
338	struct vsbus_attach_args *va = aux;
339	struct gpx_screen *scr;
340	struct wsemuldisplaydev_attach_args aa;
341	int console;
342	vaddr_t tmp;
343
344	sc->sc_dev = self;
345	console =
346#if NWSDISPLAY > 0
347	    (vax_confdata & KA420_CFG_L3CON) == 0 &&
348	    cn_tab->cn_putc == wsdisplay_cnputc;
349#else
350	    (vax_confdata & KA420_CFG_L3CON) == 0;
351#endif
352	if (console) {
353		scr = &gpx_consscr;
354		sc->sc_nscreens = 1;
355	} else {
356		scr = kmem_zalloc(sizeof(*scr), KM_SLEEP);
357
358		tmp = vax_map_physmem(va->va_paddr + GPX_READBACK_OFFSET, 1);
359		if (tmp == 0L) {
360			printf(": can not probe depth\n");
361			goto bad1;
362		}
363		scr->ss_depth = (*(uint16_t *)tmp & 0x00f0) == 0x00f0 ? 4 : 8;
364		vax_unmap_physmem(tmp, 1);
365
366		scr->ss_adder = (struct adder *)vax_map_physmem(va->va_paddr +
367		    GPX_ADDER_OFFSET, 1);
368		if (scr->ss_adder == NULL) {
369			aprint_error(": can not map frame buffer registers\n");
370			goto bad1;
371		}
372
373		scr->ss_vdac = (void *)vax_map_physmem(va->va_paddr +
374		    GPX_VDAC_OFFSET, 1);
375		if (scr->ss_vdac == NULL) {
376			aprint_error(": can not map RAMDAC\n");
377			goto bad2;
378		}
379
380#if 0
381		scr->ss_cursor =
382		    (struct dc503reg *)vax_map_physmem(va->va_paddr +
383		    GPX_CURSOR_OFFSET, 1);
384		if (scr->ss_cursor == NULL) {
385			aprint_error(": can not map cursor chip\n");
386			goto bad3;
387		}
388#endif
389
390		if (gpx_setup_screen(scr) != 0) {
391			aprint_error(": initialization failed\n");
392			goto bad4;
393		}
394	}
395	sc->sc_scr = scr;
396
397	aprint_normal("\n");
398	aprint_normal_dev(self, "%dx%d %d plane color framebuffer\n",
399	    GPX_WIDTH, GPX_VISHEIGHT, scr->ss_depth);
400
401	aa.console = console;
402	aa.scrdata = &gpx_screenlist;
403	aa.accessops = &gpx_accessops;
404	aa.accesscookie = sc;
405
406	config_found(self, &aa, wsemuldisplaydevprint, CFARGS_NONE);
407
408	return;
409
410 bad4:
411#if 0
412	vax_unmap_physmem((vaddr_t)scr->ss_cursor, 1);
413 bad3:
414#endif
415	vax_unmap_physmem((vaddr_t)scr->ss_vdac, 1);
416 bad2:
417	vax_unmap_physmem((vaddr_t)scr->ss_adder, 1);
418 bad1:
419	kmem_free(scr, sizeof(*scr));
420}
421
422/*
423 * wsdisplay accessops
424 */
425
426static int
427gpx_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
428{
429	struct gpx_softc *sc = v;
430	struct gpx_screen *ss = sc->sc_scr;
431	struct wsdisplay_fbinfo *wdf;
432	struct wsdisplay_cmap *cm;
433	int error;
434
435	switch (cmd) {
436	case WSDISPLAYIO_GTYPE:
437		*(u_int *)data = WSDISPLAY_TYPE_GPX;
438		break;
439
440	case WSDISPLAYIO_GINFO:
441		wdf = (struct wsdisplay_fbinfo *)data;
442		wdf->height = ss->ss_ri.ri_height;
443		wdf->width = ss->ss_ri.ri_width;
444		wdf->depth = ss->ss_depth;
445		wdf->cmsize = 1 << ss->ss_depth;
446		break;
447
448	case WSDISPLAYIO_GETCMAP:
449		cm = (struct wsdisplay_cmap *)data;
450		error = gpx_getcmap(ss, cm);
451		if (error != 0)
452			return error;
453		break;
454	case WSDISPLAYIO_PUTCMAP:
455		cm = (struct wsdisplay_cmap *)data;
456		error = gpx_putcmap(ss, cm);
457		if (error != 0)
458			return error;
459		gpx_loadcmap(ss, cm->index, cm->count);
460		break;
461
462	case WSDISPLAYIO_GVIDEO:
463	case WSDISPLAYIO_SVIDEO:
464		break;
465
466	case WSDISPLAYIO_LINEBYTES:	/* no linear mapping */
467		return -1;
468
469	default:
470		return EPASSTHROUGH;
471	}
472	return 0;
473}
474
475static paddr_t
476gpx_mmap(void *v, void *vs, off_t offset, int prot)
477{
478
479	return -1;
480}
481
482static int
483gpx_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
484    int *curxp, int *curyp, long *defattrp)
485{
486	struct gpx_softc *sc = v;
487	struct gpx_screen *ss = sc->sc_scr;
488	struct rasops_info *ri = &ss->ss_ri;
489
490	if (sc->sc_nscreens > 0)
491		return ENOMEM;
492
493	*cookiep = ri;
494	*curxp = *curyp = 0;
495	ri->ri_ops.allocattr(ri, 0, 0, 0, defattrp);
496	sc->sc_nscreens++;
497
498	return 0;
499}
500
501static void
502gpx_free_screen(void *v, void *cookie)
503{
504	struct gpx_softc *sc = v;
505
506	sc->sc_nscreens--;
507}
508
509static int
510gpx_show_screen(void *v, void *cookie, int waitok,
511    void (*cb)(void *, int, int), void *cbarg)
512{
513
514	return 0;
515}
516
517/*
518 * wsdisplay emulops
519 */
520
521static void
522gpx_putchar(void *v, int row, int col, u_int uc, long attr)
523{
524	struct rasops_info *ri = v;
525	struct gpx_screen *ss = ri->ri_hw;
526	struct wsdisplay_font *font = ri->ri_font;
527	int dx, dy, sx, sy, fg, bg, ul;
528
529	rasops_unpack_attr(attr, &fg, &bg, &ul);
530
531	/* find where to output the glyph... */
532	dx = col * font->fontwidth + ri->ri_xorigin;
533	dy = row * font->fontheight + ri->ri_yorigin;
534	/* ... and where to pick it from */
535	uc -= font->firstchar;
536	sx = (uc % ss->ss_gpr) * font->stride * NBBY;
537	sy = GPX_HEIGHT - (1 + uc / ss->ss_gpr) * font->fontheight;
538
539	/* setup VIPER operand control registers */
540	while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff))
541		continue;
542	gpx_viper_write(ss, SRC1_OCR_B,
543	    EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
544	gpx_viper_write(ss, DST_OCR_B,
545	    EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
546	gpx_viper_write(ss, MASK_1, 0xffff);
547	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, fg);
548	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, bg);
549	ss->ss_adder->x_clip_min = 0;
550	ss->ss_adder->x_clip_max = GPX_WIDTH;
551	ss->ss_adder->y_clip_min = 0;
552	ss->ss_adder->y_clip_max = GPX_VISHEIGHT;
553	/* load DESTINATION origin and vectors */
554	ss->ss_adder->fast_dest_dy = 0;
555	ss->ss_adder->slow_dest_dx = 0;
556	ss->ss_adder->error_1 = 0;
557	ss->ss_adder->error_2 = 0;
558	ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
559	gpx_wait(ss, RASTEROP_COMPLETE);
560	ss->ss_adder->destination_x = dx;
561	ss->ss_adder->fast_dest_dx = font->fontwidth;
562	ss->ss_adder->destination_y = dy;
563	ss->ss_adder->slow_dest_dy = font->fontheight;
564	/* load SOURCE origin and vectors */
565	ss->ss_adder->source_1_x = sx;
566	ss->ss_adder->source_1_y = sy;
567	ss->ss_adder->source_1_dx = font->fontwidth;
568	ss->ss_adder->source_1_dy = font->fontheight;
569	ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | LF_R1;
570
571	if (ul != 0) {
572		gpx_fillrect(ss, dx, dy + font->fontheight - 2, font->fontwidth,
573		    1, attr, LF_R3);	/* fg fill */
574	}
575}
576
577static void
578gpx_copycols(void *v, int row, int src, int dst, int cnt)
579{
580	struct rasops_info *ri = v;
581	struct gpx_screen *ss = ri->ri_hw;
582	struct wsdisplay_font *font = ri->ri_font;
583	int sx, y, dx, w, h;
584
585	sx = ri->ri_xorigin + src * font->fontwidth;
586	dx = ri->ri_xorigin + dst * font->fontwidth;
587	w = cnt * font->fontwidth;
588	y = ri->ri_yorigin + row * font->fontheight;
589	h = font->fontheight;
590
591	gpx_copyrect(ss, sx, y, dx, y, w, h);
592}
593
594static void
595gpx_erasecols(void *v, int row, int col, int cnt, long attr)
596{
597	struct rasops_info *ri = v;
598	struct gpx_screen *ss = ri->ri_hw;
599	struct wsdisplay_font *font = ri->ri_font;
600	int x, y, dx, dy;
601
602	x = ri->ri_xorigin + col * font->fontwidth;
603	dx = cnt * font->fontwidth;
604	y = ri->ri_yorigin + row * font->fontheight;
605	dy = font->fontheight;
606
607	gpx_fillrect(ss, x, y, dx, dy, attr, LF_R2); /* bg fill */
608}
609
610static void
611gpx_copyrows(void *v, int src, int dst, int cnt)
612{
613	struct rasops_info *ri = v;
614	struct gpx_screen *ss = ri->ri_hw;
615	struct wsdisplay_font *font = ri->ri_font;
616	int x, sy, dy, w, h;
617
618	x = ri->ri_xorigin;
619	w = ri->ri_emustride;
620	sy = ri->ri_yorigin + src * font->fontheight;
621	dy = ri->ri_yorigin + dst * font->fontheight;
622	h = cnt * font->fontheight;
623
624	gpx_copyrect(ss, x, sy, x, dy, w, h);
625}
626
627static void
628gpx_eraserows(void *v, int row, int cnt, long attr)
629{
630	struct rasops_info *ri = v;
631	struct gpx_screen *ss = ri->ri_hw;
632	struct wsdisplay_font *font = ri->ri_font;
633	int x, y, dx, dy;
634
635	x = ri->ri_xorigin;
636	dx = ri->ri_emustride;
637	y = ri->ri_yorigin + row * font->fontheight;
638	dy = cnt * font->fontheight;
639
640	gpx_fillrect(ss, x, y, dx, dy, attr, LF_R2); /* bg fill */
641}
642
643static void
644gpx_do_cursor(struct rasops_info *ri)
645{
646	struct gpx_screen *ss = ri->ri_hw;
647	int x, y, w, h;
648
649	x = ri->ri_ccol * ri->ri_font->fontwidth + ri->ri_xorigin;
650	y = ri->ri_crow * ri->ri_font->fontheight + ri->ri_yorigin;
651	w = ri->ri_font->fontwidth;
652	h = ri->ri_font->fontheight;
653
654	gpx_fillrect(ss, x, y, w, h, WSCOL_WHITE << 24, LF_R4);	/* invert */
655}
656
657/*
658 * low-level programming routines
659 */
660
661static int
662gpx_wait(struct gpx_screen *ss, int bits)
663{
664	int i;
665
666	ss->ss_adder->status = 0;
667	for (i = 100000; i != 0; i--) {
668		if ((ss->ss_adder->status & bits) == bits)
669			break;
670		DELAY(1);
671	}
672
673	return i == 0;
674}
675
676static int
677gpx_viper_write(struct gpx_screen *ss, u_int reg, uint16_t val)
678{
679	if (gpx_wait(ss, ADDRESS_COMPLETE) == 0 &&
680	    gpx_wait(ss, TX_READY) == 0) {
681		ss->ss_adder->id_data = val;
682		ss->ss_adder->command = ID_LOAD | reg;
683		return 0;
684	}
685#ifdef DEBUG
686	if (ss->ss_console == 0)	/* don't make things worse! */
687		printf("gpx_viper_write failure, reg %x val %x\n", reg, val);
688#endif
689	return 1;
690}
691
692/* Initialize the damned beast. Straight from qdss. */
693static void
694gpx_reset_viper(struct gpx_screen *ss)
695{
696	int i;
697
698	ss->ss_adder->interrupt_enable = 0;
699	ss->ss_adder->command = CANCEL;
700	/* set monitor timing */
701	ss->ss_adder->x_scan_count_0 = 0x2800;
702	ss->ss_adder->x_scan_count_1 = 0x1020;
703	ss->ss_adder->x_scan_count_2 = 0x003a;
704	ss->ss_adder->x_scan_count_3 = 0x38f0;
705	ss->ss_adder->x_scan_count_4 = 0x6128;
706	ss->ss_adder->x_scan_count_5 = 0x093a;
707	ss->ss_adder->x_scan_count_6 = 0x313c;
708	ss->ss_adder->sync_phase_adj = 0x0100;
709	ss->ss_adder->x_scan_conf = 0x00c8;
710	/*
711	 * got a bug in second pass ADDER! lets take care of it...
712	 *
713	 * normally, just use the code in the following bug fix code, but to
714	 * make repeated demos look pretty, load the registers as if there was
715	 * no bug and then test to see if we are getting sync
716	 */
717	ss->ss_adder->y_scan_count_0 = 0x135f;
718	ss->ss_adder->y_scan_count_1 = 0x3363;
719	ss->ss_adder->y_scan_count_2 = 0x2366;
720	ss->ss_adder->y_scan_count_3 = 0x0388;
721	/*
722	 * if no sync, do the bug fix code
723	 */
724	if (gpx_wait(ss, FRAME_SYNC) != 0) {
725		/*
726		 * First load all Y scan registers with very short frame and
727		 * wait for scroll service.  This guarantees at least one SYNC
728		 * to fix the pass 2 Adder initialization bug (synchronizes
729		 * XCINCH with DMSEEDH)
730		 */
731		ss->ss_adder->y_scan_count_0 = 0x01;
732		ss->ss_adder->y_scan_count_1 = 0x01;
733		ss->ss_adder->y_scan_count_2 = 0x01;
734		ss->ss_adder->y_scan_count_3 = 0x01;
735		/* delay at least 1 full frame time */
736		gpx_wait(ss, FRAME_SYNC);
737		gpx_wait(ss, FRAME_SYNC);
738		/*
739		 * now load the REAL sync values (in reverse order just to
740		 * be safe).
741		 */
742		ss->ss_adder->y_scan_count_3 = 0x0388;
743		ss->ss_adder->y_scan_count_2 = 0x2366;
744		ss->ss_adder->y_scan_count_1 = 0x3363;
745		ss->ss_adder->y_scan_count_0 = 0x135f;
746	}
747	/* zero the index registers */
748	ss->ss_adder->x_index_pending = 0;
749	ss->ss_adder->y_index_pending = 0;
750	ss->ss_adder->x_index_new = 0;
751	ss->ss_adder->y_index_new = 0;
752	ss->ss_adder->x_index_old = 0;
753	ss->ss_adder->y_index_old = 0;
754	ss->ss_adder->pause = 0;
755	/* set rasterop mode to normal pen down */
756	ss->ss_adder->rasterop_mode =
757	    DST_WRITE_ENABLE | DST_INDEX_ENABLE | NORMAL;
758	/* set the rasterop registers to default values */
759	ss->ss_adder->source_1_dx = 1;
760	ss->ss_adder->source_1_dy = 1;
761	ss->ss_adder->source_1_x = 0;
762	ss->ss_adder->source_1_y = 0;
763	ss->ss_adder->destination_x = 0;
764	ss->ss_adder->destination_y = 0;
765	ss->ss_adder->fast_dest_dx = 1;
766	ss->ss_adder->fast_dest_dy = 0;
767	ss->ss_adder->slow_dest_dx = 0;
768	ss->ss_adder->slow_dest_dy = 1;
769	ss->ss_adder->error_1 = 0;
770	ss->ss_adder->error_2 = 0;
771	/* scale factor = UNITY */
772	ss->ss_adder->fast_scale = UNITY;
773	ss->ss_adder->slow_scale = UNITY;
774	/* set the source 2 parameters */
775	ss->ss_adder->source_2_x = 0;
776	ss->ss_adder->source_2_y = 0;
777	ss->ss_adder->source_2_size = 0x0022;
778	/* initialize plane addresses for eight vipers */
779	for (i = 0; i < 8; i++) {
780		gpx_viper_write(ss, CS_UPDATE_MASK, 1 << i);
781		gpx_viper_write(ss, PLANE_ADDRESS, i);
782	}
783	/* initialize the external registers. */
784	gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff);
785	gpx_viper_write(ss, CS_SCROLL_MASK, 0x00ff);
786	/* initialize resolution mode */
787	gpx_viper_write(ss, MEMORY_BUS_WIDTH, 0x000c);	/* bus width = 16 */
788	gpx_viper_write(ss, RESOLUTION_MODE, 0x0000);	/* one bit/pixel */
789	/* initialize viper registers */
790	gpx_viper_write(ss, SCROLL_CONSTANT,
791	    SCROLL_ENABLE | VIPER_LEFT | VIPER_UP);
792	gpx_viper_write(ss, SCROLL_FILL, 0x0000);
793	/* set clipping and scrolling limits to full screen */
794	gpx_wait(ss, ADDRESS_COMPLETE);
795	ss->ss_adder->x_clip_min = 0;
796	ss->ss_adder->x_clip_max = GPX_WIDTH;
797	ss->ss_adder->y_clip_min = 0;
798	ss->ss_adder->y_clip_max = GPX_HEIGHT;
799	ss->ss_adder->scroll_x_min = 0;
800	ss->ss_adder->scroll_x_max = GPX_WIDTH;
801	ss->ss_adder->scroll_y_min = 0;
802	ss->ss_adder->scroll_y_max = GPX_HEIGHT;
803	gpx_wait(ss, FRAME_SYNC);	/* wait at LEAST 1 full frame */
804	gpx_wait(ss, FRAME_SYNC);
805	ss->ss_adder->x_index_pending = 0;
806	ss->ss_adder->y_index_pending = 0;
807	ss->ss_adder->x_index_new = 0;
808	ss->ss_adder->y_index_new = 0;
809	ss->ss_adder->x_index_old = 0;
810	ss->ss_adder->y_index_old = 0;
811	gpx_wait(ss, ADDRESS_COMPLETE);
812	gpx_viper_write(ss, LEFT_SCROLL_MASK, 0x0000);
813	gpx_viper_write(ss, RIGHT_SCROLL_MASK, 0x0000);
814	/* set source and the mask register to all ones */
815	gpx_viper_write(ss, SOURCE, 0xffff);
816	gpx_viper_write(ss, MASK_1, 0xffff);
817	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
818	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
819	/* initialize Operand Control Register banks for fill command */
820	gpx_viper_write(ss, SRC1_OCR_A,
821	    EXT_NONE | INT_M1_M2  | NO_ID | WAIT);
822	gpx_viper_write(ss, SRC2_OCR_A,
823	    EXT_NONE | INT_SOURCE | NO_ID | NO_WAIT);
824	gpx_viper_write(ss, DST_OCR_A,
825	    EXT_NONE | INT_NONE | NO_ID | NO_WAIT);
826	gpx_viper_write(ss, SRC1_OCR_B,
827	    EXT_NONE | INT_SOURCE | NO_ID | WAIT);
828	gpx_viper_write(ss, SRC2_OCR_B,
829	    EXT_NONE | INT_M1_M2  | NO_ID | NO_WAIT);
830	gpx_viper_write(ss, DST_OCR_B,
831	    EXT_NONE | INT_NONE | NO_ID | NO_WAIT);
832
833	/*
834	 * Init Logic Unit Function registers.
835	 */
836	/* putchar */
837	gpx_viper_write(ss, LU_FUNCTION_R1, FULL_SRC_RESOLUTION | LF_SOURCE);
838	/* erase{cols,rows} */
839	gpx_viper_write(ss, LU_FUNCTION_R2, FULL_SRC_RESOLUTION | LF_ZEROS);
840	/* underline */
841	gpx_viper_write(ss, LU_FUNCTION_R3, FULL_SRC_RESOLUTION | LF_ONES);
842	/* cursor */
843	gpx_viper_write(ss, LU_FUNCTION_R4, FULL_SRC_RESOLUTION | LF_NOT_D);
844}
845
846/* Clear the whole screen. Straight from qdss. */
847static void
848gpx_clear_screen(struct gpx_screen *ss)
849{
850
851	ss->ss_adder->x_limit = GPX_WIDTH;
852	ss->ss_adder->y_limit = GPX_HEIGHT;
853	ss->ss_adder->y_offset_pending = 0;
854	gpx_wait(ss, FRAME_SYNC);	/* wait at LEAST 1 full frame */
855	gpx_wait(ss, FRAME_SYNC);
856	ss->ss_adder->y_scroll_constant = SCROLL_ERASE;
857	gpx_wait(ss, FRAME_SYNC);
858	gpx_wait(ss, FRAME_SYNC);
859	ss->ss_adder->y_offset_pending = GPX_VISHEIGHT;
860	gpx_wait(ss, FRAME_SYNC);
861	gpx_wait(ss, FRAME_SYNC);
862	ss->ss_adder->y_scroll_constant = SCROLL_ERASE;
863	gpx_wait(ss, FRAME_SYNC);
864	gpx_wait(ss, FRAME_SYNC);
865	ss->ss_adder->y_offset_pending = 2 * GPX_VISHEIGHT;
866	gpx_wait(ss, FRAME_SYNC);
867	gpx_wait(ss, FRAME_SYNC);
868	ss->ss_adder->y_scroll_constant = SCROLL_ERASE;
869	gpx_wait(ss, FRAME_SYNC);
870	gpx_wait(ss, FRAME_SYNC);
871	ss->ss_adder->y_offset_pending = 0;	 /* back to normal */
872	gpx_wait(ss, FRAME_SYNC);
873	gpx_wait(ss, FRAME_SYNC);
874	ss->ss_adder->x_limit = GPX_WIDTH;
875	ss->ss_adder->y_limit = GPX_VISHEIGHT;
876}
877
878static int
879gpx_setup_screen(struct gpx_screen *ss)
880{
881	struct rasops_info *ri = &ss->ss_ri;
882	int cookie;
883
884	memset(ri, 0, sizeof(*ri));
885	ri->ri_depth = 8;	/* masquerade as a 8 bit device for rasops */
886	ri->ri_width = GPX_WIDTH;
887	ri->ri_height = GPX_VISHEIGHT;
888	ri->ri_stride = GPX_WIDTH;
889	ri->ri_flg = RI_CENTER;		/* no RI_CLEAR as ri_bits is NULL! */
890	ri->ri_hw = ss;
891	if (ss == &gpx_consscr)
892		ri->ri_flg |= RI_NO_AUTO;
893
894	/*
895	 * We can not let rasops select our font, because we need to use
896	 * a font with right-to-left bit order on this frame buffer.
897	 */
898	wsfont_init();
899	cookie = wsfont_find(NULL, 12, 0, 0, WSDISPLAY_FONTORDER_R2L,
900	    WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP);
901	if (cookie < 0)
902		cookie = wsfont_find(NULL, 8, 0, 0, WSDISPLAY_FONTORDER_R2L,
903		    WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP);
904	if (cookie < 0)
905		cookie = wsfont_find(NULL, 0, 0, 0, WSDISPLAY_FONTORDER_R2L,
906		    WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP);
907	if (cookie < 0)
908		return -1;
909	if (wsfont_lock(cookie, &ri->ri_font) != 0)
910		return -1;
911	ri->ri_wsfcookie = cookie;
912
913	/*
914	 * Ask for an unholy big display, rasops will trim this to more
915	 * reasonable values.
916	 */
917	if (rasops_init(ri, 160, 160) != 0)
918		return -1;
919
920	/*
921	 * Override the rasops emulops.
922	 */
923	ri->ri_ops.copyrows = gpx_copyrows;
924	ri->ri_ops.copycols = gpx_copycols;
925	ri->ri_ops.eraserows = gpx_eraserows;
926	ri->ri_ops.erasecols = gpx_erasecols;
927	ri->ri_ops.putchar = gpx_putchar;
928	ri->ri_do_cursor = gpx_do_cursor;
929
930	gpx_stdscreen.ncols = ri->ri_cols;
931	gpx_stdscreen.nrows = ri->ri_rows;
932	gpx_stdscreen.textops = &ri->ri_ops;
933	gpx_stdscreen.fontwidth = ri->ri_font->fontwidth;
934	gpx_stdscreen.fontheight = ri->ri_font->fontheight;
935	gpx_stdscreen.capabilities = ri->ri_caps;
936
937	/*
938	 * Initialize RAMDAC.
939	 */
940	if (ss->ss_depth == 8) {
941		struct ramdac8 *rd = ss->ss_vdac;
942		rd->address = BT_CR;
943		rd->control = BTCR_RAMENA | BTCR_BLINK_1648 | BTCR_MPLX_4;
944	} else {
945		struct ramdac4 *rd = ss->ss_vdac;
946		rd->control = RAMDAC4_INIT;
947	}
948
949	/*
950	 * Put the ADDER and VIPER in a good state.
951	 */
952	gpx_reset_viper(ss);
953
954	/*
955	 * Initialize colormap.
956	 */
957	gpx_resetcmap(ss);
958
959	/*
960	 * Clear display (including non-visible area), in 864 lines chunks.
961	 */
962	gpx_clear_screen(ss);
963
964	/*
965	 * Copy our font to the offscreen area.
966	 */
967	gpx_upload_font(ss);
968
969#if 0
970	ss->ss_cursor->cmdr = ss->ss_curcmd = PCCCMD_HSHI;
971#endif
972
973	return 0;
974}
975
976/*
977 * Copy the selected wsfont to non-visible frame buffer area.
978 * This is necessary since the only way to send data to the frame buffer
979 * is through the ID interface, which is slow and needs 16 bit wide data.
980 * Adapted from qdss.
981 */
982static void
983gpx_upload_font(struct gpx_screen *ss)
984{
985	struct rasops_info *ri = &ss->ss_ri;
986	struct wsdisplay_font *font = ri->ri_font;
987	uint8_t *fontbits, *fb;
988	u_int remaining, nchars, row;
989	u_int i, j;
990	uint16_t data;
991
992	/* setup VIPER operand control registers */
993
994	gpx_viper_write(ss, MASK_1, 0xffff);
995	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
996	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
997
998	gpx_viper_write(ss, SRC1_OCR_B,
999	    EXT_NONE | INT_NONE | ID | BAR_SHIFT_DELAY);
1000	gpx_viper_write(ss, SRC2_OCR_B,
1001	    EXT_NONE | INT_NONE | ID | BAR_SHIFT_DELAY);
1002	gpx_viper_write(ss, DST_OCR_B,
1003	    EXT_SOURCE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
1004
1005	ss->ss_adder->rasterop_mode =
1006	    DST_WRITE_ENABLE | DST_INDEX_ENABLE | NORMAL;
1007	gpx_wait(ss, RASTEROP_COMPLETE);
1008
1009	/*
1010	 * Load font data. The font is uploaded in 8 or 16 bit wide cells, on
1011	 * as many ``lines'' as necessary at the end of the display.
1012	 */
1013	ss->ss_gpr = MIN(GPX_WIDTH / (NBBY * font->stride), font->numchars);
1014	if ((ss->ss_gpr & 1) != 0)
1015		ss->ss_gpr--;
1016	fontbits = font->data;
1017	for (row = 1, remaining = font->numchars; remaining != 0;
1018	    row++, remaining -= nchars) {
1019		nchars = MIN(ss->ss_gpr, remaining);
1020
1021		ss->ss_adder->destination_x = 0;
1022		ss->ss_adder->destination_y =
1023		    GPX_HEIGHT - row * font->fontheight;
1024		ss->ss_adder->fast_dest_dx = nchars * 16;
1025		ss->ss_adder->slow_dest_dy = font->fontheight;
1026
1027		/* setup for processor to bitmap xfer */
1028		gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff);
1029		ss->ss_adder->cmd = PBT | OCRB | DTE | LF_R1 | 2; /*XXX why 2?*/
1030
1031		/* iteratively do the processor to bitmap xfer */
1032		for (i = font->fontheight; i != 0; i--) {
1033			fb = fontbits;
1034			fontbits += font->stride;
1035			/* PTOB a scan line */
1036			for (j = nchars; j != 0; j--) {
1037				/* PTOB one scan of a char cell */
1038				if (font->stride == 1) {
1039					data = *fb;
1040					fb += font->fontheight;
1041					/*
1042					 * Do not access past font memory if
1043					 * it has an odd number of characters
1044					 * and this is the last pair.
1045					 */
1046					if (j != 1 || (nchars & 1) == 0 ||
1047					    remaining != nchars) {
1048						data |= ((uint16_t)*fb) << 8;
1049						fb += font->fontheight;
1050					}
1051				} else {
1052					data =
1053					    fb[0] | (((uint16_t)fb[1]) << 8);
1054					fb += font->fontheight * font->stride;
1055				}
1056
1057				gpx_wait(ss, TX_READY);
1058				ss->ss_adder->id_data = data;
1059			}
1060		}
1061		fontbits += (nchars - 1) * font->stride * font->fontheight;
1062	}
1063}
1064
1065static void
1066gpx_copyrect(struct gpx_screen *ss,
1067    int sx, int sy, int dx, int dy, int w, int h)
1068{
1069
1070	while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff))
1071		continue;
1072	gpx_viper_write(ss, MASK_1, 0xffff);
1073	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
1074	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
1075	gpx_viper_write(ss, SRC1_OCR_B,
1076	    EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
1077	gpx_viper_write(ss, DST_OCR_B,
1078	    EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
1079	ss->ss_adder->fast_dest_dy = 0;
1080	ss->ss_adder->slow_dest_dx = 0;
1081	ss->ss_adder->error_1 = 0;
1082	ss->ss_adder->error_2 = 0;
1083	ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
1084	gpx_wait(ss, RASTEROP_COMPLETE);
1085	ss->ss_adder->destination_x = dx;
1086	ss->ss_adder->fast_dest_dx = w;
1087	ss->ss_adder->destination_y = dy;
1088	ss->ss_adder->slow_dest_dy = h;
1089	ss->ss_adder->source_1_x = sx;
1090	ss->ss_adder->source_1_dx = w;
1091	ss->ss_adder->source_1_y = sy;
1092	ss->ss_adder->source_1_dy = h;
1093	ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | LF_R1;
1094}
1095
1096/*
1097 * Fill a rectangle with the given attribute and function (i.e. rop).
1098 */
1099static void
1100gpx_fillrect(struct gpx_screen *ss, int x, int y, int dx, int dy, long attr,
1101    u_int function)
1102{
1103	int fg, bg;
1104
1105	rasops_unpack_attr(attr, &fg, &bg, NULL);
1106
1107	while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff))
1108		continue;
1109	gpx_viper_write(ss, MASK_1, 0xffff);
1110	gpx_viper_write(ss, SOURCE, 0xffff);
1111	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, fg);
1112	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, bg);
1113	gpx_viper_write(ss, SRC1_OCR_B,
1114	    EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
1115	gpx_viper_write(ss, DST_OCR_B,
1116	    EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
1117	ss->ss_adder->fast_dest_dx = 0;
1118	ss->ss_adder->fast_dest_dy = 0;
1119	ss->ss_adder->slow_dest_dx = 0;
1120	ss->ss_adder->error_1 = 0;
1121	ss->ss_adder->error_2 = 0;
1122	ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
1123	gpx_wait(ss, RASTEROP_COMPLETE);
1124	ss->ss_adder->destination_x = x;
1125	ss->ss_adder->fast_dest_dx = dx;
1126	ss->ss_adder->destination_y = y;
1127	ss->ss_adder->slow_dest_dy = dy;
1128	ss->ss_adder->source_1_x = x;
1129	ss->ss_adder->source_1_dx = dx;
1130	ss->ss_adder->source_1_y = y;
1131	ss->ss_adder->source_1_dy = dy;
1132	ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | function;
1133}
1134
1135/*
1136 * Colormap handling routines
1137 */
1138
1139static int
1140gpx_getcmap(struct gpx_screen *ss, struct wsdisplay_cmap *cm)
1141{
1142	u_int index = cm->index, count = cm->count, i;
1143	u_int colcount = 1 << ss->ss_depth;
1144	int error;
1145	uint8_t ramp[256], *c, *r;
1146
1147	if (index >= colcount || count > colcount - index)
1148		return EINVAL;
1149
1150	/* extract reds */
1151	c = ss->ss_cmap + 0 + index * 3;
1152	for (i = count, r = ramp; i != 0; i--)
1153		*r++ = *c << (8 - ss->ss_depth), c += 3;
1154	if ((error = copyout(ramp, cm->red, count)) != 0)
1155		return error;
1156
1157	/* extract greens */
1158	c = ss->ss_cmap + 1 + index * 3;
1159	for (i = count, r = ramp; i != 0; i--)
1160		*r++ = *c << (8 - ss->ss_depth), c += 3;
1161	if ((error = copyout(ramp, cm->green, count)) != 0)
1162		return error;
1163
1164	/* extract blues */
1165	c = ss->ss_cmap + 2 + index * 3;
1166	for (i = count, r = ramp; i != 0; i--)
1167		*r++ = *c << (8 - ss->ss_depth), c += 3;
1168	if ((error = copyout(ramp, cm->blue, count)) != 0)
1169		return error;
1170
1171	return 0;
1172}
1173
1174static int
1175gpx_putcmap(struct gpx_screen *ss, struct wsdisplay_cmap *cm)
1176{
1177	u_int index = cm->index, count = cm->count;
1178	u_int colcount = 1 << ss->ss_depth;
1179	int i, error;
1180	uint8_t r[256], g[256], b[256], *nr, *ng, *nb, *c;
1181
1182	if (index >= colcount || count > colcount - index)
1183		return EINVAL;
1184
1185	if ((error = copyin(cm->red, r, count)) != 0)
1186		return error;
1187	if ((error = copyin(cm->green, g, count)) != 0)
1188		return error;
1189	if ((error = copyin(cm->blue, b, count)) != 0)
1190		return error;
1191
1192	nr = r, ng = g, nb = b;
1193	c = ss->ss_cmap + index * 3;
1194	for (i = count; i != 0; i--) {
1195		*c++ = *nr++ >> (8 - ss->ss_depth);
1196		*c++ = *ng++ >> (8 - ss->ss_depth);
1197		*c++ = *nb++ >> (8 - ss->ss_depth);
1198	}
1199
1200	return 0;
1201}
1202
1203static void
1204gpx_loadcmap(struct gpx_screen *ss, int from, int count)
1205{
1206	uint8_t *cmap = ss->ss_cmap;
1207	int i, color12;
1208
1209	gpx_wait(ss, FRAME_SYNC);
1210	if (ss->ss_depth == 8) {
1211		struct ramdac8 *rd = ss->ss_vdac;
1212
1213		cmap += from * 3;
1214		rd->address = from;
1215		for (i = 0; i < count * 3; i++)
1216			rd->cmapdata = *cmap++;
1217	} else {
1218		struct ramdac4 *rd = ss->ss_vdac;
1219
1220		cmap = ss->ss_cmap + from;
1221		for (i = from; i < from + count; i++) {
1222			color12  = (*cmap++ >> 4) << 0;
1223			color12 |= (*cmap++ >> 4) << 8;
1224			color12 |= (*cmap++ >> 4) << 4;
1225			rd->colormap[i] = color12;
1226		}
1227	}
1228}
1229
1230static void
1231gpx_resetcmap(struct gpx_screen *ss)
1232{
1233
1234	if (ss->ss_depth == 8)
1235		memcpy(ss->ss_cmap, rasops_cmap, sizeof(ss->ss_cmap));
1236	else {
1237		memcpy(ss->ss_cmap, rasops_cmap, 8 * 3);
1238		memcpy(ss->ss_cmap + 8 * 3, rasops_cmap + 0xf8 * 3, 8 * 3);
1239	}
1240	gpx_loadcmap(ss, 0, 1 << ss->ss_depth);
1241
1242	/*
1243	 * On the 4bit RAMDAC, make the hardware cursor black on black
1244	 */
1245	if (ss->ss_depth != 8) {
1246		struct ramdac4 *rd = ss->ss_vdac;
1247
1248		rd->cursormap[0] = rd->cursormap[1] =
1249		    rd->cursormap[2] = rd->cursormap[3] = 0x0000;
1250	}
1251}
1252
1253/*
1254 * Console support code
1255 */
1256
1257cons_decl(gpx);
1258
1259/*
1260 * Called very early to setup the glass tty as console.
1261 * Because it's called before the VM system is initialized, virtual memory
1262 * for the framebuffer can be stolen directly without disturbing anything.
1263 */
1264void
1265gpxcnprobe(struct consdev *cndev)
1266{
1267	extern vaddr_t virtual_avail;
1268	extern const struct cdevsw wsdisplay_cdevsw;
1269	volatile struct adder *adder;
1270	vaddr_t tmp;
1271	int depth;
1272	u_short status;
1273
1274	switch (vax_boardtype) {
1275	case VAX_BTYP_410:
1276	case VAX_BTYP_420:
1277	case VAX_BTYP_43:
1278		if ((vax_confdata & (KA420_CFG_L3CON | KA420_CFG_MULTU)) != 0)
1279			break; /* doesn't use graphics console */
1280
1281		if ((vax_confdata & KA420_CFG_VIDOPT) == 0)
1282			break; /* no color option */
1283
1284		/* Check for hardware */
1285		tmp = virtual_avail;
1286		ioaccess(tmp, vax_trunc_page(GPXADDR + GPX_ADDER_OFFSET), 1);
1287		adder = (struct adder *)tmp;
1288		adder->status = 0;
1289		status = adder->status;
1290		iounaccess(tmp, 1);
1291		if (status == offsetof(struct adder, status))
1292			return;
1293
1294		/* Check for a recognized color depth */
1295		tmp = virtual_avail;
1296		ioaccess(tmp, vax_trunc_page(GPXADDR + GPX_READBACK_OFFSET), 1);
1297		depth = *(uint16_t *)
1298		    (tmp + (GPX_READBACK_OFFSET & VAX_PGOFSET)) & 0x00f0;
1299		iounaccess(tmp, 1);
1300		if (depth != 0x00f0 && depth != 0x0080)
1301			return;
1302
1303		cndev->cn_pri = CN_INTERNAL;
1304		cndev->cn_dev =
1305		    makedev(cdevsw_lookup_major(&wsdisplay_cdevsw), 0);
1306		break;
1307
1308	default:
1309		break;
1310	}
1311}
1312
1313/*
1314 * Called very early to setup the glass tty as console.
1315 * Because it's called before the VM system is initialized, virtual memory
1316 * for the framebuffer can be stolen directly without disturbing anything.
1317 */
1318void
1319gpxcninit(struct consdev *cndev)
1320{
1321	struct gpx_screen *ss = &gpx_consscr;
1322	extern vaddr_t virtual_avail;
1323	vaddr_t ova;
1324	long defattr;
1325	struct rasops_info *ri;
1326
1327	ova = virtual_avail;
1328
1329	ioaccess(virtual_avail,
1330	    vax_trunc_page(GPXADDR + GPX_READBACK_OFFSET), 1);
1331	ss->ss_depth = (0x00f0 & *(uint16_t *)(virtual_avail +
1332	    (GPX_READBACK_OFFSET & VAX_PGOFSET))) == 0x00f0 ? 4 : 8;
1333
1334	ioaccess(virtual_avail, GPXADDR + GPX_ADDER_OFFSET, 1);
1335	ss->ss_adder = (struct adder *)virtual_avail;
1336	virtual_avail += VAX_NBPG;
1337
1338	ioaccess(virtual_avail, vax_trunc_page(GPXADDR + GPX_VDAC_OFFSET), 1);
1339	ss->ss_vdac = (void *)(virtual_avail + (GPX_VDAC_OFFSET & VAX_PGOFSET));
1340	virtual_avail += VAX_NBPG;
1341
1342#if 0
1343	ioaccess(virtual_avail, GPXADDR + GPX_CURSOR_OFFSET, 1);
1344	ss->ss_cursor = (struct dc503reg *)virtual_avail;
1345	virtual_avail += VAX_NBPG;
1346#endif
1347
1348	virtual_avail = round_page(virtual_avail);
1349
1350	/* this had better not fail */
1351	if (gpx_setup_screen(ss) != 0) {
1352#if 0
1353		iounaccess((vaddr_t)ss->ss_cursor, 1);
1354#endif
1355		iounaccess((vaddr_t)ss->ss_vdac, 1);
1356		iounaccess((vaddr_t)ss->ss_adder, 1);
1357		virtual_avail = ova;
1358		return;
1359	}
1360
1361	ri = &ss->ss_ri;
1362	ri->ri_ops.allocattr(ri, 0, 0, 0, &defattr);
1363	wsdisplay_cnattach(&gpx_stdscreen, ri, 0, 0, defattr);
1364	cn_tab->cn_pri = CN_INTERNAL;
1365
1366#if NDZKBD > 0
1367	dzkbd_cnattach(0); /* Connect keyboard and screen together */
1368#endif
1369}
1370