/* $OpenBSD: tcx.c,v 1.47 2013/10/20 20:07:26 miod Exp $ */ /* $NetBSD: tcx.c,v 1.8 1997/07/29 09:58:14 fair Exp $ */ /* * Copyright (c) 2002, 2003, 2008 Miodrag Vallat. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. * * * Copyright (c) 1996 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Paul Kranenburg. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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. */ /* * Color display (TCX) driver. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for prom console hook */ /* per-display variables */ struct tcx_softc { struct sunfb sc_sunfb; /* common base part */ struct rom_reg sc_phys[2]; /* copy of prom ranges for mmap */ volatile struct bt_regs *sc_bt; /* Brooktree registers */ volatile struct tcx_thc *sc_thc;/* THC registers */ volatile u_int8_t *sc_dfb8; /* 8 bit plane */ paddr_t sc_cplane; /* S24 control plane PA */ union bt_cmap sc_cmap; /* Brooktree color map */ struct intrhand sc_ih; int sc_isconsole; /* acceleration parts */ paddr_t sc_stipple; /* Stipple space PA */ paddr_t sc_blit; /* Blitter space PA */ int sc_blit_width; /* maximal blith width */ int (*sc_plain_copycols)(void *, int, int, int, int); }; void tcx_accel_init(struct tcx_softc *, struct confargs *); void tcx_accel_plug(struct tcx_softc *, struct confargs *); void tcx_blit(struct tcx_softc *, uint32_t, uint32_t, int); void tcx_burner(void *, u_int, u_int); int tcx_copyrows(void *, int, int, int); int tcx_copycols(void *, int, int, int, int); int tcx_do_cursor(struct rasops_info *); int tcx_erasecols(void *, int, int, int, long); int tcx_eraserows(void *, int, int, long); int tcx_intr(void *); int tcx_ioctl(void *, u_long, caddr_t, int, struct proc *); static __inline__ void tcx_loadcmap_deferred(struct tcx_softc *, u_int, u_int); paddr_t tcx_mmap(void *, off_t, int); void tcx_prom(struct tcx_softc *); int tcx_putchar(void *, int, int, u_int, long); void tcx_reset(struct tcx_softc *, int); void tcx_s24_reset(struct tcx_softc *, int); void tcx_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t); void tcx_stipple(struct tcx_softc *, int, int, int, int, int); struct wsdisplay_accessops tcx_accessops = { .ioctl = tcx_ioctl, .mmap = tcx_mmap, .burn_screen = tcx_burner }; int tcxmatch(struct device *, void *, void *); void tcxattach(struct device *, struct device *, void *); int tcxactivate(struct device *, int); const struct cfattach tcx_ca = { sizeof(struct tcx_softc), tcxmatch, tcxattach, NULL, tcxactivate }; struct cfdriver tcx_cd = { NULL, "tcx", DV_DULL }; /* * There are three ways to access the framebuffer memory of the S24: * - 26 bits per pixel, in 32-bit words; the low-order 24 bits are blue, * green and red values, and the other two bits select the display modes, * per pixel. * This is the view we'll use to initialize the frame buffer. * - 24 bits per pixel, in 32-bit words; the high-order byte reads as zero, * and is ignored on writes (so the mode bits can not be altered). * This is the view available via mmap, for the X server. * - 8 bits per pixel, unpadded; writes to this space do not modify the * other 18 bits, which are hidden. * This is the view used for the console emulation mode, as well as for * the X server on 8-bit only devices. * * The entry-level tcx found on the SPARCstation 4 can only provide the 8-bit * mode. * * Both flavours can be told out by the `tcx-8-bit' property; also, on * 8-bit tcx, the 24 bit color regions have a size of zero (or one with * the most recent PROM versions). */ #define TCX_CTL_8_MAPPED 0x00000000 /* 8 bits, uses colormap */ #define TCX_CTL_24_MAPPED 0x01000000 /* 24 bits, uses colormap */ #define TCX_CTL_24_LEVEL 0x03000000 /* 24 bits, direct color */ #define TCX_CTL_PIXELMASK 0x00ffffff /* mask for index/level */ int tcxmatch(struct device *parent, void *vcf, void *aux) { struct confargs *ca = aux; struct romaux *ra = &ca->ca_ra; if (strcmp(ra->ra_name, "SUNW,tcx") != 0) return (0); return (1); } void tcxattach(struct device *parent, struct device *self, void *args) { struct tcx_softc *sc = (struct tcx_softc *)self; struct confargs *ca = args; int node, pri; char *nam = NULL; vaddr_t thc_offset; pri = ca->ca_ra.ra_intr[0].int_pri; printf(" pri %d: ", pri); node = ca->ca_ra.ra_node; sc->sc_isconsole = node == fbnode; if (ca->ca_ra.ra_nreg < TCX_NREG) { printf("expected %d registers, got %d\n", TCX_NREG, ca->ca_ra.ra_nreg); return; } nam = getpropstring(node, "model"); if (*nam != '\0') printf("%s, ", nam); /* * Copy the register address spaces needed for mmap operation. */ sc->sc_phys[0] = ca->ca_ra.ra_reg[TCX_REG_DFB8]; sc->sc_phys[1] = ca->ca_ra.ra_reg[TCX_REG_DFB24]; /* * Can't trust the PROM range len here, it is only 4 bytes on the * 8-bit model. Not that it matters much anyway since we map in * pages. */ sc->sc_bt = (volatile struct bt_regs *) mapiodev(&ca->ca_ra.ra_reg[TCX_REG_CMAP], 0, sizeof *sc->sc_bt); /* * For some reason S24 PROM sets up TEC and THC ranges at the * right addresses (701000 and 301000), while 8 bit TCX doesn't * (and uses 70000 and 30000) - be sure to only compensate on 8 bit * models. */ if (((vaddr_t)ca->ca_ra.ra_reg[TCX_REG_THC].rr_paddr & 0x1000) != 0) thc_offset = 0; else thc_offset = 0x1000; sc->sc_thc = (volatile struct tcx_thc *) mapiodev(&ca->ca_ra.ra_reg[TCX_REG_THC], thc_offset, sizeof *sc->sc_thc); /* * Find out frame buffer geometry, so that we know how much * memory to map. */ fb_setsize(&sc->sc_sunfb, 8, 1152, 900, node, ca->ca_bustype); sc->sc_dfb8 = mapiodev(&ca->ca_ra.ra_reg[TCX_REG_DFB8], 0, round_page(sc->sc_sunfb.sf_fbsize)); /* * If the frame buffer advertizes itself as the 8 bit model, or * if the PROM ranges are too small, limit ourselves to 8 bit * operations. * * Further code needing to know which capabilities the frame buffer * has will rely on sc_cplane being non-zero if 24 bit operation * is possible. */ if (!node_has_property(node, "tcx-8-bit") && ca->ca_ra.ra_reg[TCX_REG_RDFB32].rr_len >= sc->sc_sunfb.sf_fbsize * 4) { sc->sc_cplane = (paddr_t)ca->ca_ra.ra_reg[TCX_REG_RDFB32].rr_paddr; } printf("%dx%dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height, sc->sc_cplane == 0 ? 8 : 24); /* * Set up mappings for the acceleration code. This may fail. */ tcx_accel_init(sc, ca); /* reset cursor & frame buffer controls */ tcx_reset(sc, 8); /* enable video */ tcx_burner(sc, 1, 0); sc->sc_sunfb.sf_ro.ri_hw = sc; sc->sc_sunfb.sf_ro.ri_bits = (void *)sc->sc_dfb8; fbwscons_init(&sc->sc_sunfb, sc->sc_isconsole); fbwscons_setcolormap(&sc->sc_sunfb, tcx_setcolor); /* * Now plug accelerated console routines, if possible. */ tcx_accel_plug(sc, ca); sc->sc_ih.ih_fun = tcx_intr; sc->sc_ih.ih_arg = sc; intr_establish(pri, &sc->sc_ih, IPL_FB, self->dv_xname); if (sc->sc_isconsole) fbwscons_console_init(&sc->sc_sunfb, -1); fbwscons_attach(&sc->sc_sunfb, &tcx_accessops, sc->sc_isconsole); } int tcxactivate(struct device *self, int act) { struct tcx_softc *sc = (struct tcx_softc *)self; int ret = 0; switch (act) { case DVACT_POWERDOWN: if (sc->sc_isconsole) tcx_prom(sc); break; } return (ret); } int tcx_ioctl(void *dev, u_long cmd, caddr_t data, int flags, struct proc *p) { struct tcx_softc *sc = dev; struct wsdisplay_cmap *cm; struct wsdisplay_fbinfo *wdf; int error; /* * Note that, although the emulation (text) mode is running in 8-bit * mode, if the frame buffer is able to run in 24-bit mode, it will * be advertized as such. */ switch (cmd) { case WSDISPLAYIO_GTYPE: *(u_int *)data = WSDISPLAY_TYPE_SUNTCX; break; case WSDISPLAYIO_GINFO: wdf = (struct wsdisplay_fbinfo *)data; wdf->height = sc->sc_sunfb.sf_height; wdf->width = sc->sc_sunfb.sf_width; wdf->depth = sc->sc_sunfb.sf_depth; wdf->cmsize = sc->sc_cplane == 0 ? 256 : 0; break; case WSDISPLAYIO_GETSUPPORTEDDEPTH: if (sc->sc_cplane != 0) *(u_int *)data = WSDISPLAYIO_DEPTH_24_32; else return (-1); break; case WSDISPLAYIO_LINEBYTES: if (sc->sc_cplane == 0) *(u_int *)data = sc->sc_sunfb.sf_linebytes; else *(u_int *)data = sc->sc_sunfb.sf_linebytes * 4; break; case WSDISPLAYIO_GETCMAP: if (sc->sc_cplane == 0) { cm = (struct wsdisplay_cmap *)data; error = bt_getcmap(&sc->sc_cmap, cm); if (error) return (error); } break; case WSDISPLAYIO_PUTCMAP: if (sc->sc_cplane == 0) { cm = (struct wsdisplay_cmap *)data; error = bt_putcmap(&sc->sc_cmap, cm); if (error) return (error); tcx_loadcmap_deferred(sc, cm->index, cm->count); } break; case WSDISPLAYIO_SMODE: if (*(int *)data == WSDISPLAYIO_MODE_EMUL) { /* Back from X11 to text mode */ tcx_reset(sc, 8); } else { /* Starting X11, try to switch to 24 bit mode */ if (sc->sc_cplane != 0) tcx_reset(sc, 32); } break; case WSDISPLAYIO_SVIDEO: case WSDISPLAYIO_GVIDEO: break; default: return (-1); /* not supported yet */ } return (0); } void tcx_reset(struct tcx_softc *sc, int depth) { volatile struct bt_regs *bt; /* Hide the cursor, just in case */ sc->sc_thc->thc_cursoraddr = THC_CURSOFF; /* Enable cursor in Brooktree DAC. */ bt = sc->sc_bt; bt->bt_addr = BT_CR << 24; bt->bt_ctrl |= (BTCR_DISPENA_OV1 | BTCR_DISPENA_OV0) << 24; /* * Change mode if appropriate */ if (sc->sc_sunfb.sf_depth != depth) { if (sc->sc_cplane != 0) { tcx_s24_reset(sc, depth); } if (depth == 8) fbwscons_setcolormap(&sc->sc_sunfb, tcx_setcolor); } sc->sc_sunfb.sf_depth = depth; } void tcx_prom(struct tcx_softc *sc) { extern struct consdev consdev_prom; if (sc->sc_sunfb.sf_depth != 8) { /* * Select 8-bit mode. */ tcx_reset(sc, 8); /* * Go back to prom output for the last few messages, so they * will be displayed correctly. */ cn_tab = &consdev_prom; } } void tcx_burner(void *v, u_int on, u_int flags) { struct tcx_softc *sc = v; int s; u_int32_t thcm; s = splhigh(); thcm = sc->sc_thc->thc_hcmisc; if (on) { thcm |= THC_MISC_VIDEN; thcm &= ~(THC_MISC_VSYNC_DISABLE | THC_MISC_HSYNC_DISABLE); } else { thcm &= ~THC_MISC_VIDEN; if (flags & WSDISPLAY_BURN_VBLANK) thcm |= THC_MISC_VSYNC_DISABLE | THC_MISC_HSYNC_DISABLE; } sc->sc_thc->thc_hcmisc = thcm; splx(s); } paddr_t tcx_mmap(void *v, off_t offset, int prot) { struct tcx_softc *sc = v; int regno; if (offset & PGOFSET || offset < 0) return (-1); /* Allow mapping as a dumb framebuffer from offset 0 */ if (sc->sc_sunfb.sf_depth == 8 && offset < sc->sc_sunfb.sf_fbsize) regno = 0; /* copy of TCX_REG_DFB8 */ else if (sc->sc_sunfb.sf_depth != 8 && offset < sc->sc_sunfb.sf_fbsize * 4) regno = 1; /* copy of TCX_REG_RDFB32 */ else return (-1); return (REG2PHYS(&sc->sc_phys[regno], offset) | PMAP_NC); } void tcx_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b) { struct tcx_softc *sc = v; bt_setcolor(&sc->sc_cmap, sc->sc_bt, index, r, g, b, 1); } static __inline__ void tcx_loadcmap_deferred(struct tcx_softc *sc, u_int start, u_int ncolors) { u_int32_t thcm; thcm = sc->sc_thc->thc_hcmisc; thcm |= THC_MISC_INTEN; sc->sc_thc->thc_hcmisc = thcm; } int tcx_intr(void *v) { struct tcx_softc *sc = v; u_int32_t thcm; thcm = sc->sc_thc->thc_hcmisc; if (thcm & THC_MISC_INTEN) { thcm &= ~(THC_MISC_INTR | THC_MISC_INTEN); /* Acknowledge the interrupt */ sc->sc_thc->thc_hcmisc = thcm | THC_MISC_INTR; bt_loadcmap(&sc->sc_cmap, sc->sc_bt, 0, 256, 1); /* Disable further interrupts now */ sc->sc_thc->thc_hcmisc = thcm; return (1); } return (0); } /* * Switch video mode and clear screen */ void tcx_s24_reset(struct tcx_softc *sc, int depth) { struct rasops_info *ri = &sc->sc_sunfb.sf_ro; uint32_t pixel; paddr_t dst; int n; if (depth == 8) pixel = TCX_CTL_8_MAPPED | (ri->ri_devcmap[WSCOL_WHITE] & 0xff); else pixel = TCX_CTL_24_LEVEL | 0xffffff; /* * Set the first 32 pixels as white in the intended mode, using the * control plane. */ dst = sc->sc_cplane; for (n = 32; n != 0; n--) { sta(dst, ASI_BYPASS, pixel); dst += 4; } /* * Do the remaining pixels: either with the blitter if we can use it, * or continuing manual writes if we can't. */ if (sc->sc_blit != 0) { dst = sc->sc_blit + (32 << 3); pixel = ((sc->sc_blit_width - 1) << 24) | 0; for (n = sc->sc_sunfb.sf_fbsize - 32; n != 0; n -= sc->sc_blit_width) { stda(dst, ASI_BYPASS, pixel); dst += sc->sc_blit_width << 3; } } else { /* this relies on video memory being contiguous */ for (n = sc->sc_sunfb.sf_fbsize - 32; n != 0; n--) { sta(dst, ASI_BYPASS, pixel); dst += 4; } } } /* * Accelerated console operations * * Most of the TCX and S24 frame buffers can not perform simple ROPs * besides GXcopy. Those which can, have a ``stip-rop'' property, * and appear to support at least copy and invert rops. * * We use the blitter for block moves: copycols, copyrows; and the * stipple for solid fills: erasecols, eraserows, putchar. * * This work has been made possible thanks to the information collected in * http://ftp.rodents-montreal.org/mouse/docs/Sun/S24/memory-map * and experiments. * * It turns out that frame buffer memory is contiguous, so addressing * of a given (x,y) pixel is always (y * width + x), regardless of the * actual frame buffer resolution. */ void tcx_accel_init(struct tcx_softc *sc, struct confargs *ca) { int stipple_align, regno; /* * On S24, try and map raw blit and raw stipple spaces. * We prefer the raw spaces so that we can eventually switch * between 8 bit and 24 bit modes with blitter operations. * * However, on 8-bit TCX, these spaces are missing (and empty!), * so we should fallback to non-raw spaces in this case. * * Since this frame buffer can only exist on SS4 and SS5, * we can rely upon the fact this code will only run on sun4m, * and use stda() bypassing the MMU to access these spaces, * instead of mapping them (8MB KVA each, after all, even more * on an SS4 with the resolution extender VSIMM). */ sc->sc_blit_width = getpropint(ca->ca_ra.ra_node, "blit-width", 0); if (sc->sc_blit_width > 5) sc->sc_blit_width = 5; /* paranoia */ if (sc->sc_blit_width <= 3) /* not worth until more than 8 pixels */ sc->sc_blit_width = 0; else { sc->sc_blit_width = 1 << sc->sc_blit_width; regno = sc->sc_cplane == 0 ? TCX_REG_BLIT : TCX_REG_RBLIT; if (ca->ca_ra.ra_reg[regno].rr_len >= sc->sc_sunfb.sf_fbsize * 8) sc->sc_blit = (paddr_t)ca->ca_ra.ra_reg[regno].rr_paddr; } /* * Do not assume the stipple space is usable unless there is a * `stipple-align' property. Also, don't try to use it if the * alignment is not 32 - our code may not behave correctly. */ stipple_align = getpropint(ca->ca_ra.ra_node, "stipple-align", 0); if (stipple_align == 5) { regno = sc->sc_cplane == 0 ? TCX_REG_STIP : TCX_REG_RSTIP; if (ca->ca_ra.ra_reg[regno].rr_len >= sc->sc_sunfb.sf_fbsize * 8) sc->sc_stipple = (paddr_t)ca->ca_ra.ra_reg[regno].rr_paddr; } } /* * After rasops has initialized, override its basic operations with * accelerated routines whenever possible (this will depend on the * frame buffer capabilities and our success at using blit and * stipple spaces). */ void tcx_accel_plug(struct tcx_softc *sc, struct confargs *ca) { struct rasops_info *ri = &sc->sc_sunfb.sf_ro; if (sc->sc_stipple != 0) { sc->sc_sunfb.sf_ro.ri_ops.eraserows = tcx_eraserows; sc->sc_sunfb.sf_ro.ri_ops.erasecols = tcx_erasecols; if (node_has_property(ca->ca_ra.ra_node, "stip-rop")) { /* needs GXinvert support */ sc->sc_sunfb.sf_ro.ri_do_cursor = tcx_do_cursor; } if (ri->ri_font->fontwidth <= 16) { /* * The code in tcx_putchar really can handle up * to 32 bit font width if one extends the font * bits loading part to support more than 16 bits, * but then there is no such font in wsfont at the * moment, so why bother. */ sc->sc_sunfb.sf_ro.ri_ops.putchar = tcx_putchar; } } if (sc->sc_blit != 0) { sc->sc_sunfb.sf_ro.ri_ops.copyrows = tcx_copyrows; sc->sc_plain_copycols = sc->sc_sunfb.sf_ro.ri_ops.copycols; sc->sc_sunfb.sf_ro.ri_ops.copycols = tcx_copycols; } } /* * Blitter operations * * Since the blitter only operates on 1 pixel height areas, we need * to invoke it many times, and handle overlapping regions ourselves. */ int tcx_copycols(void *cookie, int row, int src, int dst, int n) { struct rasops_info *ri = cookie; struct tcx_softc *sc = ri->ri_hw; int h; uint32_t dstaddr, srcaddr; if (dst > src && dst < src + n) { /* Areas overlap, do it the slow but safe way */ return (*sc->sc_plain_copycols)(cookie, row, src, dst, n); } /* Areas do not overlap dangerously, copy forwards */ /* actual columns */ n *= ri->ri_font->fontwidth; src *= ri->ri_font->fontwidth; dst *= ri->ri_font->fontwidth; row *= ri->ri_font->fontheight; srcaddr = dstaddr = ri->ri_xorigin + (ri->ri_yorigin + row) * sc->sc_sunfb.sf_width; srcaddr += src; dstaddr += dst; for (h = ri->ri_font->fontheight; h != 0; h--) { tcx_blit(sc, dstaddr, srcaddr, n); srcaddr += sc->sc_sunfb.sf_width; dstaddr += sc->sc_sunfb.sf_width; } return 0; } int tcx_copyrows(void *cookie, int src, int dst, int n) { struct rasops_info *ri = cookie; struct tcx_softc *sc = ri->ri_hw; int reverse; uint32_t dstaddr, srcaddr; /* actual lines */ src *= ri->ri_font->fontheight; dst *= ri->ri_font->fontheight; n *= ri->ri_font->fontheight; if (dst > src && dst < src + n) { /* Areas overlap, copy backwards */ dst += n - 1; src += n - 1; reverse = 1; } else { /* Areas do not overlap dangerously, copy forwards */ reverse = 0; } dstaddr = (ri->ri_yorigin + dst) * sc->sc_sunfb.sf_width; srcaddr = (ri->ri_yorigin + src) * sc->sc_sunfb.sf_width; dstaddr += ri->ri_xorigin; srcaddr += ri->ri_xorigin; for (; n != 0; n--) { tcx_blit(sc, dstaddr, srcaddr, ri->ri_emuwidth); if (reverse) { dstaddr -= sc->sc_sunfb.sf_width; srcaddr -= sc->sc_sunfb.sf_width; } else { dstaddr += sc->sc_sunfb.sf_width; srcaddr += sc->sc_sunfb.sf_width; } } return 0; } /* * Perform a blit operation, copying the line starting at computed * position src to computed position dst, for a length of len pixels. */ void tcx_blit(struct tcx_softc *sc, uint32_t dst, uint32_t src, int len) { int cx; uint32_t addr; addr = sc->sc_blit + (dst << 3); /* do the incomplete chunk first if needed */ cx = len & (sc->sc_blit_width - 1); if (cx == 0) cx = sc->sc_blit_width; while (len != 0) { stda(addr, ASI_BYPASS, ((cx - 1) << 24) | src); src += cx; addr += cx << 3; len -= cx; /* and then full steam ahead for the others */ cx = sc->sc_blit_width; } } /* * Stipple operations */ /* canonical rop values */ #define GXcopy 0x03 #define GXinvert 0x0a #define STIPPLE_ROP_SHIFT 28 /* * Perform a stipple operation rop from (x, y) to (x + cnt - 1, y). * * We probably should honour the stipple alignment property (stipple-align), * in case it is different than 32 (1 << 5). However, due to the way * the stipple space is accessed, it is not possible to have a stricter * alignment requirement, so let's settle for 32. There probably haven't * been TCX boards with relaxed alignment rules anyway. */ void tcx_stipple(struct tcx_softc *sc, int x, int y, int cnt, int rop, int bg) { int rx; /* aligned x */ int lbcnt; /* count of untouched pixels on the left */ int rbcnt; /* count of untouched pixels on the right */ uint32_t wmask; /* write mask */ uint32_t soffs; /* stipple offset */ uint64_t scmd; scmd = rop << STIPPLE_ROP_SHIFT; scmd |= TCX_CTL_8_MAPPED | bg; /* pixel bits, here in 8-bit mode */ scmd <<= 32; /* * The first pass needs to align the position to a 32 pixel * boundary, which explains why the loop is a bit unnatural * at first glance. */ rx = x & ~(32 - 1); soffs = sc->sc_stipple + ((y * sc->sc_sunfb.sf_width + rx) << 3); lbcnt = x - rx; wmask = 0xffffffff >> lbcnt; while (cnt != 0) { rbcnt = (32 - lbcnt) - cnt; if (rbcnt < 0) rbcnt = 0; if (rbcnt != 0) wmask &= ~((1 << rbcnt) - 1); stda(soffs, ASI_BYPASS, scmd | wmask); cnt -= (32 - lbcnt) - rbcnt; soffs += 32 << 3; /* further loops are aligned */ lbcnt = 0; wmask = 0xffffffff; } } int tcx_erasecols(void *cookie, int row, int col, int n, long attr) { struct rasops_info *ri = cookie; struct tcx_softc *sc = ri->ri_hw; int fg, bg, h, cury, sx; ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL); bg = ri->ri_devcmap[bg] & 0xff; /* 8 bit palette index */ n *= ri->ri_font->fontwidth; sx = ri->ri_xorigin + col * ri->ri_font->fontwidth; cury = ri->ri_yorigin + row * ri->ri_font->fontheight; for (h = ri->ri_font->fontheight; h != 0; cury++, h--) tcx_stipple(sc, sx, cury, n, GXcopy, bg); return 0; } int tcx_eraserows(void *cookie, int row, int n, long attr) { struct rasops_info *ri = cookie; struct tcx_softc *sc = ri->ri_hw; int fg, bg, x, y, w; ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL); bg = ri->ri_devcmap[bg] & 0xff; /* 8 bit palette index */ if ((n == ri->ri_rows) && ISSET(ri->ri_flg, RI_FULLCLEAR)) { n = ri->ri_height; x = y = 0; w = ri->ri_width; } else { n *= ri->ri_font->fontheight; x = ri->ri_xorigin; y = ri->ri_yorigin + row * ri->ri_font->fontheight; w = ri->ri_emuwidth; } for (; n != 0; y++, n--) tcx_stipple(sc, x, y, w, GXcopy, bg); return 0; } int tcx_do_cursor(struct rasops_info *ri) { struct tcx_softc *sc = ri->ri_hw; int x, y, n; x = ri->ri_ccol * ri->ri_font->fontwidth + ri->ri_xorigin; y = ri->ri_crow * ri->ri_font->fontheight + ri->ri_yorigin; for (n = ri->ri_font->fontheight; n != 0; y++, n--) tcx_stipple(sc, x, y, ri->ri_font->fontwidth, GXinvert, 0xff); return 0; } int tcx_putchar(void *cookie, int row, int col, u_int uc, long attr) { struct rasops_info *ri = cookie; struct tcx_softc *sc = ri->ri_hw; struct wsdisplay_font *font = ri->ri_font; int fg, bg, ul; int h, x, y; uint8_t *fontbits; uint32_t fgpattern, bgpattern; ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, &ul); fg = ri->ri_devcmap[fg] & 0xff; /* 8 bit palette index */ bg = ri->ri_devcmap[bg] & 0xff; /* 8 bit palette index */ x = ri->ri_xorigin + col * font->fontwidth; y = ri->ri_yorigin + row * font->fontheight; if (uc == ' ') { /* inline tcx_erasecols(cookie, row, col, 1, attr) */ for (h = font->fontheight; h != 0; y++, h--) tcx_stipple(sc, x, y, font->fontwidth, GXcopy, h == 2 && ul != 0 ? fg : bg); } else { int rx; int lbcnt, rbcnt; uint32_t soffs; uint64_t stmpl, scmd; stmpl = (GXcopy << STIPPLE_ROP_SHIFT) | TCX_CTL_8_MAPPED; fontbits = (uint8_t *)font->data + (uc - font->firstchar) * ri->ri_fontscale; rx = x & ~(32 - 1); lbcnt = x - rx; rbcnt = (32 - lbcnt) - font->fontwidth; /* may be negative */ soffs = sc->sc_stipple + ((y * sc->sc_sunfb.sf_width + rx) << 3); for (h = font->fontheight; h != 0; y++, h--) { if (font->fontwidth <= 8) fgpattern = *(uint8_t *)fontbits >> (8 - font->fontwidth); else /* if (font->fontwidth <= 16) */ fgpattern = *(uint16_t *)fontbits >> (16 - font->fontwidth); /* see tcx_accel_plug() for the reason why larger font sizes are not supported, yet */ fontbits += font->stride; /* underline */ if (ul && h == 2) fgpattern = 0xffffffff & ((1 << font->fontwidth) - 1); bgpattern = ~fgpattern & ((1 << font->fontwidth) - 1); /* * We have a pattern of font->fontwidth bits in * the low bits of `fgpattern' and its one-complement * in `bgpattern'. The bgpattern bits need to be * painted with the background colour, while the * fgpattern bits need to be painted with the * foreground colour. * * The particular character cell position might * span two stipple cells, so we have to account * for this. */ if (rbcnt >= 0) { /* everything fits in one stipple cell. */ /* foreground */ scmd = (stmpl | fg) << 32; stda(soffs, ASI_BYPASS, scmd | (fgpattern << rbcnt)); /* background */ scmd = (stmpl | bg) << 32; stda(soffs, ASI_BYPASS, scmd | (bgpattern << rbcnt)); } else { /* needs two stipple cells. */ /* foreground, first stipple cell */ scmd = (stmpl | fg) << 32; stda(soffs, ASI_BYPASS, scmd | (fgpattern >> -rbcnt)); /* background, first stipple cell */ scmd = (stmpl | bg) << 32; stda(soffs, ASI_BYPASS, scmd | (bgpattern >> -rbcnt)); /* rotate patterns, relying on 32 bit size */ fgpattern <<= (32 + rbcnt); bgpattern <<= (32 + rbcnt); /* foreground, second stipple cell */ scmd = (stmpl | fg) << 32; stda(soffs + (32 << 3), ASI_BYPASS, scmd | fgpattern); /* background, second stipple cell */ scmd = (stmpl | bg) << 32; stda(soffs + (32 << 3), ASI_BYPASS, scmd | bgpattern); } soffs += sc->sc_sunfb.sf_width << 3; } } return 0; }