root/sys/dev/ic/igsfb.c
/*      $NetBSD: igsfb.c,v 1.60 2021/08/07 16:19:12 thorpej 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 <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: igsfb.c,v 1.60 2021/08/07 16:19:12 thorpej Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/ioctl.h>

#include <sys/bus.h>

#include <dev/wscons/wsdisplayvar.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wsfont/wsfont.h>
#include <dev/rasops/rasops.h>

#include <dev/wscons/wsdisplay_vconsvar.h>

#include <dev/ic/igsfbreg.h>
#include <dev/ic/igsfbvar.h>


struct igsfb_devconfig igsfb_console_dc = {
        .dc_mmap = NULL,
        .dc_modestring = "",
};

/*
 * wsscreen
 */

/* filled from rasops_info in igsfb_init_wsdisplay */
static struct wsscreen_descr igsfb_stdscreen = {
        .name = "std",
};

static const struct wsscreen_descr *_igsfb_scrlist[] = {
        &igsfb_stdscreen,
};

static const struct wsscreen_list igsfb_screenlist = {
        .nscreens = sizeof(_igsfb_scrlist) / sizeof(_igsfb_scrlist[0]),
        .screens = _igsfb_scrlist,
};


/*
 * wsdisplay_accessops
 */

static int      igsfb_ioctl(void *, void *, u_long, void *, int, struct lwp *);
static paddr_t  igsfb_mmap(void *, void *, off_t, int);

static struct wsdisplay_accessops igsfb_accessops = {
        .ioctl = igsfb_ioctl,
        .mmap = igsfb_mmap,
};


/*
 * acceleration
 */
static int      igsfb_make_text_cursor(struct igsfb_devconfig *,
                                       struct vcons_screen *);
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(void *, struct vcons_screen *, int,
                                     long *);


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(struct igsfb_devconfig *dc)
{
        struct rasops_info *ri;
        long defattr;

        KASSERT(dc == &igsfb_console_dc);

        igsfb_init_video(dc);
        dc->dc_vd.active = NULL;
        igsfb_init_wsdisplay(dc, &dc->dc_console, 1, &defattr);

        ri = &dc->dc_console.scr_ri;
        ri->ri_hw = &dc->dc_console;
        dc->dc_console.scr_cookie = dc;

        (*ri->ri_ops.allocattr)(ri,
                                WS_DEFAULT_FG, /* fg */
                                WS_DEFAULT_BG, /* 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(struct igsfb_softc *sc, int isconsole)
{
        struct igsfb_devconfig *dc = sc->sc_dc;
        struct wsemuldisplaydev_attach_args waa;
        struct rasops_info *ri;
        long defattr;

        KASSERT(dc != NULL);

        if (!isconsole) {
                igsfb_init_video(dc);
        }

        vcons_init(&dc->dc_vd, dc, &igsfb_stdscreen, &igsfb_accessops);
        dc->dc_vd.init_screen = igsfb_init_wsdisplay;

        vcons_init_screen(&dc->dc_vd, &dc->dc_console, 1, &defattr);
        dc->dc_console.scr_flags |= VCONS_SCREEN_IS_STATIC;

        aprint_normal("%s: %dMB, %s%dx%d, %dbpp\n",
               device_xname(sc->sc_dev),
               (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);
        aprint_normal("%s: using %dbpp for X\n", device_xname(sc->sc_dev),
               dc->dc_maxdepth);
        ri = &dc->dc_console.scr_ri;
        ri->ri_ops.eraserows(ri, 0, ri->ri_rows, defattr);

        if (isconsole)
                vcons_replay_msgbuf(&dc->dc_console);

        /* attach wsdisplay */
        waa.console = isconsole;
        waa.scrdata = &igsfb_screenlist;
        waa.accessops = &igsfb_accessops;
        waa.accesscookie = &dc->dc_vd;

        config_found(sc->sc_dev, &waa, wsemuldisplaydevprint, CFARGS_NONE);
}


static int
igsfb_init_video(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;
        void *va;

        /* 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;
                }
        }

        /*
         * Map graphic coprocessor for mode setting and 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;
                }
        }

        igsfb_hw_setup(dc);

        /*
         * Don't map in all N megs, just the amount we need for the wsscreen.
         */
        dc->dc_fbsz = dc->dc_stride * dc->dc_height;
        if (bus_space_map(dc->dc_memt, fbaddr, dc->dc_fbsz,
                          dc->dc_memflags | BUS_SPACE_MAP_LINEAR,
                          &dc->dc_fbh) != 0)
        {
                if (dc->dc_id >= 0x2000) { /* XXX */
                        bus_space_unmap(dc->dc_iot, dc->dc_coph, IGS_COP_SIZE);
                }
                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)
        {
                if (dc->dc_id >= 0x2000) { /* XXX */
                        bus_space_unmap(dc->dc_iot, dc->dc_coph, IGS_COP_SIZE);
                }
                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));
        va = bus_space_vaddr(dc->dc_memt, dc->dc_crh);
        memset(va, /* 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;

        /*
         * Init graphic coprocessor for accelerated rasops.
         */
        if (dc->dc_id >= 0x2000) { /* XXX */
                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,
                                  howmany(dc->dc_depth, NBBY) - 1);
        }

        /* make sure screen is not blanked */
        dc->dc_blanked = 0;
        igsfb_blank_screen(dc, dc->dc_blanked);

        return 0;
}


