/* $NetBSD: gpx.c,v 1.3 2024/02/03 16:21:25 tsutsui Exp $ */ /* $OpenBSD: gpx.c,v 1.25 2014/12/23 21:39:12 miod Exp $ */ /* * Copyright (c) 2006 Miodrag Vallat. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice, this permission notice, and the disclaimer below * appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /*- * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)qd.c 7.1 (Berkeley) 6/28/91 */ /************************************************************************ * * * Copyright (c) 1985-1988 by * * Digital Equipment Corporation, Maynard, MA * * All rights reserved. * * * * This software is furnished under a license and may be used and * * copied only in accordance with the terms of such license and * * with the inclusion of the above copyright notice. This * * software or any other copies thereof may not be provided or * * otherwise made available to any other person. No title to and * * ownership of the software is hereby transferred. * * * * The information in this software is subject to change without * * notice and should not be construed as a commitment by Digital * * Equipment Corporation. * * * * Digital assumes no responsibility for the use or reliability * * of its software on equipment which is not supplied by Digital. * * * *************************************************************************/ /* * Driver for the GPX color option on VAXstation 3100, based on the * MicroVAX II qdss driver. * * The frame buffer memory itself is not directly accessible (unlike * the on-board monochrome smg frame buffer), and writes through the * Dragon chip can only happen in multiples of 16 pixels, horizontally. * * Because of this limitation, the font image is copied to offscreen * memory (which there is plenty of), and screen to screen blt operations * are done for everything. */ #include "dzkbd.h" #include "wsdisplay.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if 0 #include #include #endif #define GPXADDR 0x3c000000 /* base address on VAXstation 3100 */ #define GPX_ADDER_OFFSET 0x0000 #define GPX_VDAC_OFFSET 0x0300 #define GPX_CURSOR_OFFSET 0x0400 /* DC503 */ #define GPX_READBACK_OFFSET 0x0500 #define GPX_WIDTH 1024 #define GPX_VISHEIGHT 864 #define GPX_HEIGHT 2048 /* XXX these should be in */ /* * Brooktree Bt451, Bt457, Bt458 register definitions */ #define BT_OV0 0x00 /* overlay 0 */ #define BT_OV1 0x01 /* overlay 1 */ #define BT_OV2 0x02 /* overlay 2 */ #define BT_OV3 0x03 /* overlay 3 */ #define BT_RMR 0x04 /* read mask */ #define BT_BMR 0x05 /* blink mask */ #define BT_CR 0x06 /* control */ #define BT_CTR 0x07 /* control/test */ #define BTCR_MPLX_5 0x80 /* multiplex select, 5:1 */ #define BTCR_MPLX_4 0x00 /* multiplex select, 4:1 */ #define BTCR_RAMENA 0x40 /* use color palette RAM */ #define BTCR_BLINK_M 0x30 /* blink mask */ #define BTCR_BLINK_1648 0x00 /* 16 on, 48 off */ #define BTCR_BLINK_1616 0x10 /* 16 on, 16 off */ #define BTCR_BLINK_3232 0x20 /* 32 on, 32 off */ #define BTCR_BLINK_6464 0x30 /* 64 on, 64 off */ #define BTCR_BLINKENA_OV1 0x08 /* OV1 blink enable */ #define BTCR_BLINKENA_OV0 0x04 /* OV0 blink enable */ #define BTCR_DISPENA_OV1 0x02 /* OV1 display enable */ #define BTCR_DISPENA_OV0 0x01 /* OV0 display enable */ #define BTCTR_R_ENA 0x01 /* red channel enable */ #define BTCTR_G_ENA 0x02 /* green channel enable */ #define BTCTR_B_ENA 0x04 /* blue channel enable */ #define BTCTR_NIB_M 0x08 /* nibble mask: */ #define BTCTR_NIB_LOW 0x08 /* low */ #define BTCTR_NIB_HIGH 0x00 /* high */ /* 4 plane option RAMDAC */ struct ramdac4 { uint16_t colormap[16]; uint8_t unknown[0x20]; uint16_t cursormap[4]; uint8_t unknown2[0x18]; uint16_t control; #define RAMDAC4_INIT 0x0047 #define RAMDAC4_ENABLE 0x0002 }; /* 8 plane option RAMDAC - Bt458 or compatible */ struct ramdac8 { uint16_t address; uint16_t cmapdata; uint16_t control; uint16_t omapdata; }; struct gpx_screen { struct rasops_info ss_ri; int ss_console; u_int ss_depth; u_int ss_gpr; /* font glyphs per row */ struct adder *ss_adder; void *ss_vdac; uint8_t ss_cmap[256 * 3]; #if 0 struct dc503reg *ss_cursor; uint16_t ss_curcmd; #endif }; struct gpx_softc { device_t sc_dev; struct gpx_screen *sc_scr; int sc_nscreens; }; static int gpx_match(device_t, cfdata_t, void *); static void gpx_attach(device_t, device_t, void *); static int gpx_ioctl(void *, void *, u_long, void *, int, struct lwp *); static paddr_t gpx_mmap(void *, void *, off_t, int); static int gpx_alloc_screen(void *, const struct wsscreen_descr *, void **, int *, int *, long *); static void gpx_free_screen(void *, void *); static int gpx_show_screen(void *, void *, int, void (*) (void *, int, int), void *); static void gpx_putchar(void *, int, int, u_int, long); static void gpx_copycols(void *, int, int, int, int); static void gpx_erasecols(void *, int, int, int, long); static void gpx_copyrows(void *, int, int, int); static void gpx_eraserows(void *, int, int, long); static void gpx_do_cursor(struct rasops_info *); static int gpx_wait(struct gpx_screen *, int); static int gpx_viper_write(struct gpx_screen *, u_int, uint16_t); static void gpx_reset_viper(struct gpx_screen *); static void gpx_clear_screen(struct gpx_screen *); static int gpx_setup_screen(struct gpx_screen *); static void gpx_upload_font(struct gpx_screen *); static void gpx_copyrect(struct gpx_screen *, int, int, int, int, int, int); static void gpx_fillrect(struct gpx_screen *, int, int, int, int, long, u_int); static int gpx_getcmap(struct gpx_screen *, struct wsdisplay_cmap *); static int gpx_putcmap(struct gpx_screen *, struct wsdisplay_cmap *); static void gpx_loadcmap(struct gpx_screen *, int, int); static void gpx_resetcmap(struct gpx_screen *); /* for console */ static struct gpx_screen gpx_consscr; CFATTACH_DECL_NEW(gpx, sizeof(struct gpx_softc), gpx_match, gpx_attach, NULL, NULL); static struct wsscreen_descr gpx_stdscreen = { "std", }; static const struct wsscreen_descr *_gpx_scrlist[] = { &gpx_stdscreen, }; static const struct wsscreen_list gpx_screenlist = { sizeof(_gpx_scrlist) / sizeof(struct wsscreen_descr *), _gpx_scrlist, }; static const struct wsdisplay_accessops gpx_accessops = { .ioctl = gpx_ioctl, .mmap = gpx_mmap, .alloc_screen = gpx_alloc_screen, .free_screen = gpx_free_screen, .show_screen = gpx_show_screen, .load_font = NULL }; /* * Autoconf glue */ static int gpx_match(device_t parent, cfdata_t match, void *aux) { struct vsbus_attach_args *va = aux; volatile struct adder *adder; vaddr_t tmp; u_int depth; u_short status; switch (vax_boardtype) { default: return 0; case VAX_BTYP_410: case VAX_BTYP_420: case VAX_BTYP_43: if (va->va_paddr != GPXADDR) return 0; /* not present on microvaxes */ if ((vax_confdata & KA420_CFG_MULTU) != 0) return 0; if ((vax_confdata & KA420_CFG_VIDOPT) == 0) return 0; break; } /* Check for hardware */ adder = (volatile struct adder *) vax_map_physmem(va->va_paddr + GPX_ADDER_OFFSET, 1); if (adder == NULL) return 0; adder->status = 0; status = adder->status; vax_unmap_physmem((vaddr_t)adder, 1); if (status == offsetof(struct adder, status)) return 0; /* Check for a recognized color depth */ tmp = vax_map_physmem(va->va_paddr + GPX_READBACK_OFFSET, 1); if (tmp == 0L) return 0; depth = (*(volatile uint16_t *)tmp) & 0x00f0; vax_unmap_physmem(tmp, 1); if (depth != 0x00f0 && depth != 0x0080) return 0; /* when already running as console, always fake things */ if ((vax_confdata & KA420_CFG_L3CON) == 0 #if NWSDISPLAY > 0 && cn_tab->cn_putc == wsdisplay_cnputc #endif ) { struct vsbus_softc *sc = device_private(parent); sc->sc_mask = 0x08; scb_fake(0x44, 0x15); } else { adder = (struct adder *)vax_map_physmem(va->va_paddr + GPX_ADDER_OFFSET, 1); if (adder == NULL) return 0; adder->interrupt_enable = FRAME_SYNC; DELAY(100000); /* enough to get a retrace interrupt */ adder->interrupt_enable = 0; vax_unmap_physmem((vaddr_t)adder, 1); } return 20; } static void gpx_attach(device_t parent, device_t self, void *aux) { struct gpx_softc *sc = device_private(self); struct vsbus_attach_args *va = aux; struct gpx_screen *scr; struct wsemuldisplaydev_attach_args aa; int console; vaddr_t tmp; sc->sc_dev = self; console = #if NWSDISPLAY > 0 (vax_confdata & KA420_CFG_L3CON) == 0 && cn_tab->cn_putc == wsdisplay_cnputc; #else (vax_confdata & KA420_CFG_L3CON) == 0; #endif if (console) { scr = &gpx_consscr; sc->sc_nscreens = 1; } else { scr = kmem_zalloc(sizeof(*scr), KM_SLEEP); tmp = vax_map_physmem(va->va_paddr + GPX_READBACK_OFFSET, 1); if (tmp == 0L) { printf(": can not probe depth\n"); goto bad1; } scr->ss_depth = (*(uint16_t *)tmp & 0x00f0) == 0x00f0 ? 4 : 8; vax_unmap_physmem(tmp, 1); scr->ss_adder = (struct adder *)vax_map_physmem(va->va_paddr + GPX_ADDER_OFFSET, 1); if (scr->ss_adder == NULL) { aprint_error(": can not map frame buffer registers\n"); goto bad1; } scr->ss_vdac = (void *)vax_map_physmem(va->va_paddr + GPX_VDAC_OFFSET, 1); if (scr->ss_vdac == NULL) { aprint_error(": can not map RAMDAC\n"); goto bad2; } #if 0 scr->ss_cursor = (struct dc503reg *)vax_map_physmem(va->va_paddr + GPX_CURSOR_OFFSET, 1); if (scr->ss_cursor == NULL) { aprint_error(": can not map cursor chip\n"); goto bad3; } #endif if (gpx_setup_screen(scr) != 0) { aprint_error(": initialization failed\n"); goto bad4; } } sc->sc_scr = scr; aprint_normal("\n"); aprint_normal_dev(self, "%dx%d %d plane color framebuffer\n", GPX_WIDTH, GPX_VISHEIGHT, scr->ss_depth); aa.console = console; aa.scrdata = &gpx_screenlist; aa.accessops = &gpx_accessops; aa.accesscookie = sc; config_found(self, &aa, wsemuldisplaydevprint, CFARGS_NONE); return; bad4: #if 0 vax_unmap_physmem((vaddr_t)scr->ss_cursor, 1); bad3: #endif vax_unmap_physmem((vaddr_t)scr->ss_vdac, 1); bad2: vax_unmap_physmem((vaddr_t)scr->ss_adder, 1); bad1: kmem_free(scr, sizeof(*scr)); } /* * wsdisplay accessops */ static int gpx_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) { struct gpx_softc *sc = v; struct gpx_screen *ss = sc->sc_scr; struct wsdisplay_fbinfo *wdf; struct wsdisplay_cmap *cm; int error; switch (cmd) { case WSDISPLAYIO_GTYPE: *(u_int *)data = WSDISPLAY_TYPE_GPX; break; case WSDISPLAYIO_GINFO: wdf = (struct wsdisplay_fbinfo *)data; wdf->height = ss->ss_ri.ri_height; wdf->width = ss->ss_ri.ri_width; wdf->depth = ss->ss_depth; wdf->cmsize = 1 << ss->ss_depth; break; case WSDISPLAYIO_GETCMAP: cm = (struct wsdisplay_cmap *)data; error = gpx_getcmap(ss, cm); if (error != 0) return error; break; case WSDISPLAYIO_PUTCMAP: cm = (struct wsdisplay_cmap *)data; error = gpx_putcmap(ss, cm); if (error != 0) return error; gpx_loadcmap(ss, cm->index, cm->count); break; case WSDISPLAYIO_GVIDEO: case WSDISPLAYIO_SVIDEO: break; case WSDISPLAYIO_LINEBYTES: /* no linear mapping */ return -1; default: return EPASSTHROUGH; } return 0; } static paddr_t gpx_mmap(void *v, void *vs, off_t offset, int prot) { return -1; } static int gpx_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep, int *curxp, int *curyp, long *defattrp) { struct gpx_softc *sc = v; struct gpx_screen *ss = sc->sc_scr; struct rasops_info *ri = &ss->ss_ri; if (sc->sc_nscreens > 0) return ENOMEM; *cookiep = ri; *curxp = *curyp = 0; ri->ri_ops.allocattr(ri, 0, 0, 0, defattrp); sc->sc_nscreens++; return 0; } static void gpx_free_screen(void *v, void *cookie) { struct gpx_softc *sc = v; sc->sc_nscreens--; } static int gpx_show_screen(void *v, void *cookie, int waitok, void (*cb)(void *, int, int), void *cbarg) { return 0; } /* * wsdisplay emulops */ static void gpx_putchar(void *v, int row, int col, u_int uc, long attr) { struct rasops_info *ri = v; struct gpx_screen *ss = ri->ri_hw; struct wsdisplay_font *font = ri->ri_font; int dx, dy, sx, sy, fg, bg, ul; rasops_unpack_attr(attr, &fg, &bg, &ul); /* find where to output the glyph... */ dx = col * font->fontwidth + ri->ri_xorigin; dy = row * font->fontheight + ri->ri_yorigin; /* ... and where to pick it from */ uc -= font->firstchar; sx = (uc % ss->ss_gpr) * font->stride * NBBY; sy = GPX_HEIGHT - (1 + uc / ss->ss_gpr) * font->fontheight; /* setup VIPER operand control registers */ while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff)) continue; gpx_viper_write(ss, SRC1_OCR_B, EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY); gpx_viper_write(ss, DST_OCR_B, EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY); gpx_viper_write(ss, MASK_1, 0xffff); gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, fg); gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, bg); ss->ss_adder->x_clip_min = 0; ss->ss_adder->x_clip_max = GPX_WIDTH; ss->ss_adder->y_clip_min = 0; ss->ss_adder->y_clip_max = GPX_VISHEIGHT; /* load DESTINATION origin and vectors */ ss->ss_adder->fast_dest_dy = 0; ss->ss_adder->slow_dest_dx = 0; ss->ss_adder->error_1 = 0; ss->ss_adder->error_2 = 0; ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL; gpx_wait(ss, RASTEROP_COMPLETE); ss->ss_adder->destination_x = dx; ss->ss_adder->fast_dest_dx = font->fontwidth; ss->ss_adder->destination_y = dy; ss->ss_adder->slow_dest_dy = font->fontheight; /* load SOURCE origin and vectors */ ss->ss_adder->source_1_x = sx; ss->ss_adder->source_1_y = sy; ss->ss_adder->source_1_dx = font->fontwidth; ss->ss_adder->source_1_dy = font->fontheight; ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | LF_R1; if (ul != 0) { gpx_fillrect(ss, dx, dy + font->fontheight - 2, font->fontwidth, 1, attr, LF_R3); /* fg fill */ } } static void gpx_copycols(void *v, int row, int src, int dst, int cnt) { struct rasops_info *ri = v; struct gpx_screen *ss = ri->ri_hw; struct wsdisplay_font *font = ri->ri_font; int sx, y, dx, w, h; sx = ri->ri_xorigin + src * font->fontwidth; dx = ri->ri_xorigin + dst * font->fontwidth; w = cnt * font->fontwidth; y = ri->ri_yorigin + row * font->fontheight; h = font->fontheight; gpx_copyrect(ss, sx, y, dx, y, w, h); } static void gpx_erasecols(void *v, int row, int col, int cnt, long attr) { struct rasops_info *ri = v; struct gpx_screen *ss = ri->ri_hw; struct wsdisplay_font *font = ri->ri_font; int x, y, dx, dy; x = ri->ri_xorigin + col * font->fontwidth; dx = cnt * font->fontwidth; y = ri->ri_yorigin + row * font->fontheight; dy = font->fontheight; gpx_fillrect(ss, x, y, dx, dy, attr, LF_R2); /* bg fill */ } static void gpx_copyrows(void *v, int src, int dst, int cnt) { struct rasops_info *ri = v; struct gpx_screen *ss = ri->ri_hw; struct wsdisplay_font *font = ri->ri_font; int x, sy, dy, w, h; x = ri->ri_xorigin; w = ri->ri_emustride; sy = ri->ri_yorigin + src * font->fontheight; dy = ri->ri_yorigin + dst * font->fontheight; h = cnt * font->fontheight; gpx_copyrect(ss, x, sy, x, dy, w, h); } static void gpx_eraserows(void *v, int row, int cnt, long attr) { struct rasops_info *ri = v; struct gpx_screen *ss = ri->ri_hw; struct wsdisplay_font *font = ri->ri_font; int x, y, dx, dy; x = ri->ri_xorigin; dx = ri->ri_emustride; y = ri->ri_yorigin + row * font->fontheight; dy = cnt * font->fontheight; gpx_fillrect(ss, x, y, dx, dy, attr, LF_R2); /* bg fill */ } static void gpx_do_cursor(struct rasops_info *ri) { struct gpx_screen *ss = ri->ri_hw; int x, y, w, h; x = ri->ri_ccol * ri->ri_font->fontwidth + ri->ri_xorigin; y = ri->ri_crow * ri->ri_font->fontheight + ri->ri_yorigin; w = ri->ri_font->fontwidth; h = ri->ri_font->fontheight; gpx_fillrect(ss, x, y, w, h, WSCOL_WHITE << 24, LF_R4); /* invert */ } /* * low-level programming routines */ static int gpx_wait(struct gpx_screen *ss, int bits) { int i; ss->ss_adder->status = 0; for (i = 100000; i != 0; i--) { if ((ss->ss_adder->status & bits) == bits) break; DELAY(1); } return i == 0; } static int gpx_viper_write(struct gpx_screen *ss, u_int reg, uint16_t val) { if (gpx_wait(ss, ADDRESS_COMPLETE) == 0 && gpx_wait(ss, TX_READY) == 0) { ss->ss_adder->id_data = val; ss->ss_adder->command = ID_LOAD | reg; return 0; } #ifdef DEBUG if (ss->ss_console == 0) /* don't make things worse! */ printf("gpx_viper_write failure, reg %x val %x\n", reg, val); #endif return 1; } /* Initialize the damned beast. Straight from qdss. */ static void gpx_reset_viper(struct gpx_screen *ss) { int i; ss->ss_adder->interrupt_enable = 0; ss->ss_adder->command = CANCEL; /* set monitor timing */ ss->ss_adder->x_scan_count_0 = 0x2800; ss->ss_adder->x_scan_count_1 = 0x1020; ss->ss_adder->x_scan_count_2 = 0x003a; ss->ss_adder->x_scan_count_3 = 0x38f0; ss->ss_adder->x_scan_count_4 = 0x6128; ss->ss_adder->x_scan_count_5 = 0x093a; ss->ss_adder->x_scan_count_6 = 0x313c; ss->ss_adder->sync_phase_adj = 0x0100; ss->ss_adder->x_scan_conf = 0x00c8; /* * got a bug in second pass ADDER! lets take care of it... * * normally, just use the code in the following bug fix code, but to * make repeated demos look pretty, load the registers as if there was * no bug and then test to see if we are getting sync */ ss->ss_adder->y_scan_count_0 = 0x135f; ss->ss_adder->y_scan_count_1 = 0x3363; ss->ss_adder->y_scan_count_2 = 0x2366; ss->ss_adder->y_scan_count_3 = 0x0388; /* * if no sync, do the bug fix code */ if (gpx_wait(ss, FRAME_SYNC) != 0) { /* * First load all Y scan registers with very short frame and * wait for scroll service. This guarantees at least one SYNC * to fix the pass 2 Adder initialization bug (synchronizes * XCINCH with DMSEEDH) */ ss->ss_adder->y_scan_count_0 = 0x01; ss->ss_adder->y_scan_count_1 = 0x01; ss->ss_adder->y_scan_count_2 = 0x01; ss->ss_adder->y_scan_count_3 = 0x01; /* delay at least 1 full frame time */ gpx_wait(ss, FRAME_SYNC); gpx_wait(ss, FRAME_SYNC); /* * now load the REAL sync values (in reverse order just to * be safe). */ ss->ss_adder->y_scan_count_3 = 0x0388; ss->ss_adder->y_scan_count_2 = 0x2366; ss->ss_adder->y_scan_count_1 = 0x3363; ss->ss_adder->y_scan_count_0 = 0x135f; } /* zero the index registers */ ss->ss_adder->x_index_pending = 0; ss->ss_adder->y_index_pending = 0; ss->ss_adder->x_index_new = 0; ss->ss_adder->y_index_new = 0; ss->ss_adder->x_index_old = 0; ss->ss_adder->y_index_old = 0; ss->ss_adder->pause = 0; /* set rasterop mode to normal pen down */ ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | DST_INDEX_ENABLE | NORMAL; /* set the rasterop registers to default values */ ss->ss_adder->source_1_dx = 1; ss->ss_adder->source_1_dy = 1; ss->ss_adder->source_1_x = 0; ss->ss_adder->source_1_y = 0; ss->ss_adder->destination_x = 0; ss->ss_adder->destination_y = 0; ss->ss_adder->fast_dest_dx = 1; ss->ss_adder->fast_dest_dy = 0; ss->ss_adder->slow_dest_dx = 0; ss->ss_adder->slow_dest_dy = 1; ss->ss_adder->error_1 = 0; ss->ss_adder->error_2 = 0; /* scale factor = UNITY */ ss->ss_adder->fast_scale = UNITY; ss->ss_adder->slow_scale = UNITY; /* set the source 2 parameters */ ss->ss_adder->source_2_x = 0; ss->ss_adder->source_2_y = 0; ss->ss_adder->source_2_size = 0x0022; /* initialize plane addresses for eight vipers */ for (i = 0; i < 8; i++) { gpx_viper_write(ss, CS_UPDATE_MASK, 1 << i); gpx_viper_write(ss, PLANE_ADDRESS, i); } /* initialize the external registers. */ gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff); gpx_viper_write(ss, CS_SCROLL_MASK, 0x00ff); /* initialize resolution mode */ gpx_viper_write(ss, MEMORY_BUS_WIDTH, 0x000c); /* bus width = 16 */ gpx_viper_write(ss, RESOLUTION_MODE, 0x0000); /* one bit/pixel */ /* initialize viper registers */ gpx_viper_write(ss, SCROLL_CONSTANT, SCROLL_ENABLE | VIPER_LEFT | VIPER_UP); gpx_viper_write(ss, SCROLL_FILL, 0x0000); /* set clipping and scrolling limits to full screen */ gpx_wait(ss, ADDRESS_COMPLETE); ss->ss_adder->x_clip_min = 0; ss->ss_adder->x_clip_max = GPX_WIDTH; ss->ss_adder->y_clip_min = 0; ss->ss_adder->y_clip_max = GPX_HEIGHT; ss->ss_adder->scroll_x_min = 0; ss->ss_adder->scroll_x_max = GPX_WIDTH; ss->ss_adder->scroll_y_min = 0; ss->ss_adder->scroll_y_max = GPX_HEIGHT; gpx_wait(ss, FRAME_SYNC); /* wait at LEAST 1 full frame */ gpx_wait(ss, FRAME_SYNC); ss->ss_adder->x_index_pending = 0; ss->ss_adder->y_index_pending = 0; ss->ss_adder->x_index_new = 0; ss->ss_adder->y_index_new = 0; ss->ss_adder->x_index_old = 0; ss->ss_adder->y_index_old = 0; gpx_wait(ss, ADDRESS_COMPLETE); gpx_viper_write(ss, LEFT_SCROLL_MASK, 0x0000); gpx_viper_write(ss, RIGHT_SCROLL_MASK, 0x0000); /* set source and the mask register to all ones */ gpx_viper_write(ss, SOURCE, 0xffff); gpx_viper_write(ss, MASK_1, 0xffff); gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255); gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0); /* initialize Operand Control Register banks for fill command */ gpx_viper_write(ss, SRC1_OCR_A, EXT_NONE | INT_M1_M2 | NO_ID | WAIT); gpx_viper_write(ss, SRC2_OCR_A, EXT_NONE | INT_SOURCE | NO_ID | NO_WAIT); gpx_viper_write(ss, DST_OCR_A, EXT_NONE | INT_NONE | NO_ID | NO_WAIT); gpx_viper_write(ss, SRC1_OCR_B, EXT_NONE | INT_SOURCE | NO_ID | WAIT); gpx_viper_write(ss, SRC2_OCR_B, EXT_NONE | INT_M1_M2 | NO_ID | NO_WAIT); gpx_viper_write(ss, DST_OCR_B, EXT_NONE | INT_NONE | NO_ID | NO_WAIT); /* * Init Logic Unit Function registers. */ /* putchar */ gpx_viper_write(ss, LU_FUNCTION_R1, FULL_SRC_RESOLUTION | LF_SOURCE); /* erase{cols,rows} */ gpx_viper_write(ss, LU_FUNCTION_R2, FULL_SRC_RESOLUTION | LF_ZEROS); /* underline */ gpx_viper_write(ss, LU_FUNCTION_R3, FULL_SRC_RESOLUTION | LF_ONES); /* cursor */ gpx_viper_write(ss, LU_FUNCTION_R4, FULL_SRC_RESOLUTION | LF_NOT_D); } /* Clear the whole screen. Straight from qdss. */ static void gpx_clear_screen(struct gpx_screen *ss) { ss->ss_adder->x_limit = GPX_WIDTH; ss->ss_adder->y_limit = GPX_HEIGHT; ss->ss_adder->y_offset_pending = 0; gpx_wait(ss, FRAME_SYNC); /* wait at LEAST 1 full frame */ gpx_wait(ss, FRAME_SYNC); ss->ss_adder->y_scroll_constant = SCROLL_ERASE; gpx_wait(ss, FRAME_SYNC); gpx_wait(ss, FRAME_SYNC); ss->ss_adder->y_offset_pending = GPX_VISHEIGHT; gpx_wait(ss, FRAME_SYNC); gpx_wait(ss, FRAME_SYNC); ss->ss_adder->y_scroll_constant = SCROLL_ERASE; gpx_wait(ss, FRAME_SYNC); gpx_wait(ss, FRAME_SYNC); ss->ss_adder->y_offset_pending = 2 * GPX_VISHEIGHT; gpx_wait(ss, FRAME_SYNC); gpx_wait(ss, FRAME_SYNC); ss->ss_adder->y_scroll_constant = SCROLL_ERASE; gpx_wait(ss, FRAME_SYNC); gpx_wait(ss, FRAME_SYNC); ss->ss_adder->y_offset_pending = 0; /* back to normal */ gpx_wait(ss, FRAME_SYNC); gpx_wait(ss, FRAME_SYNC); ss->ss_adder->x_limit = GPX_WIDTH; ss->ss_adder->y_limit = GPX_VISHEIGHT; } static int gpx_setup_screen(struct gpx_screen *ss) { struct rasops_info *ri = &ss->ss_ri; int cookie; memset(ri, 0, sizeof(*ri)); ri->ri_depth = 8; /* masquerade as a 8 bit device for rasops */ ri->ri_width = GPX_WIDTH; ri->ri_height = GPX_VISHEIGHT; ri->ri_stride = GPX_WIDTH; ri->ri_flg = RI_CENTER; /* no RI_CLEAR as ri_bits is NULL! */ ri->ri_hw = ss; if (ss == &gpx_consscr) ri->ri_flg |= RI_NO_AUTO; /* * We can not let rasops select our font, because we need to use * a font with right-to-left bit order on this frame buffer. */ wsfont_init(); cookie = wsfont_find(NULL, 12, 0, 0, WSDISPLAY_FONTORDER_R2L, WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP); if (cookie < 0) cookie = wsfont_find(NULL, 8, 0, 0, WSDISPLAY_FONTORDER_R2L, WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP); if (cookie < 0) cookie = wsfont_find(NULL, 0, 0, 0, WSDISPLAY_FONTORDER_R2L, WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP); if (cookie < 0) return -1; if (wsfont_lock(cookie, &ri->ri_font) != 0) return -1; ri->ri_wsfcookie = cookie; /* * Ask for an unholy big display, rasops will trim this to more * reasonable values. */ if (rasops_init(ri, 160, 160) != 0) return -1; /* * Override the rasops emulops. */ ri->ri_ops.copyrows = gpx_copyrows; ri->ri_ops.copycols = gpx_copycols; ri->ri_ops.eraserows = gpx_eraserows; ri->ri_ops.erasecols = gpx_erasecols; ri->ri_ops.putchar = gpx_putchar; ri->ri_do_cursor = gpx_do_cursor; gpx_stdscreen.ncols = ri->ri_cols; gpx_stdscreen.nrows = ri->ri_rows; gpx_stdscreen.textops = &ri->ri_ops; gpx_stdscreen.fontwidth = ri->ri_font->fontwidth; gpx_stdscreen.fontheight = ri->ri_font->fontheight; gpx_stdscreen.capabilities = ri->ri_caps; /* * Initialize RAMDAC. */ if (ss->ss_depth == 8) { struct ramdac8 *rd = ss->ss_vdac; rd->address = BT_CR; rd->control = BTCR_RAMENA | BTCR_BLINK_1648 | BTCR_MPLX_4; } else { struct ramdac4 *rd = ss->ss_vdac; rd->control = RAMDAC4_INIT; } /* * Put the ADDER and VIPER in a good state. */ gpx_reset_viper(ss); /* * Initialize colormap. */ gpx_resetcmap(ss); /* * Clear display (including non-visible area), in 864 lines chunks. */ gpx_clear_screen(ss); /* * Copy our font to the offscreen area. */ gpx_upload_font(ss); #if 0 ss->ss_cursor->cmdr = ss->ss_curcmd = PCCCMD_HSHI; #endif return 0; } /* * Copy the selected wsfont to non-visible frame buffer area. * This is necessary since the only way to send data to the frame buffer * is through the ID interface, which is slow and needs 16 bit wide data. * Adapted from qdss. */ static void gpx_upload_font(struct gpx_screen *ss) { struct rasops_info *ri = &ss->ss_ri; struct wsdisplay_font *font = ri->ri_font; uint8_t *fontbits, *fb; u_int remaining, nchars, row; u_int i, j; uint16_t data; /* setup VIPER operand control registers */ gpx_viper_write(ss, MASK_1, 0xffff); gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255); gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0); gpx_viper_write(ss, SRC1_OCR_B, EXT_NONE | INT_NONE | ID | BAR_SHIFT_DELAY); gpx_viper_write(ss, SRC2_OCR_B, EXT_NONE | INT_NONE | ID | BAR_SHIFT_DELAY); gpx_viper_write(ss, DST_OCR_B, EXT_SOURCE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY); ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | DST_INDEX_ENABLE | NORMAL; gpx_wait(ss, RASTEROP_COMPLETE); /* * Load font data. The font is uploaded in 8 or 16 bit wide cells, on * as many ``lines'' as necessary at the end of the display. */ ss->ss_gpr = MIN(GPX_WIDTH / (NBBY * font->stride), font->numchars); if ((ss->ss_gpr & 1) != 0) ss->ss_gpr--; fontbits = font->data; for (row = 1, remaining = font->numchars; remaining != 0; row++, remaining -= nchars) { nchars = MIN(ss->ss_gpr, remaining); ss->ss_adder->destination_x = 0; ss->ss_adder->destination_y = GPX_HEIGHT - row * font->fontheight; ss->ss_adder->fast_dest_dx = nchars * 16; ss->ss_adder->slow_dest_dy = font->fontheight; /* setup for processor to bitmap xfer */ gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff); ss->ss_adder->cmd = PBT | OCRB | DTE | LF_R1 | 2; /*XXX why 2?*/ /* iteratively do the processor to bitmap xfer */ for (i = font->fontheight; i != 0; i--) { fb = fontbits; fontbits += font->stride; /* PTOB a scan line */ for (j = nchars; j != 0; j--) { /* PTOB one scan of a char cell */ if (font->stride == 1) { data = *fb; fb += font->fontheight; /* * Do not access past font memory if * it has an odd number of characters * and this is the last pair. */ if (j != 1 || (nchars & 1) == 0 || remaining != nchars) { data |= ((uint16_t)*fb) << 8; fb += font->fontheight; } } else { data = fb[0] | (((uint16_t)fb[1]) << 8); fb += font->fontheight * font->stride; } gpx_wait(ss, TX_READY); ss->ss_adder->id_data = data; } } fontbits += (nchars - 1) * font->stride * font->fontheight; } } static void gpx_copyrect(struct gpx_screen *ss, int sx, int sy, int dx, int dy, int w, int h) { while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff)) continue; gpx_viper_write(ss, MASK_1, 0xffff); gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255); gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0); gpx_viper_write(ss, SRC1_OCR_B, EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY); gpx_viper_write(ss, DST_OCR_B, EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY); ss->ss_adder->fast_dest_dy = 0; ss->ss_adder->slow_dest_dx = 0; ss->ss_adder->error_1 = 0; ss->ss_adder->error_2 = 0; ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL; gpx_wait(ss, RASTEROP_COMPLETE); ss->ss_adder->destination_x = dx; ss->ss_adder->fast_dest_dx = w; ss->ss_adder->destination_y = dy; ss->ss_adder->slow_dest_dy = h; ss->ss_adder->source_1_x = sx; ss->ss_adder->source_1_dx = w; ss->ss_adder->source_1_y = sy; ss->ss_adder->source_1_dy = h; ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | LF_R1; } /* * Fill a rectangle with the given attribute and function (i.e. rop). */ static void gpx_fillrect(struct gpx_screen *ss, int x, int y, int dx, int dy, long attr, u_int function) { int fg, bg; rasops_unpack_attr(attr, &fg, &bg, NULL); while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff)) continue; gpx_viper_write(ss, MASK_1, 0xffff); gpx_viper_write(ss, SOURCE, 0xffff); gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, fg); gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, bg); gpx_viper_write(ss, SRC1_OCR_B, EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY); gpx_viper_write(ss, DST_OCR_B, EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY); ss->ss_adder->fast_dest_dx = 0; ss->ss_adder->fast_dest_dy = 0; ss->ss_adder->slow_dest_dx = 0; ss->ss_adder->error_1 = 0; ss->ss_adder->error_2 = 0; ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL; gpx_wait(ss, RASTEROP_COMPLETE); ss->ss_adder->destination_x = x; ss->ss_adder->fast_dest_dx = dx; ss->ss_adder->destination_y = y; ss->ss_adder->slow_dest_dy = dy; ss->ss_adder->source_1_x = x; ss->ss_adder->source_1_dx = dx; ss->ss_adder->source_1_y = y; ss->ss_adder->source_1_dy = dy; ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | function; } /* * Colormap handling routines */ static int gpx_getcmap(struct gpx_screen *ss, struct wsdisplay_cmap *cm) { u_int index = cm->index, count = cm->count, i; u_int colcount = 1 << ss->ss_depth; int error; uint8_t ramp[256], *c, *r; if (index >= colcount || count > colcount - index) return EINVAL; /* extract reds */ c = ss->ss_cmap + 0 + index * 3; for (i = count, r = ramp; i != 0; i--) *r++ = *c << (8 - ss->ss_depth), c += 3; if ((error = copyout(ramp, cm->red, count)) != 0) return error; /* extract greens */ c = ss->ss_cmap + 1 + index * 3; for (i = count, r = ramp; i != 0; i--) *r++ = *c << (8 - ss->ss_depth), c += 3; if ((error = copyout(ramp, cm->green, count)) != 0) return error; /* extract blues */ c = ss->ss_cmap + 2 + index * 3; for (i = count, r = ramp; i != 0; i--) *r++ = *c << (8 - ss->ss_depth), c += 3; if ((error = copyout(ramp, cm->blue, count)) != 0) return error; return 0; } static int gpx_putcmap(struct gpx_screen *ss, struct wsdisplay_cmap *cm) { u_int index = cm->index, count = cm->count; u_int colcount = 1 << ss->ss_depth; int i, error; uint8_t r[256], g[256], b[256], *nr, *ng, *nb, *c; if (index >= colcount || count > colcount - index) return EINVAL; if ((error = copyin(cm->red, r, count)) != 0) return error; if ((error = copyin(cm->green, g, count)) != 0) return error; if ((error = copyin(cm->blue, b, count)) != 0) return error; nr = r, ng = g, nb = b; c = ss->ss_cmap + index * 3; for (i = count; i != 0; i--) { *c++ = *nr++ >> (8 - ss->ss_depth); *c++ = *ng++ >> (8 - ss->ss_depth); *c++ = *nb++ >> (8 - ss->ss_depth); } return 0; } static void gpx_loadcmap(struct gpx_screen *ss, int from, int count) { uint8_t *cmap = ss->ss_cmap; int i, color12; gpx_wait(ss, FRAME_SYNC); if (ss->ss_depth == 8) { struct ramdac8 *rd = ss->ss_vdac; cmap += from * 3; rd->address = from; for (i = 0; i < count * 3; i++) rd->cmapdata = *cmap++; } else { struct ramdac4 *rd = ss->ss_vdac; cmap = ss->ss_cmap + from; for (i = from; i < from + count; i++) { color12 = (*cmap++ >> 4) << 0; color12 |= (*cmap++ >> 4) << 8; color12 |= (*cmap++ >> 4) << 4; rd->colormap[i] = color12; } } } static void gpx_resetcmap(struct gpx_screen *ss) { if (ss->ss_depth == 8) memcpy(ss->ss_cmap, rasops_cmap, sizeof(ss->ss_cmap)); else { memcpy(ss->ss_cmap, rasops_cmap, 8 * 3); memcpy(ss->ss_cmap + 8 * 3, rasops_cmap + 0xf8 * 3, 8 * 3); } gpx_loadcmap(ss, 0, 1 << ss->ss_depth); /* * On the 4bit RAMDAC, make the hardware cursor black on black */ if (ss->ss_depth != 8) { struct ramdac4 *rd = ss->ss_vdac; rd->cursormap[0] = rd->cursormap[1] = rd->cursormap[2] = rd->cursormap[3] = 0x0000; } } /* * Console support code */ cons_decl(gpx); /* * Called very early to setup the glass tty as console. * Because it's called before the VM system is initialized, virtual memory * for the framebuffer can be stolen directly without disturbing anything. */ void gpxcnprobe(struct consdev *cndev) { extern vaddr_t virtual_avail; extern const struct cdevsw wsdisplay_cdevsw; volatile struct adder *adder; vaddr_t tmp; int depth; u_short status; switch (vax_boardtype) { case VAX_BTYP_410: case VAX_BTYP_420: case VAX_BTYP_43: if ((vax_confdata & (KA420_CFG_L3CON | KA420_CFG_MULTU)) != 0) break; /* doesn't use graphics console */ if ((vax_confdata & KA420_CFG_VIDOPT) == 0) break; /* no color option */ /* Check for hardware */ tmp = virtual_avail; ioaccess(tmp, vax_trunc_page(GPXADDR + GPX_ADDER_OFFSET), 1); adder = (struct adder *)tmp; adder->status = 0; status = adder->status; iounaccess(tmp, 1); if (status == offsetof(struct adder, status)) return; /* Check for a recognized color depth */ tmp = virtual_avail; ioaccess(tmp, vax_trunc_page(GPXADDR + GPX_READBACK_OFFSET), 1); depth = *(uint16_t *) (tmp + (GPX_READBACK_OFFSET & VAX_PGOFSET)) & 0x00f0; iounaccess(tmp, 1); if (depth != 0x00f0 && depth != 0x0080) return; cndev->cn_pri = CN_INTERNAL; cndev->cn_dev = makedev(cdevsw_lookup_major(&wsdisplay_cdevsw), 0); break; default: break; } } /* * Called very early to setup the glass tty as console. * Because it's called before the VM system is initialized, virtual memory * for the framebuffer can be stolen directly without disturbing anything. */ void gpxcninit(struct consdev *cndev) { struct gpx_screen *ss = &gpx_consscr; extern vaddr_t virtual_avail; vaddr_t ova; long defattr; struct rasops_info *ri; ova = virtual_avail; ioaccess(virtual_avail, vax_trunc_page(GPXADDR + GPX_READBACK_OFFSET), 1); ss->ss_depth = (0x00f0 & *(uint16_t *)(virtual_avail + (GPX_READBACK_OFFSET & VAX_PGOFSET))) == 0x00f0 ? 4 : 8; ioaccess(virtual_avail, GPXADDR + GPX_ADDER_OFFSET, 1); ss->ss_adder = (struct adder *)virtual_avail; virtual_avail += VAX_NBPG; ioaccess(virtual_avail, vax_trunc_page(GPXADDR + GPX_VDAC_OFFSET), 1); ss->ss_vdac = (void *)(virtual_avail + (GPX_VDAC_OFFSET & VAX_PGOFSET)); virtual_avail += VAX_NBPG; #if 0 ioaccess(virtual_avail, GPXADDR + GPX_CURSOR_OFFSET, 1); ss->ss_cursor = (struct dc503reg *)virtual_avail; virtual_avail += VAX_NBPG; #endif virtual_avail = round_page(virtual_avail); /* this had better not fail */ if (gpx_setup_screen(ss) != 0) { #if 0 iounaccess((vaddr_t)ss->ss_cursor, 1); #endif iounaccess((vaddr_t)ss->ss_vdac, 1); iounaccess((vaddr_t)ss->ss_adder, 1); virtual_avail = ova; return; } ri = &ss->ss_ri; ri->ri_ops.allocattr(ri, 0, 0, 0, &defattr); wsdisplay_cnattach(&gpx_stdscreen, ri, 0, 0, defattr); cn_tab->cn_pri = CN_INTERNAL; #if NDZKBD > 0 dzkbd_cnattach(0); /* Connect keyboard and screen together */ #endif }