/* $NetBSD: igsfb.c,v 1.26 2005/12/11 12:21:27 christos Exp $ */ /* * Copyright (c) 2002, 2003 Valeriy E. Ushakov * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * 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. */ /* * Integraphics Systems IGA 168x and CyberPro series. */ #include __KERNEL_RCSID(0, "$NetBSD: igsfb.c,v 1.26 2005/12/11 12:21:27 christos Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct igsfb_devconfig igsfb_console_dc; /* * wsscreen */ /* filled from rasops_info in igsfb_common_init */ static struct wsscreen_descr igsfb_stdscreen = { "std", /* name */ 0, 0, /* ncols, nrows */ NULL, /* textops */ 0, 0, /* fontwidth, fontheight */ 0 /* capabilities */ }; static const struct wsscreen_descr *_igsfb_scrlist[] = { &igsfb_stdscreen, }; static const struct wsscreen_list igsfb_screenlist = { sizeof(_igsfb_scrlist) / sizeof(struct wsscreen_descr *), _igsfb_scrlist }; /* * wsdisplay_accessops */ static int igsfb_ioctl(void *, u_long, caddr_t, int, struct lwp *); static paddr_t igsfb_mmap(void *, off_t, int); static int igsfb_alloc_screen(void *, const struct wsscreen_descr *, void **, int *, int *, long *); static void igsfb_free_screen(void *, void *); static int igsfb_show_screen(void *, void *, int, void (*) (void *, int, int), void *); static const struct wsdisplay_accessops igsfb_accessops = { igsfb_ioctl, igsfb_mmap, igsfb_alloc_screen, igsfb_free_screen, igsfb_show_screen, NULL, /* load_font */ NULL, /* pollc */ NULL, /* getwschar */ NULL /* putwschar */ }; /* * acceleration */ static int igsfb_make_text_cursor(struct igsfb_devconfig *); static void igsfb_accel_cursor(void *, int, int, int); static int igsfb_accel_wait(struct igsfb_devconfig *); static void igsfb_accel_fill(struct igsfb_devconfig *, uint32_t, uint32_t, uint16_t, uint16_t); static void igsfb_accel_copy(struct igsfb_devconfig *, uint32_t, uint32_t, uint16_t, uint16_t); static void igsfb_accel_copycols(void *, int, int, int, int); static void igsfb_accel_erasecols(void *, int, int, int, long); static void igsfb_accel_copyrows(void *, int, int, int); static void igsfb_accel_eraserows(void *, int, int, long); static void igsfb_accel_putchar(void *, int, int, u_int, long); /* * internal functions */ static int igsfb_init_video(struct igsfb_devconfig *); static void igsfb_init_cmap(struct igsfb_devconfig *); static uint16_t igsfb_spread_bits_8(uint8_t); static void igsfb_init_bit_table(struct igsfb_devconfig *); static void igsfb_init_wsdisplay(struct igsfb_devconfig *); static void igsfb_blank_screen(struct igsfb_devconfig *, int); static int igsfb_get_cmap(struct igsfb_devconfig *, struct wsdisplay_cmap *); static int igsfb_set_cmap(struct igsfb_devconfig *, const struct wsdisplay_cmap *); static void igsfb_update_cmap(struct igsfb_devconfig *, u_int, u_int); static void igsfb_set_curpos(struct igsfb_devconfig *, const struct wsdisplay_curpos *); static void igsfb_update_curpos(struct igsfb_devconfig *); static int igsfb_get_cursor(struct igsfb_devconfig *, struct wsdisplay_cursor *); static int igsfb_set_cursor(struct igsfb_devconfig *, const struct wsdisplay_cursor *); static void igsfb_update_cursor(struct igsfb_devconfig *, u_int); static void igsfb_convert_cursor_data(struct igsfb_devconfig *, u_int, u_int); int igsfb_cnattach_subr(dc) struct igsfb_devconfig *dc; { struct rasops_info *ri; long defattr; KASSERT(dc == &igsfb_console_dc); igsfb_init_video(dc); igsfb_init_wsdisplay(dc); dc->dc_nscreens = 1; ri = &dc->dc_ri; (*ri->ri_ops.allocattr)(ri, WSCOL_BLACK, /* fg */ WSCOL_BLACK, /* bg */ 0, /* wsattrs */ &defattr); wsdisplay_cnattach(&igsfb_stdscreen, ri, /* emulcookie */ 0, 0, /* cursor position */ defattr); return (0); } /* * Finish off the attach. Bus specific attach method should have * enabled io and memory accesses and mapped io (and cop?) registers. */ void igsfb_attach_subr(sc, isconsole) struct igsfb_softc *sc; int isconsole; { struct igsfb_devconfig *dc = sc->sc_dc; struct wsemuldisplaydev_attach_args waa; KASSERT(dc != NULL); if (!isconsole) { igsfb_init_video(dc); igsfb_init_wsdisplay(dc); } printf("%s: %dMB, %s%dx%d, %dbpp\n", sc->sc_dev.dv_xname, (uint32_t)(dc->dc_vmemsz >> 20), (dc->dc_hwflags & IGSFB_HW_BSWAP) ? (dc->dc_hwflags & IGSFB_HW_BE_SELECT) ? "hardware bswap, " : "software bswap, " : "", dc->dc_width, dc->dc_height, dc->dc_depth); /* attach wsdisplay */ waa.console = isconsole; waa.scrdata = &igsfb_screenlist; waa.accessops = &igsfb_accessops; waa.accesscookie = dc; config_found(&sc->sc_dev, &waa, wsemuldisplaydevprint); } static int igsfb_init_video(dc) struct igsfb_devconfig *dc; { bus_space_handle_t tmph; uint8_t *p; int need_bswap; bus_addr_t fbaddr, craddr; off_t croffset; uint8_t busctl, curctl; /* Total amount of video memory. */ busctl = igs_ext_read(dc->dc_iot, dc->dc_ioh, IGS_EXT_BUS_CTL); if (busctl & 0x2) dc->dc_vmemsz = 4; else if (busctl & 0x1) dc->dc_vmemsz = 2; else dc->dc_vmemsz = 1; dc->dc_vmemsz <<= 20; /* megabytes -> bytes */ /* * Check for endianness mismatch by writing a word at the end of * the video memory (off-screen) and reading it back byte-by-byte. */ if (bus_space_map(dc->dc_memt, dc->dc_memaddr + dc->dc_vmemsz - sizeof(uint32_t), sizeof(uint32_t), dc->dc_memflags | BUS_SPACE_MAP_LINEAR, &tmph) != 0) { printf("unable to map video memory for endianness test\n"); return (1); } p = bus_space_vaddr(dc->dc_memt, tmph); #if BYTE_ORDER == BIG_ENDIAN *((uint32_t *)p) = 0x12345678; #else *((uint32_t *)p) = 0x78563412; #endif if (p[0] == 0x12 && p[1] == 0x34 && p[2] == 0x56 && p[3] == 0x78) need_bswap = 0; else need_bswap = 1; bus_space_unmap(dc->dc_memt, tmph, sizeof(uint32_t)); /* * On CyberPro we can use magic bswap bit in linear address. */ fbaddr = dc->dc_memaddr; if (need_bswap) { dc->dc_hwflags |= IGSFB_HW_BSWAP; if (dc->dc_id >= 0x2000) { dc->dc_hwflags |= IGSFB_HW_BE_SELECT; fbaddr |= IGS_MEM_BE_SELECT; } } /* * XXX: TODO: make it possible to select the desired video mode. * For now - hardcode to 1024x768/8bpp. This is what Krups OFW uses. */ igsfb_hw_setup(dc); delay(10000); /* XXX: uwe */ dc->dc_width = 1024; dc->dc_height = 768; dc->dc_depth = 8; /* * Don't map in all N megs, just the amount we need for the wsscreen. */ dc->dc_fbsz = dc->dc_width * dc->dc_height; /* XXX: 8bpp specific */ if (bus_space_map(dc->dc_memt, fbaddr, dc->dc_fbsz, dc->dc_memflags | BUS_SPACE_MAP_LINEAR, &dc->dc_fbh) != 0) { bus_space_unmap(dc->dc_iot, dc->dc_ioh, IGS_REG_SIZE); printf("unable to map framebuffer\n"); return (1); } igsfb_init_cmap(dc); /* * 1KB for cursor sprite data at the very end of the video memory. */ croffset = dc->dc_vmemsz - IGS_CURSOR_DATA_SIZE; craddr = fbaddr + croffset; if (bus_space_map(dc->dc_memt, craddr, IGS_CURSOR_DATA_SIZE, dc->dc_memflags | BUS_SPACE_MAP_LINEAR, &dc->dc_crh) != 0) { bus_space_unmap(dc->dc_iot, dc->dc_ioh, IGS_REG_SIZE); bus_space_unmap(dc->dc_memt, dc->dc_fbh, dc->dc_fbsz); printf("unable to map cursor sprite region\n"); return (1); } /* * Tell the device where cursor sprite data are located in the * linear space (it takes data offset in 1KB units). */ croffset >>= 10; /* bytes -> kilobytes */ igs_ext_write(dc->dc_iot, dc->dc_ioh, IGS_EXT_SPRITE_DATA_LO, croffset & 0xff); igs_ext_write(dc->dc_iot, dc->dc_ioh, IGS_EXT_SPRITE_DATA_HI, (croffset >> 8) & 0xf); /* init the bit expansion table for cursor sprite data conversion */ igsfb_init_bit_table(dc); /* XXX: fill dc_cursor and use igsfb_update_cursor() instead? */ memset(&dc->dc_cursor, 0, sizeof(struct igs_hwcursor)); memset(bus_space_vaddr(dc->dc_memt, dc->dc_crh), /* transparent */ 0xaa, IGS_CURSOR_DATA_SIZE); curctl = igs_ext_read(dc->dc_iot, dc->dc_ioh, IGS_EXT_SPRITE_CTL); curctl |= IGS_EXT_SPRITE_64x64; curctl &= ~IGS_EXT_SPRITE_VISIBLE; igs_ext_write(dc->dc_iot, dc->dc_ioh, IGS_EXT_SPRITE_CTL, curctl); dc->dc_curenb = 0; /* * Map and init graphic coprocessor for accelerated rasops. */ if (dc->dc_id >= 0x2000) { /* XXX */ if (bus_space_map(dc->dc_iot, dc->dc_iobase + IGS_COP_BASE_B, IGS_COP_SIZE, dc->dc_ioflags, &dc->dc_coph) != 0) { printf("unable to map COP registers\n"); return (1); } /* XXX: hardcoded 8bpp */ bus_space_write_2(dc->dc_iot, dc->dc_coph, IGS_COP_SRC_MAP_WIDTH_REG, dc->dc_width - 1); bus_space_write_2(dc->dc_iot, dc->dc_coph, IGS_COP_DST_MAP_WIDTH_REG, dc->dc_width - 1); bus_space_write_1(dc->dc_iot, dc->dc_coph, IGS_COP_MAP_FMT_REG, IGS_COP_MAP_8BPP); } /* make sure screen is not blanked */ dc->dc_blanked = 0; igsfb_blank_screen(dc, dc->dc_blanked); return (0); } static void igsfb_init_cmap(dc) struct igsfb_devconfig *dc; { bus_space_tag_t iot = dc->dc_iot; bus_space_handle_t ioh = dc->dc_ioh; const uint8_t *p; int i; p = rasops_cmap; /* "ANSI" color map */ /* init software copy */ for (i = 0; i < IGS_CMAP_SIZE; ++i, p += 3) { dc->dc_cmap.r[i] = p[0]; dc->dc_cmap.g[i] = p[1]; dc->dc_cmap.b[i] = p[2]; } /* propagate to the device */ igsfb_update_cmap(dc, 0, IGS_CMAP_SIZE); /* set overscan color (XXX: use defattr's background?) */ igs_ext_write(iot, ioh, IGS_EXT_OVERSCAN_RED, 0); igs_ext_write(iot, ioh, IGS_EXT_OVERSCAN_GREEN, 0); igs_ext_write(iot, ioh, IGS_EXT_OVERSCAN_BLUE, 0); } static void igsfb_init_wsdisplay(dc) struct igsfb_devconfig *dc; { struct rasops_info *ri; int wsfcookie; ri = &dc->dc_ri; ri->ri_hw = dc; ri->ri_flg = RI_CENTER | RI_CLEAR; if (IGSFB_HW_SOFT_BSWAP(dc)) ri->ri_flg |= RI_BSWAP; ri->ri_depth = dc->dc_depth; ri->ri_width = dc->dc_width; ri->ri_height = dc->dc_height; ri->ri_stride = dc->dc_width; /* XXX: 8bpp specific */ ri->ri_bits = (u_char *)dc->dc_fbh; /* * Initialize wsfont related stuff. */ wsfont_init(); /* prefer gallant that is identical to the one the prom uses */ wsfcookie = wsfont_find("Gallant", 12, 22, 0, WSDISPLAY_FONTORDER_L2R, WSDISPLAY_FONTORDER_L2R); if (wsfcookie <= 0) { #ifdef DIAGNOSTIC printf("unable to find font Gallant 12x22\n"); #endif wsfcookie = wsfont_find(NULL, 0, 0, 0, /* any font at all? */ WSDISPLAY_FONTORDER_L2R, WSDISPLAY_FONTORDER_L2R); } if (wsfcookie <= 0) { printf("unable to find any fonts\n"); return; } if (wsfont_lock(wsfcookie, &ri->ri_font) != 0) { printf("unable to lock font\n"); return; } ri->ri_wsfcookie = wsfcookie; /* XXX: TODO: compute term size based on font dimensions? */ rasops_init(ri, 34, 80); rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight, ri->ri_width / ri->ri_font->fontwidth); /* use the sprite for the text mode cursor */ igsfb_make_text_cursor(dc); /* the cursor is "busy" while we are in the text mode */ dc->dc_hwflags |= IGSFB_HW_TEXT_CURSOR; /* propagate sprite data to the device */ igsfb_update_cursor(dc, WSDISPLAY_CURSOR_DOSHAPE); /* accelerated text cursor */ ri->ri_ops.cursor = igsfb_accel_cursor; if (dc->dc_id >= 0x2000) { /* XXX */ /* accelerated erase/copy */ ri->ri_ops.copycols = igsfb_accel_copycols; ri->ri_ops.erasecols = igsfb_accel_erasecols; ri->ri_ops.copyrows = igsfb_accel_copyrows; ri->ri_ops.eraserows = igsfb_accel_eraserows; /* putchar hook to sync with the cop */ dc->dc_ri_putchar = ri->ri_ops.putchar; ri->ri_ops.putchar = igsfb_accel_putchar; } igsfb_stdscreen.nrows = ri->ri_rows; igsfb_stdscreen.ncols = ri->ri_cols; igsfb_stdscreen.textops = &ri->ri_ops; igsfb_stdscreen.capabilities = ri->ri_caps; } /* * Init cursor data in dc_cursor for the accelerated text cursor. */ static int igsfb_make_text_cursor(dc) struct igsfb_devconfig *dc; { struct wsdisplay_font *f = dc->dc_ri.ri_font; uint16_t cc_scan[8]; /* one sprite scanline */ uint16_t s; int w, i; KASSERT(f->fontwidth <= IGS_CURSOR_MAX_SIZE); KASSERT(f->fontheight <= IGS_CURSOR_MAX_SIZE); w = f->fontwidth; for (i = 0; i < f->stride; ++i) { if (w >= 8) { s = 0xffff; /* all inverted */ w -= 8; } else { /* first w pixels inverted, the rest is transparent */ s = ~(0x5555 << (w * 2)); if (IGSFB_HW_SOFT_BSWAP(dc)) s = bswap16(s); w = 0; } cc_scan[i] = s; } dc->dc_cursor.cc_size.x = f->fontwidth; dc->dc_cursor.cc_size.y = f->fontheight; dc->dc_cursor.cc_hot.x = 0; dc->dc_cursor.cc_hot.y = 0; /* init sprite array to be all transparent */ memset(dc->dc_cursor.cc_sprite, 0xaa, IGS_CURSOR_DATA_SIZE); for (i = 0; i < f->fontheight; ++i) memcpy(&dc->dc_cursor.cc_sprite[i * 8], cc_scan, f->stride * sizeof(uint16_t)); return (0); } /* * Spread a byte (abcd.efgh) into two (0a0b.0c0d 0e0f.0g0h). * Helper function for igsfb_init_bit_table(). */ static uint16_t igsfb_spread_bits_8(b) uint8_t b; { uint16_t s = b; s = ((s & 0x00f0) << 4) | (s & 0x000f); s = ((s & 0x0c0c) << 2) | (s & 0x0303); s = ((s & 0x2222) << 1) | (s & 0x1111); return (s); } /* * Cursor sprite data are in 2bpp. Incoming image/mask are in 1bpp. * Prebuild the table to expand 1bpp->2bpp, with bswapping if necessary. */ static void igsfb_init_bit_table(dc) struct igsfb_devconfig *dc; { uint16_t *expand = dc->dc_bexpand; int need_bswap = IGSFB_HW_SOFT_BSWAP(dc); uint16_t s; u_int i; for (i = 0; i < 256; ++i) { s = igsfb_spread_bits_8(i); expand[i] = need_bswap ? bswap16(s) : s; } } /* * wsdisplay_accessops: mmap() * XXX: allow mmapping i/o mapped i/o regs if INSECURE??? */ static paddr_t igsfb_mmap(v, offset, prot) void *v; off_t offset; int prot; { struct igsfb_devconfig *dc = v; if (offset >= dc->dc_memsz || offset < 0) return (-1); return (bus_space_mmap(dc->dc_memt, dc->dc_memaddr, offset, prot, dc->dc_memflags | BUS_SPACE_MAP_LINEAR)); } /* * wsdisplay_accessops: ioctl() */ static int igsfb_ioctl(v, cmd, data, flag, l) void *v; u_long cmd; caddr_t data; int flag; struct lwp *l; { struct igsfb_devconfig *dc = v; struct rasops_info *ri; int cursor_busy; int turnoff; /* don't permit cursor ioctls if we use sprite for text cursor */ cursor_busy = !dc->dc_mapped && (dc->dc_hwflags & IGSFB_HW_TEXT_CURSOR); switch (cmd) { case WSDISPLAYIO_GTYPE: *(u_int *)data = WSDISPLAY_TYPE_PCIMISC; return (0); case WSDISPLAYIO_GINFO: ri = &dc->dc_ri; #define wsd_fbip ((struct wsdisplay_fbinfo *)data) wsd_fbip->height = ri->ri_height; wsd_fbip->width = ri->ri_width; wsd_fbip->depth = ri->ri_depth; wsd_fbip->cmsize = IGS_CMAP_SIZE; #undef wsd_fbip return (0); case WSDISPLAYIO_LINEBYTES: ri = &dc->dc_ri; *(int *)data = ri->ri_stride; return (0); case WSDISPLAYIO_SMODE: #define d (*(int *)data) if (d != WSDISPLAYIO_MODE_EMUL) { dc->dc_mapped = 1; /* turn off hardware cursor */ if (dc->dc_hwflags & IGSFB_HW_TEXT_CURSOR) { dc->dc_curenb = 0; igsfb_update_cursor(dc, WSDISPLAY_CURSOR_DOCUR); } } else { dc->dc_mapped = 0; /* reinit sprite for text cursor */ if (dc->dc_hwflags & IGSFB_HW_TEXT_CURSOR) { igsfb_make_text_cursor(dc); dc->dc_curenb = 0; igsfb_update_cursor(dc, WSDISPLAY_CURSOR_DOSHAPE | WSDISPLAY_CURSOR_DOCUR); } } return (0); case WSDISPLAYIO_GVIDEO: *(u_int *)data = dc->dc_blanked ? WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON; return (0); case WSDISPLAYIO_SVIDEO: turnoff = (*(u_int *)data == WSDISPLAYIO_VIDEO_OFF); if (dc->dc_blanked != turnoff) { dc->dc_blanked = turnoff; igsfb_blank_screen(dc, dc->dc_blanked); } return (0); case WSDISPLAYIO_GETCMAP: return (igsfb_get_cmap(dc, (struct wsdisplay_cmap *)data)); case WSDISPLAYIO_PUTCMAP: return (igsfb_set_cmap(dc, (struct wsdisplay_cmap *)data)); case WSDISPLAYIO_GCURMAX: ((struct wsdisplay_curpos *)data)->x = IGS_CURSOR_MAX_SIZE; ((struct wsdisplay_curpos *)data)->y = IGS_CURSOR_MAX_SIZE; return (0); case WSDISPLAYIO_GCURPOS: if (cursor_busy) return (EBUSY); *(struct wsdisplay_curpos *)data = dc->dc_cursor.cc_pos; return (0); case WSDISPLAYIO_SCURPOS: if (cursor_busy) return (EBUSY); igsfb_set_curpos(dc, (struct wsdisplay_curpos *)data); return (0); case WSDISPLAYIO_GCURSOR: if (cursor_busy) return (EBUSY); return (igsfb_get_cursor(dc, (struct wsdisplay_cursor *)data)); case WSDISPLAYIO_SCURSOR: if (cursor_busy) return (EBUSY); return (igsfb_set_cursor(dc, (struct wsdisplay_cursor *)data)); } return (EPASSTHROUGH); } /* * wsdisplay_accessops: ioctl(WSDISPLAYIO_SVIDEO) */ static void igsfb_blank_screen(dc, blank) struct igsfb_devconfig *dc; int blank; { igs_ext_write(dc->dc_iot, dc->dc_ioh, IGS_EXT_SYNC_CTL, blank ? IGS_EXT_SYNC_H0 | IGS_EXT_SYNC_V0 : 0); } /* * wsdisplay_accessops: ioctl(WSDISPLAYIO_GETCMAP) * Served from the software cmap copy. */ static int igsfb_get_cmap(dc, p) struct igsfb_devconfig *dc; struct wsdisplay_cmap *p; { u_int index, count; int err; index = p->index; count = p->count; if (index >= IGS_CMAP_SIZE || count > IGS_CMAP_SIZE - index) return (EINVAL); err = copyout(&dc->dc_cmap.r[index], p->red, count); if (err) return (err); err = copyout(&dc->dc_cmap.g[index], p->green, count); if (err) return (err); err = copyout(&dc->dc_cmap.b[index], p->blue, count); if (err) return (err); return (0); } /* * wsdisplay_accessops: ioctl(WSDISPLAYIO_PUTCMAP) * Set the software cmap copy and propagate changed range to the device. */ static int igsfb_set_cmap(dc, p) struct igsfb_devconfig *dc; const struct wsdisplay_cmap *p; { u_int index, count; uint8_t r[IGS_CMAP_SIZE]; uint8_t g[IGS_CMAP_SIZE]; uint8_t b[IGS_CMAP_SIZE]; int error; index = p->index; count = p->count; if (index >= IGS_CMAP_SIZE || count > IGS_CMAP_SIZE - index) return (EINVAL); error = copyin(p->red, &r[index], count); if (error) return error; error = copyin(p->green, &g[index], count); if (error) return error; error = copyin(p->blue, &b[index], count); if (error) return error; memcpy(&dc->dc_cmap.r[index], &r[index], count); memcpy(&dc->dc_cmap.g[index], &g[index], count); memcpy(&dc->dc_cmap.b[index], &b[index], count); /* propagate changes to the device */ igsfb_update_cmap(dc, index, count); return (0); } /* * Propagate specified part of the software cmap copy to the device. */ static void igsfb_update_cmap(dc, index, count) struct igsfb_devconfig *dc; u_int index, count; { bus_space_tag_t t; bus_space_handle_t h; u_int last, i; if (index >= IGS_CMAP_SIZE) return; last = index + count; if (last > IGS_CMAP_SIZE) last = IGS_CMAP_SIZE; t = dc->dc_iot; h = dc->dc_ioh; /* start palette writing, index is autoincremented by hardware */ bus_space_write_1(t, h, IGS_DAC_PEL_WRITE_IDX, index); for (i = index; i < last; ++i) { bus_space_write_1(t, h, IGS_DAC_PEL_DATA, dc->dc_cmap.r[i]); bus_space_write_1(t, h, IGS_DAC_PEL_DATA, dc->dc_cmap.g[i]); bus_space_write_1(t, h, IGS_DAC_PEL_DATA, dc->dc_cmap.b[i]); } } /* * wsdisplay_accessops: ioctl(WSDISPLAYIO_SCURPOS) */ static void igsfb_set_curpos(dc, curpos) struct igsfb_devconfig *dc; const struct wsdisplay_curpos *curpos; { struct rasops_info *ri = &dc->dc_ri; u_int x = curpos->x, y = curpos->y; if (x >= ri->ri_width) x = ri->ri_width - 1; if (y >= ri->ri_height) y = ri->ri_height - 1; dc->dc_cursor.cc_pos.x = x; dc->dc_cursor.cc_pos.y = y; /* propagate changes to the device */ igsfb_update_curpos(dc); } static void igsfb_update_curpos(dc) struct igsfb_devconfig *dc; { bus_space_tag_t t; bus_space_handle_t h; int x, xoff, y, yoff; xoff = 0; x = dc->dc_cursor.cc_pos.x - dc->dc_cursor.cc_hot.x; if (x < 0) { xoff = -x; x = 0; } yoff = 0; y = dc->dc_cursor.cc_pos.y - dc->dc_cursor.cc_hot.y; if (y < 0) { yoff = -y; y = 0; } t = dc->dc_iot; h = dc->dc_ioh; igs_ext_write(t, h, IGS_EXT_SPRITE_HSTART_LO, x & 0xff); igs_ext_write(t, h, IGS_EXT_SPRITE_HSTART_HI, (x >> 8) & 0x07); igs_ext_write(t, h, IGS_EXT_SPRITE_HPRESET, xoff & 0x3f); igs_ext_write(t, h, IGS_EXT_SPRITE_VSTART_LO, y & 0xff); igs_ext_write(t, h, IGS_EXT_SPRITE_VSTART_HI, (y >> 8) & 0x07); igs_ext_write(t, h, IGS_EXT_SPRITE_VPRESET, yoff & 0x3f); } /* * wsdisplay_accessops: ioctl(WSDISPLAYIO_GCURSOR) */ static int igsfb_get_cursor(dc, p) struct igsfb_devconfig *dc; struct wsdisplay_cursor *p; { /* XXX: TODO */ return (0); } /* * wsdisplay_accessops: ioctl(WSDISPLAYIO_SCURSOR) */ static int igsfb_set_cursor(dc, p) struct igsfb_devconfig *dc; const struct wsdisplay_cursor *p; { struct igs_hwcursor *cc; struct wsdisplay_curpos pos, hot; u_int v, index, count, icount, iwidth; uint8_t r[2], g[2], b[2], image[512], mask[512]; int error; cc = &dc->dc_cursor; v = p->which; index = count = icount = iwidth = 0; /* XXX: gcc */ /* copy in the new cursor colormap */ if (v & WSDISPLAY_CURSOR_DOCMAP) { index = p->cmap.index; count = p->cmap.count; if (index >= 2 || (index + count) > 2) return (EINVAL); error = copyin(p->cmap.red, &r[index], count); if (error) return error; error = copyin(p->cmap.green, &g[index], count); if (error) return error; error = copyin(p->cmap.blue, &b[index], count); if (error) return error; } /* verify that the new cursor data are valid */ if (v & WSDISPLAY_CURSOR_DOSHAPE) { if (p->size.x > IGS_CURSOR_MAX_SIZE || p->size.y > IGS_CURSOR_MAX_SIZE) return (EINVAL); iwidth = (p->size.x + 7) >> 3; /* bytes per scan line */ icount = iwidth * p->size.y; error = copyin(p->image, image, icount); if (error) return error; error = copyin(p->mask, mask, icount); if (error) return error; } /* enforce that the position is within screen bounds */ if (v & WSDISPLAY_CURSOR_DOPOS) { struct rasops_info *ri = &dc->dc_ri; pos = p->pos; /* local copy we can write to */ if (pos.x >= ri->ri_width) pos.x = ri->ri_width - 1; if (pos.y >= ri->ri_height) pos.y = ri->ri_height - 1; } /* enforce that the hot spot is within sprite bounds */ if (v & WSDISPLAY_CURSOR_DOHOT) hot = p->hot; /* local copy we can write to */ if (v & (WSDISPLAY_CURSOR_DOHOT | WSDISPLAY_CURSOR_DOSHAPE)) { const struct wsdisplay_curpos *nsize; struct wsdisplay_curpos *nhot; nsize = (v & WSDISPLAY_CURSOR_DOSHAPE) ? &p->size : &cc->cc_size; nhot = (v & WSDISPLAY_CURSOR_DOHOT) ? &hot : &cc->cc_hot; if (nhot->x >= nsize->x) nhot->x = nsize->x - 1; if (nhot->y >= nsize->y) nhot->y = nsize->y - 1; } /* copy data to the driver's cursor info */ if (v & WSDISPLAY_CURSOR_DOCUR) dc->dc_curenb = p->enable; if (v & WSDISPLAY_CURSOR_DOPOS) cc->cc_pos = pos; /* local copy, possibly corrected */ if (v & WSDISPLAY_CURSOR_DOHOT) cc->cc_hot = hot; /* local copy, possibly corrected */ if (v & WSDISPLAY_CURSOR_DOCMAP) { memcpy(&cc->cc_color[index], &r[index], count); memcpy(&cc->cc_color[index + 2], &g[index], count); memcpy(&cc->cc_color[index + 4], &b[index], count); } if (v & WSDISPLAY_CURSOR_DOSHAPE) { u_int trailing_bits; memcpy(cc->cc_image, image, icount); memcpy(cc->cc_mask, mask, icount); cc->cc_size = p->size; /* clear trailing bits in the "partial" mask bytes */ trailing_bits = p->size.x & 0x07; if (trailing_bits != 0) { const u_int cutmask = ~((~0) << trailing_bits); u_char *mp; u_int i; mp = cc->cc_mask + iwidth - 1; for (i = 0; i < p->size.y; ++i) { *mp &= cutmask; mp += iwidth; } } igsfb_convert_cursor_data(dc, iwidth, p->size.y); } /* propagate changes to the device */ igsfb_update_cursor(dc, v); return (0); } /* * Convert incoming 1bpp cursor image/mask into native 2bpp format. */ static void igsfb_convert_cursor_data(dc, width, height) struct igsfb_devconfig *dc; u_int width, height; { struct igs_hwcursor *cc = &dc->dc_cursor; uint16_t *expand = dc->dc_bexpand; uint8_t *ip, *mp; uint16_t is, ms, *dp; u_int line, i; /* init sprite to be all transparent */ memset(cc->cc_sprite, 0xaa, IGS_CURSOR_DATA_SIZE); /* first scanline */ ip = cc->cc_image; mp = cc->cc_mask; dp = cc->cc_sprite; for (line = 0; line < height; ++line) { for (i = 0; i < width; ++i) { is = expand[ip[i]]; /* image: 0 -> 00, 1 -> 01 */ ms = expand[mp[i]]; /* mask: 0 -> 00, 1 -> 11 */ ms |= (ms << 1); dp[i] = (0xaaaa & ~ms) | (is & ms); } /* next scanline */ ip += width; mp += width; dp += 8; /* 64 pixels, 2bpp, 8 pixels per short = 8 shorts */ } } /* * Propagate cursor changes to the device. * "which" is composed of WSDISPLAY_CURSOR_DO* bits. */ static void igsfb_update_cursor(dc, which) struct igsfb_devconfig *dc; u_int which; { bus_space_tag_t iot = dc->dc_iot; bus_space_handle_t ioh = dc->dc_ioh; uint8_t curctl; curctl = 0; /* XXX: gcc */ /* * We will need to tweak sprite control register for cursor * visibility and cursor color map manipulation. */ if (which & (WSDISPLAY_CURSOR_DOCUR | WSDISPLAY_CURSOR_DOCMAP)) { curctl = igs_ext_read(iot, ioh, IGS_EXT_SPRITE_CTL); } if (which & WSDISPLAY_CURSOR_DOSHAPE) { uint8_t *dst = bus_space_vaddr(dc->dc_memt, dc->dc_crh); /* * memcpy between spaces of different endianness would * be ... special. We cannot be sure if memcpy gonna * push data in 4-byte chunks, we can't pre-bswap it, * so do it byte-by-byte to preserve byte ordering. */ if (IGSFB_HW_SOFT_BSWAP(dc)) { uint8_t *src = (uint8_t *)dc->dc_cursor.cc_sprite; int i; for (i = 0; i < IGS_CURSOR_DATA_SIZE; ++i) *dst++ = *src++; } else { memcpy(dst, dc->dc_cursor.cc_sprite, IGS_CURSOR_DATA_SIZE); } } if (which & (WSDISPLAY_CURSOR_DOPOS | WSDISPLAY_CURSOR_DOHOT)) { /* code shared with WSDISPLAYIO_SCURPOS */ igsfb_update_curpos(dc); } if (which & WSDISPLAY_CURSOR_DOCMAP) { uint8_t *p; /* tell DAC we want access to the cursor palette */ igs_ext_write(iot, ioh, IGS_EXT_SPRITE_CTL, curctl | IGS_EXT_SPRITE_DAC_PEL); p = dc->dc_cursor.cc_color; bus_space_write_1(iot, ioh, IGS_DAC_PEL_WRITE_IDX, 0); bus_space_write_1(iot, ioh, IGS_DAC_PEL_DATA, p[0]); bus_space_write_1(iot, ioh, IGS_DAC_PEL_DATA, p[2]); bus_space_write_1(iot, ioh, IGS_DAC_PEL_DATA, p[4]); bus_space_write_1(iot, ioh, IGS_DAC_PEL_WRITE_IDX, 1); bus_space_write_1(iot, ioh, IGS_DAC_PEL_DATA, p[1]); bus_space_write_1(iot, ioh, IGS_DAC_PEL_DATA, p[3]); bus_space_write_1(iot, ioh, IGS_DAC_PEL_DATA, p[5]); /* restore access to the normal palette */ igs_ext_write(iot, ioh, IGS_EXT_SPRITE_CTL, curctl); } if (which & WSDISPLAY_CURSOR_DOCUR) { if ((curctl & IGS_EXT_SPRITE_VISIBLE) == 0 && dc->dc_curenb) igs_ext_write(iot, ioh, IGS_EXT_SPRITE_CTL, curctl | IGS_EXT_SPRITE_VISIBLE); else if ((curctl & IGS_EXT_SPRITE_VISIBLE) != 0 && !dc->dc_curenb) igs_ext_write(iot, ioh, IGS_EXT_SPRITE_CTL, curctl & ~IGS_EXT_SPRITE_VISIBLE); } } /* * wsdisplay_accessops: alloc_screen() */ static int igsfb_alloc_screen(v, type, cookiep, curxp, curyp, attrp) void *v; const struct wsscreen_descr *type; void **cookiep; int *curxp, *curyp; long *attrp; { struct igsfb_devconfig *dc = v; struct rasops_info *ri = &dc->dc_ri; if (dc->dc_nscreens > 0) /* only do single screen for now */ return (ENOMEM); dc->dc_nscreens = 1; *cookiep = ri; /* emulcookie for igsgfb_stdscreen.textops */ *curxp = *curyp = 0; /* cursor position */ (*ri->ri_ops.allocattr)(ri, WSCOL_BLACK, /* fg */ WSCOL_BLACK, /* bg */ 0, /* wsattr */ attrp); return (0); } /* * wsdisplay_accessops: free_screen() */ static void igsfb_free_screen(v, cookie) void *v; void *cookie; { /* XXX */ return; } /* * wsdisplay_accessops: show_screen() */ static int igsfb_show_screen(v, cookie, waitok, cb, cbarg) void *v; void *cookie; int waitok; void (*cb)(void *, int, int); void *cbarg; { /* XXX */ return (0); } /* * Accelerated text mode cursor that uses hardware sprite. */ static void igsfb_accel_cursor(cookie, on, row, col) void *cookie; int on, row, col; { struct rasops_info *ri = (struct rasops_info *)cookie; struct igsfb_devconfig *dc = (struct igsfb_devconfig *)ri->ri_hw; struct igs_hwcursor *cc = &dc->dc_cursor; u_int which; ri->ri_crow = row; ri->ri_ccol = col; which = 0; if (on) { ri->ri_flg |= RI_CURSOR; /* only bother to move the cursor if it's visible */ cc->cc_pos.x = ri->ri_xorigin + ri->ri_ccol * ri->ri_font->fontwidth; cc->cc_pos.y = ri->ri_yorigin + ri->ri_crow * ri->ri_font->fontheight; which |= WSDISPLAY_CURSOR_DOPOS; } else ri->ri_flg &= ~RI_CURSOR; if (dc->dc_curenb != on) { dc->dc_curenb = on; which |= WSDISPLAY_CURSOR_DOCUR; } /* propagate changes to the device */ igsfb_update_cursor(dc, which); } /* * Accelerated raster ops that use graphic coprocessor. */ static int igsfb_accel_wait(dc) struct igsfb_devconfig *dc; { bus_space_tag_t t = dc->dc_iot; bus_space_handle_t h = dc->dc_coph; int timo = 100000; uint8_t reg; while (timo--) { reg = bus_space_read_1(t, h, IGS_COP_CTL_REG); if ((reg & IGS_COP_CTL_BUSY) == 0) return (0); } return (1); } static void igsfb_accel_copy(dc, src, dst, width, height) struct igsfb_devconfig *dc; uint32_t src, dst; uint16_t width, height; { bus_space_tag_t t = dc->dc_iot; bus_space_handle_t h = dc->dc_coph; uint32_t toend; uint8_t drawcmd; drawcmd = IGS_COP_DRAW_ALL; if (dst > src) { toend = dc->dc_ri.ri_width * (height - 1) + (width - 1); src += toend; dst += toend; drawcmd |= IGS_COP_OCTANT_X_NEG | IGS_COP_OCTANT_Y_NEG; } igsfb_accel_wait(dc); bus_space_write_1(t, h, IGS_COP_CTL_REG, 0); bus_space_write_1(t, h, IGS_COP_FG_MIX_REG, IGS_COP_MIX_S); bus_space_write_2(t, h, IGS_COP_WIDTH_REG, width - 1); bus_space_write_2(t, h, IGS_COP_HEIGHT_REG, height - 1); bus_space_write_4(t, h, IGS_COP_SRC_START_REG, src); bus_space_write_4(t, h, IGS_COP_DST_START_REG, dst); bus_space_write_1(t, h, IGS_COP_PIXEL_OP_0_REG, drawcmd); bus_space_write_1(t, h, IGS_COP_PIXEL_OP_1_REG, IGS_COP_PPM_FIXED_FG); bus_space_write_1(t, h, IGS_COP_PIXEL_OP_2_REG, 0); bus_space_write_1(t, h, IGS_COP_PIXEL_OP_3_REG, IGS_COP_OP_PXBLT | IGS_COP_OP_FG_FROM_SRC); } static void igsfb_accel_fill(dc, color, dst, width, height) struct igsfb_devconfig *dc; uint32_t color; uint32_t dst; uint16_t width, height; { bus_space_tag_t t = dc->dc_iot; bus_space_handle_t h = dc->dc_coph; igsfb_accel_wait(dc); bus_space_write_1(t, h, IGS_COP_CTL_REG, 0); bus_space_write_1(t, h, IGS_COP_FG_MIX_REG, IGS_COP_MIX_S); bus_space_write_2(t, h, IGS_COP_WIDTH_REG, width - 1); bus_space_write_2(t, h, IGS_COP_HEIGHT_REG, height - 1); bus_space_write_4(t, h, IGS_COP_DST_START_REG, dst); bus_space_write_4(t, h, IGS_COP_FG_REG, color); bus_space_write_1(t, h, IGS_COP_PIXEL_OP_0_REG, IGS_COP_DRAW_ALL); bus_space_write_1(t, h, IGS_COP_PIXEL_OP_1_REG, IGS_COP_PPM_FIXED_FG); bus_space_write_1(t, h, IGS_COP_PIXEL_OP_2_REG, 0); bus_space_write_1(t, h, IGS_COP_PIXEL_OP_3_REG, IGS_COP_OP_PXBLT); } static void igsfb_accel_copyrows(cookie, src, dst, num) void *cookie; int src, dst, num; { struct rasops_info *ri = (struct rasops_info *)cookie; struct igsfb_devconfig *dc = (struct igsfb_devconfig *)ri->ri_hw; uint32_t srp, dsp; uint16_t width, height; width = ri->ri_emuwidth; height = num * ri->ri_font->fontheight; srp = ri->ri_xorigin + ri->ri_width * (ri->ri_yorigin + src * ri->ri_font->fontheight); dsp = ri->ri_xorigin + ri->ri_width * (ri->ri_yorigin + dst * ri->ri_font->fontheight); igsfb_accel_copy(dc, srp, dsp, width, height); } void igsfb_accel_copycols(cookie, row, src, dst, num) void *cookie; int row, src, dst, num; { struct rasops_info *ri = (struct rasops_info *)cookie; struct igsfb_devconfig *dc = (struct igsfb_devconfig *)ri->ri_hw; uint32_t rowp, srp, dsp; uint16_t width, height; width = num * ri->ri_font->fontwidth; height = ri->ri_font->fontheight; rowp = ri->ri_xorigin + ri->ri_width * (ri->ri_yorigin + row * ri->ri_font->fontheight); srp = rowp + src * ri->ri_font->fontwidth; dsp = rowp + dst * ri->ri_font->fontwidth; igsfb_accel_copy(dc, srp, dsp, width, height); } static void igsfb_accel_eraserows(cookie, row, num, attr) void *cookie; int row, num; long attr; { struct rasops_info *ri = (struct rasops_info *)cookie; struct igsfb_devconfig *dc = (struct igsfb_devconfig *)ri->ri_hw; uint32_t color; uint32_t dsp; uint16_t width, height; width = ri->ri_emuwidth; height = num * ri->ri_font->fontheight; dsp = ri->ri_xorigin + ri->ri_width * (ri->ri_yorigin + row * ri->ri_font->fontheight); /* XXX: we "know" the encoding that rasops' allocattr uses */ color = (attr >> 16) & 0xff; igsfb_accel_fill(dc, color, dsp, width, height); } void igsfb_accel_erasecols(cookie, row, col, num, attr) void *cookie; int row, col, num; long attr; { struct rasops_info *ri = (struct rasops_info *)cookie; struct igsfb_devconfig *dc = (struct igsfb_devconfig *)ri->ri_hw; uint32_t color; uint32_t rowp, dsp; uint16_t width, height; width = num * ri->ri_font->fontwidth; height = ri->ri_font->fontheight; rowp = ri->ri_xorigin + ri->ri_width * (ri->ri_yorigin + row * ri->ri_font->fontheight); dsp = rowp + col * ri->ri_font->fontwidth; /* XXX: we "know" the encoding that rasops' allocattr uses */ color = (attr >> 16) & 0xff; igsfb_accel_fill(dc, color, dsp, width, height); } /* * Not really implemented here, but we need to hook into the one * supplied by rasops so that we can synchronize with the COP. */ static void igsfb_accel_putchar(cookie, row, col, uc, attr) void *cookie; int row, col; u_int uc; long attr; { struct rasops_info *ri = (struct rasops_info *)cookie; struct igsfb_devconfig *dc = (struct igsfb_devconfig *)ri->ri_hw; igsfb_accel_wait(dc); (*dc->dc_ri_putchar)(cookie, row, col, uc, attr); }