static void
igsfb_init_cmap(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 */
        p = &rasops_cmap[WSDISPLAY_BORDER_COLOR * 3];
        igs_ext_write(iot, ioh, IGS_EXT_OVERSCAN_RED,   p[0]);
        igs_ext_write(iot, ioh, IGS_EXT_OVERSCAN_GREEN, p[1]);
        igs_ext_write(iot, ioh, IGS_EXT_OVERSCAN_BLUE,  p[2]);
}


static void
igsfb_init_wsdisplay(void *cookie, struct vcons_screen *scr, int existing,
                     long *defattr)
{
        struct igsfb_devconfig *dc = cookie;
        struct rasops_info *ri = &scr->scr_ri;
        int wsfcookie;

        if (scr == &dc->dc_console) {
                if (ri->ri_flg == 0) {
                        /* first time, need to set RI_NO_AUTO */
                        ri->ri_flg |= RI_NO_AUTO;
                } else {
                        /* clear it on 2nd run */
                        ri->ri_flg &= ~RI_NO_AUTO;
                }
        }
        ri->ri_flg |= RI_CENTER | RI_FULLCLEAR;

        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_stride;
        ri->ri_bits = bus_space_vaddr(dc->dc_memt, 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, WSFONT_FIND_BITMAP);
        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,
                                        WSFONT_FIND_BITMAP);
        }

        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, 0, 0);
        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, scr);

        /* 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(struct igsfb_devconfig *dc, struct vcons_screen *scr)
{
        struct rasops_info *ri = &scr->scr_ri;
        struct wsdisplay_font *f = 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));
                        s = htole16(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(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(struct igsfb_devconfig *dc)
{
        uint16_t *expand = dc->dc_bexpand;
        uint16_t s;
        u_int i;

        for (i = 0; i < 256; ++i) {
                s = igsfb_spread_bits_8(i);
                expand[i] = htole16(s);
        }
}


/*
 * wsdisplay_accessops: mmap()
 *   XXX: security considerations for allowing mmapping i/o mapped i/o regs?
 */
static paddr_t
igsfb_mmap(void *v, void *vs, off_t offset, int prot)
{
        struct vcons_data *vd = v;
        struct igsfb_devconfig *dc = vd->cookie;

        if (offset < dc->dc_memsz && offset >= 0)
                return bus_space_mmap(dc->dc_memt, dc->dc_memaddr, offset,
                    prot, dc->dc_memflags | BUS_SPACE_MAP_LINEAR);
        if (dc->dc_mmap)
                return dc->dc_mmap(v, vs, offset, prot);
        return -1;
}


/*
 * wsdisplay_accessops: ioctl()
 */
static int
igsfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
        struct lwp *l)
{
        struct vcons_data *vd = v;
        struct igsfb_devconfig *dc = vd->cookie;
        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:
#define wsd_fbip ((struct wsdisplay_fbinfo *)data)
                wsd_fbip->height = dc->dc_height;
                wsd_fbip->width = dc->dc_width;
                wsd_fbip->depth = dc->dc_maxdepth;
                wsd_fbip->cmsize = IGS_CMAP_SIZE;
#undef wsd_fbip
                return 0;

        case WSDISPLAYIO_LINEBYTES:
                *(int *)data = dc->dc_width * howmany(dc->dc_maxdepth, NBBY);
                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);
                        }
                        if ((dc->dc_mode != NULL) && (dc->dc_maxdepth != 8))
                                igsfb_set_mode(dc, dc->dc_mode,
                                    dc->dc_maxdepth);
                } else {
                        dc->dc_mapped = 0;
                        if ((dc->dc_mode != NULL) && (dc->dc_maxdepth != 8))
                                igsfb_set_mode(dc, dc->dc_mode, 8);
                        igsfb_init_cmap(dc);
                        /* reinit sprite for text cursor */
                        if (dc->dc_hwflags & IGSFB_HW_TEXT_CURSOR) {
                                igsfb_make_text_cursor(dc, dc->dc_vd.active);
                                dc->dc_curenb = 0;
                                igsfb_update_cursor(dc,
                                          WSDISPLAY_CURSOR_DOSHAPE
                                        | WSDISPLAY_CURSOR_DOCUR);
                        }
                        vcons_redraw_screen(vd->active);
                }
                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(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(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(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(struct igsfb_devconfig *dc, u_int index, u_int count)
{
        bus_space_tag_t t;
        bus_space_handle_t h;
        u_int last, i;

        if (index >= IGS_CMAP_SIZE)
                return;

        if (count > IGS_CMAP_SIZE - index)
                last = IGS_CMAP_SIZE;
        else
                last = index + count;

        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(struct igsfb_devconfig *dc,
                 const struct wsdisplay_curpos *curpos)
{
        struct rasops_info *ri = &dc->dc_vd.active->scr_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(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(struct igsfb_devconfig *dc, struct wsdisplay_cursor *p)
{

        /* XXX: TODO */
        return 0;
}


/*
 * wsdisplay_accessops: ioctl(WSDISPLAYIO_SCURSOR)
 */
static int
igsfb_set_cursor(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 */
        pos.x = pos.y = 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_vd.active->scr_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 = ~((~0U) << 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(struct igsfb_devconfig *dc,
                          u_int width, u_int 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(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);
        }
}


/*
 * Accelerated text mode cursor that uses hardware sprite.
 */
static void
igsfb_accel_cursor(void *cookie, int on, int row, int col)
{
        struct rasops_info *ri = (struct rasops_info *)cookie;
        struct vcons_screen *scr = ri->ri_hw;
        struct igsfb_devconfig *dc = scr->scr_cookie;
        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(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;

        bus_space_write_1(t, h, IGS_COP_MAP_FMT_REG,
            howmany(dc->dc_depth, NBBY) - 1);
        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(struct igsfb_devconfig *dc, uint32_t src, uint32_t dst,
                 uint16_t width, uint16_t 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_vd.active->scr_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(struct igsfb_devconfig *dc, uint32_t color, uint32_t dst,
                 uint16_t width, uint16_t 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(void *cookie, int src, int dst, int num)
{
        struct rasops_info *ri = (struct rasops_info *)cookie;
        struct vcons_screen *scr = ri->ri_hw;
        struct igsfb_devconfig *dc = (struct igsfb_devconfig *)scr->scr_cookie;
        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);
}


static void
igsfb_accel_copycols(void *cookie, int row, int src, int dst, int num)
{
        struct rasops_info *ri = (struct rasops_info *)cookie;
        struct vcons_screen *scr = ri->ri_hw;
        struct igsfb_devconfig *dc = (struct igsfb_devconfig *)scr->scr_cookie;
        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(void *cookie, int row, int num, long attr)
{
        struct rasops_info *ri = (struct rasops_info *)cookie;
        struct vcons_screen *scr = ri->ri_hw;
        struct igsfb_devconfig *dc = (struct igsfb_devconfig *)scr->scr_cookie;
        uint32_t color;
        uint32_t dsp;
        uint16_t width, height;

        if (num == ri->ri_rows && (ri->ri_flg & RI_FULLCLEAR) != 0) {
                width = ri->ri_width;
                height = ri->ri_height;
                dsp = 0;
        } else {
                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);
}


static void
igsfb_accel_erasecols(void *cookie, int row, int col, int num, long attr)
{
        struct rasops_info *ri = (struct rasops_info *)cookie;
        struct vcons_screen *scr = ri->ri_hw;
        struct igsfb_devconfig *dc = (struct igsfb_devconfig *)scr->scr_cookie;
        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(void *cookie, int row, int col, u_int uc, long attr)
{
        struct rasops_info *ri = (struct rasops_info *)cookie;
        struct vcons_screen *scr = ri->ri_hw;
        struct igsfb_devconfig *dc = (struct igsfb_devconfig *)scr->scr_cookie;

        igsfb_accel_wait(dc);
        (*dc->dc_ri_putchar)(cookie, row, col, uc, attr);
}